--HG--
branch : dev
This commit is contained in:
Andre Rodrigues
2010-11-15 15:54:47 -08:00
64 changed files with 1473 additions and 418 deletions

Binary file not shown.

BIN
lib/specflow/Gherkin.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,3 +1,127 @@
1.4.0 - 2010/10/07
Breaking changes:
+ The generator has been improved to provide source code language. Because of this, SpecFlow test generated
with this version will be incompatible with older runtimes.
New features:
+ Scoped Step Definitions: you can scope step definitions (bindings) to tags, features and scenarios. Scope filter
can be applied to a class or a method with the [StepScope] attribute.
See examples in Tests/FeatureTests/ScopedSteps/ScopedSteps.feature and Tests/FeatureTests/ScopedSteps/ScopedStepsBindings.cs
(Thanks to Jose Simas for the contribution.)
+ Adding binding-culture to App.config. If set, this culture is used during execution of steps.
+ VB-Step-Definition Skeleton Provider: For VB-projects, the suggested step skeletons are generated in VB.
+ Merging strongly typed context accessors from Darren Cauthon's SpecFlowAssist
+ Merging table/row extension methods from Darren Cauthon's SpecFlowAssist
Add a using statement for the namespace TechTalk.SpecFlow.Assist to use the extension methods.
See also Darren's youtube tutorial: http://bit.ly/aY4VOd
+ Diagnostic tracing: VS2010 integration can display trace messages to the Output window
if tracing is enabled. Tracing can be enabled by setting the environment variable SPECFLOW_TRACE
to either "all" or to the comma separated list of individual SpecFlow traing categories (currently
only the category "EditorParser" is supported).
Fixed issues:
+ Better error reporting for wrong Gherkin files (multiple errors displayed, detect duplicate scenario names)
+ Visual Studio 2010 editor slows down after editing a feature file for a longer time (Issue 9)
1.3.5.2 - 2010/08/11
Fixed issues:
+ Sorry, we're ironing out our deploy strategy with the new Mono/MonoDevelop integration. We didn't
change the version in the MonoDevelop Add-In XML file.
1.3.5.1 - 2010/08/11
New features:
+ Support for hosting add-in on http://addins.monodevelop.com
1.3.5 - 2010/08/11
New features:
+ Support for Mono (v2.6.7) & MonoDevelop (v2.4) by Dale Ragan
Fixed issues:
+ Generating code randomly for the wrong testing engine
+ Test class generation problem for Russian feature files
+ Fix tag support for Silverlight
1.3.4 - 2010/07/28
Fixed issues:
+ Installation fails if Visual Studio 2010 is not installed
+ VS2010: Background section is not colored properly
1.3.3 - 2010/07/19
New features:
+ Support for MsTest report generation
usage: specflow mstestexecutionreport projectFile [/testResult:value] [/xsltFile:value] [/out:value]
projectFile Visual Studio Project File containing specs
[/testResult:value] Test Result file generated by MsTest. Defaults to TestResult.trx
[/out:value] Generated Output File. Defaults to TestResult.html
[/xsltFile:value] Xslt file to use, defaults to built-in stylesheet if not provided
+ Visual Studio 2010 editor support:
- syntax coloring with configurable colors ("Gherkin ...")
- outlining for scenarios
Uninstall the beta integration (TechTalk.SpecFlow.VsIntegration.GherkinFile.vsix) before installing
SpecFlow 1.3.3.
Fixed issues:
+ MbUnit execution fails for pending steps (Assert method not found: Inconclusive)
1.3.2 - 2010/06/29
New features:
+ Support for MsTest for .NET 4.0 categories. Configure the test provider name to
"MsTest.2010" in order to use the [TestCategory] attribute.
+ Silverlight support (beta), see http://wiki.github.com/techtalk/SpecFlow/silverlight-support
Fixed issues:
+ Report generation fails if no custom XSLT is provided
1.3.1 - 2010/06/21
New features:
+ Using standard Gherkin parser (http://github.com/aslakhellesoy/gherkin) v2.0.1
+ Custom XSLT can be specified for generating reports.
See examples in Tests/ReportingTests/CustomXsltTemplate.feature
+ The test error can be accessed through ScenarioContext.Current.TestError
(e.g. in an AfterScenario event).
+ [StepTransformation] attribute has been renamed to [StepArgumentTransformation]
because this name describe the intention better. Using the old attribute will
generate a warning.
+ Support for MbUnit
Fixed issues:
+ NullReference exception when using BeforeTestRun event (Issue 41)
1.3.0 - 2010/05/05
New features:
+ Using standard Gherkin parser (http://github.com/aslakhellesoy/gherkin) v1.0.24
+ Context injection in step definitions. Step definitions can get a context injected with
constructor injection. (Issue 30)
See examples in Tests/FeatureTests/ContextInjection
+ Using steps in other assemblies. This enables writing steps in VB. (Issue 19)
See examples in Tests/FeatureTests/ExternalSteps
+ Steps can be invoked from other steps using step text. See examples in
Tests/FeatureTests/CallingStepsFromStepDefinitions
+ Custom step parameter converters can be defined as a binding.
See examples in Tests/FeatureTests/StepArgumentTransfomation
+ SpecFlow feature files can be added also to VB.NET projects
+ Support for xUnit
+ Single installer for Visual Studio 2008 and 2010 (Issue 6, 10, 11)
+ Place GeneratedCodeAttribute and 'Designer generated code' region on generated code to
avoid having this code parsed by code analysis. (Issue 33)
+ Configuration option to disable all output. (Issue 29)
Use the following config to disable output:
<trace listener="TechTalk.SpecFlow.Tracing.NullListener, TechTalk.SpecFlow" />
Fixed issues:
+ SpecFlow Reporting doesn't work with Firefox (Issue 31)
+ Binding methods are executed using the culture of the feature file.
+ Several parsing issues are solved now (Issue 1, 8, 9, 37)
1.2.0 - 2009/11/25
@@ -28,7 +152,8 @@ New features:
Fixed issues:
+ Runtime: Remove direct dependency on nunit.framework.dll from the runtime (Issue 12)
+ Runtime: Binding methods with more than 4 parameters cannot be used (Issue 21)
+ Generator: Special language characters (e.g. accented letters) are removed when generating test method names (Issue 22)
+ Generator: Special language characters (e.g. accented letters) are removed when generating
test method names (Issue 22)
1.0.2 - 2009/10/20

Binary file not shown.

View File

@@ -85,6 +85,7 @@
<Reference Include="System.Data.SqlServerCe, Version=3.5.1.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\sqlce\System.Data.SqlServerCe.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions">

View File

@@ -6,7 +6,6 @@ using Moq;
using NUnit.Framework;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.ContentManagement.MetaData;
using Orchard.ContentManagement.Records;
@@ -110,14 +109,13 @@ namespace Orchard.Core.Tests.Routable.Services {
}
}
[Test]
public void VeryLongStringTruncatedTo1000Chars() {
var veryVeryLongTitle = "this is a very long title...";
for (var i = 0; i < 100; i++)
veryVeryLongTitle += "aaaaaaaaaa";
var thing = CreateRoutePart(veryVeryLongTitle);
var thing = CreateRoutePartFromScratch(veryVeryLongTitle);
_routableService.FillSlugFromTitle(thing);
Assert.That(veryVeryLongTitle.Length, Is.AtLeast(1001));
@@ -126,72 +124,111 @@ namespace Orchard.Core.Tests.Routable.Services {
[Test]
public void NoExistingLikeSlugsGeneratesSameSlug() {
string slug = _routableService.GenerateUniqueSlug(CreateRoutePart("woohoo"), null);
string slug = _routableService.GenerateUniqueSlug(CreateRoutePartFromScratch("woohoo"), null);
Assert.That(slug, Is.EqualTo("woohoo"));
}
[Test]
public void ExistingSingleLikeSlugThatsAConflictGeneratesADash2() {
string slug = _routableService.GenerateUniqueSlug(CreateRoutePart("woohoo"), new List<string> { "woohoo" });
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(CreateRoutePart("woohoo"), new List<string> { "woohoo-2" });
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(CreateRoutePart("woohoo"), new List<string> { "woohoo", "woohoo-2" });
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(CreateRoutePart("woohoo"), new List<string> { "woohoo-2", "woohoo-4", "woohoo-5" });
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(CreateRoutePart("woohoo-2"), new List<string> { "woohoo-2", "woohoo-4", "woohoo-5" });
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 GeneratedSlugIsLowerCased() {
var thing = CreateRoutePart("This Is Some Interesting Title");
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() {
var thing1 = CreateRoutePart("bar", "bar", "foo");
var thing2 = CreateRoutePart("fooslashbar", "foo/bar");
Assert.That(thing2.Slug, Is.EqualTo("foo/bar-2"));
CreateRoutePartFromScratch("bar", "bar", "foo");
var thing2 = CreateRoutePartFromScratch("fooslashbar", "foo/bar");
Assert.That(thing2.Path, Is.EqualTo("foo/bar-2"));
}
private RoutePart CreateRoutePart(string title, string slug = "", string containerPath = "") {
[Test]
public void GeneratedSlugInConflictInSameContaierPathIsVersioned() {
var thing1 = CreateRoutePartFromScratch("Foo", "", "bar");
var thing2 = CreateRoutePartWithExistingContainer("Foo", thing1.As<ICommonPart>().Container);
Assert.That(thing2.Path, Is.EqualTo("bar/foo-2"));
Assert.That(thing2.Slug, Is.EqualTo("foo"));
}
[Test]
public void GivenSlugInConflictInSameContaierPathIsVersioned() {
var thing1 = CreateRoutePartFromScratch("Hi", "foo", "bar");
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"));
}
[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();
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;
});
}
})
.As<RoutePart>();
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>();
}

View File

@@ -1,6 +1,4 @@
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Linq;
using Orchard.Environment.Configuration;
using Orchard.Environment.Descriptor;
using Orchard.Environment.Descriptor.Models;
@@ -16,7 +14,7 @@ namespace Orchard.Specs.Bindings {
var webApp = Binding<WebAppHosting>();
webApp.GivenIHaveACleanSiteWith(TableData(
new { extension = "module", names = "Orchard.Setup, Orchard.Modules, Orchard.Packaging, Orchard.PublishLater, Orchard.Themes, Orchard.Widgets, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.jQuery, Orchard.Tags, TinyMce" },
new { extension = "module", names = "Orchard.Setup, Orchard.Pages, Orchard.Blogs, Orchard.Modules, Orchard.Packaging, Orchard.PublishLater, Orchard.Themes, Orchard.Widgets, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.jQuery, Orchard.Tags, TinyMce" },
new { extension = "core", names = "Common, Dashboard, Feeds, HomePage, Messaging, Navigation, Contents, Routable, Scheduling, Settings, Shapes, XmlRpc" },
new { extension = "theme", names = "SafeMode, TheAdmin, TheThemeMachine" }));
@@ -34,9 +32,7 @@ namespace Orchard.Specs.Bindings {
public void GivenIHaveInstalled(string name) {
var webApp = Binding<WebAppHosting>();
webApp.GivenIHaveModule(name);
webApp.Host.Execute(() => {
MvcApplication.ReloadExtensions();
});
webApp.Host.Execute(MvcApplication.ReloadExtensions);
GivenIHaveEnabled(name);
}

View File

@@ -156,7 +156,7 @@ namespace Orchard.Specs.Bindings {
[When(@"I fill in")]
public void WhenIFillIn(Table table) {
var inputs = _doc.DocumentNode
.SelectNodes("//input") ?? Enumerable.Empty<HtmlNode>();
.SelectNodes("(//input|//textarea)") ?? Enumerable.Empty<HtmlNode>();
foreach (var row in table.Rows) {
var r = row;
@@ -191,12 +191,15 @@ namespace Orchard.Specs.Bindings {
var form = Form.LocateAround(submit);
var urlPath = form.Start.GetAttributeValue("action", Details.UrlPath);
var inputs = form.Children
.SelectMany(elt => elt.DescendantsAndSelf("input"))
.SelectMany(elt => elt.DescendantsAndSelf("input").Concat(elt.Descendants("textarea")))
.Where(node => !((node.GetAttributeValue("type", "") == "radio" || node.GetAttributeValue("type", "") == "checkbox") && node.GetAttributeValue("checked", "") != "checked"))
.GroupBy(elt => elt.GetAttributeValue("name", elt.GetAttributeValue("id", "")), elt => elt.GetAttributeValue("value", ""))
.Where(g => !string.IsNullOrEmpty(g.Key))
.ToDictionary(elt => elt.Key, elt => (IEnumerable<string>)elt);
if (submit.Attributes.Contains("name"))
inputs.Add(submit.GetAttributeValue("name", ""), new[] {submit.GetAttributeValue("value", "yes")});
Details = Host.SendRequest(urlPath, inputs);
_doc = new HtmlDocument();
_doc.Load(new StringReader(Details.ResponseText));
@@ -221,7 +224,7 @@ namespace Orchard.Specs.Bindings {
[Then(@"I should see ""(.*)""")]
public void ThenIShouldSee(string text) {
Assert.That(Details.ResponseText, Is.StringContaining(text));
Assert.That(Details.ResponseText, Is.StringMatching(text));
}
[Then(@"I should not see ""(.*)""")]

View File

@@ -0,0 +1,94 @@
Feature: Blog management
In order to add blogs to my site
As an author
I want to create blogs and create, publish and edit blog posts
Scenario: In the admin (menu) there is a link to create a Blog
Given I have installed Orchard
When I go to "admin"
Then I should see "<a href="/Admin/Blogs/Create">Blogs</a>"
Scenario: I can create a new blog and blog post
Given I have installed Orchard
When I go to "admin/blogs/create"
And I fill in
| name | value |
| Routable.Title | My Blog |
And I hit "Save"
And I go to "my-blog"
Then I should see "<h1[^>]*>.*?My Blog.*?</h1>"
When I go to "admin/blogs/my-blog/posts/create"
And I fill in
| name | value |
| Routable.Title | My Post |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I go to "my-blog/my-post"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Hi there."
Scenario: I can create a new blog with multiple blog posts each with the same title and unique slugs are generated or given for said posts
Given I have installed Orchard
When I go to "admin/blogs/create"
And I fill in
| name | value |
| Routable.Title | My Blog |
And I hit "Save"
And I go to "admin/blogs/my-blog/posts/create"
And I fill in
| name | value |
| Routable.Title | My Post |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I go to "my-blog/my-post"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Hi there."
When I go to "admin/blogs/my-blog/posts/create"
And I fill in
| name | value |
| Routable.Title | My Post |
| Body.Text | Hi there, again. |
And I hit "Publish Now"
And I go to "my-blog/my-post-2"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Hi there, again."
When I go to "admin/blogs/my-blog/posts/create"
And I fill in
| name | value |
| Routable.Title | My Post |
| Routable.Slug | my-post |
| Body.Text | Are you still there? |
And I hit "Publish Now"
And I go to "my-blog/my-post-3"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Are you still there?"
Scenario: I can create a new blog and blog post and when I change the slug of the blog the path of the plog post is updated
Given I have installed Orchard
When I go to "admin/blogs/create"
And I fill in
| name | value |
| Routable.Title | My Blog |
And I hit "Save"
And I go to "my-blog"
Then I should see "<h1[^>]*>.*?My Blog.*?</h1>"
When I go to "admin/blogs/my-blog/posts/create"
And I fill in
| name | value |
| Routable.Title | My Post |
| Body.Text | Hi there. |
And I hit "Publish Now"
And I go to "my-blog/my-post"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Hi there."
When I go to "admin/blogs/my-blog"
And I follow "Blog Properties"
And I fill in
| name | value |
| Routable.Slug | my-other-blog |
And I hit "Save"
And I go to "my-other-blog"
Then I should see "<h1[^>]*>.*?My Blog.*?</h1>"
When I go to "my-other-blog/my-post"
Then I should see "<h1[^>]*>.*?My Post.*?</h1>"
And I should see "Hi there."

302
src/Orchard.Specs/Blogs.feature.cs generated Normal file
View File

@@ -0,0 +1,302 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.4.0.0
// Runtime Version:4.0.30319.1
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
#region Designer generated code
namespace Orchard.Specs
{
using TechTalk.SpecFlow;
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.4.0.0")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Blog management")]
public partial class BlogManagementFeature
{
private static TechTalk.SpecFlow.ITestRunner testRunner;
#line 1 "Blogs.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"), "Blog management", "In order to add blogs to my site\r\nAs an author\r\nI want to create blogs and create" +
", publish and edit blog posts", GenerationTargetLanguage.CSharp, ((string[])(null)));
testRunner.OnFeatureStart(featureInfo);
}
[NUnit.Framework.TestFixtureTearDownAttribute()]
public virtual void FeatureTearDown()
{
testRunner.OnFeatureEnd();
testRunner = null;
}
public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
{
testRunner.OnScenarioStart(scenarioInfo);
}
[NUnit.Framework.TearDownAttribute()]
public virtual void ScenarioTearDown()
{
testRunner.OnScenarioEnd();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("In the admin (menu) there is a link to create a Blog")]
public virtual void InTheAdminMenuThereIsALinkToCreateABlog()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("In the admin (menu) there is a link to create a Blog", ((string[])(null)));
#line 6
this.ScenarioSetup(scenarioInfo);
#line 7
testRunner.Given("I have installed Orchard");
#line 8
testRunner.When("I go to \"admin\"");
#line 9
testRunner.Then("I should see \"<a href=\"/Admin/Blogs/Create\">Blogs</a>\"");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("I can create a new blog and blog post")]
public virtual void ICanCreateANewBlogAndBlogPost()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create a new blog and blog post", ((string[])(null)));
#line 11
this.ScenarioSetup(scenarioInfo);
#line 12
testRunner.Given("I have installed Orchard");
#line 13
testRunner.When("I go to \"admin/blogs/create\"");
#line hidden
TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table1.AddRow(new string[] {
"Routable.Title",
"My Blog"});
#line 14
testRunner.And("I fill in", ((string)(null)), table1);
#line 17
testRunner.And("I hit \"Save\"");
#line 18
testRunner.And("I go to \"my-blog\"");
#line 19
testRunner.Then("I should see \"<h1[^>]*>.*?My Blog.*?</h1>\"");
#line 20
testRunner.When("I go to \"admin/blogs/my-blog/posts/create\"");
#line hidden
TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table2.AddRow(new string[] {
"Routable.Title",
"My Post"});
table2.AddRow(new string[] {
"Body.Text",
"Hi there."});
#line 21
testRunner.And("I fill in", ((string)(null)), table2);
#line 25
testRunner.And("I hit \"Publish Now\"");
#line 26
testRunner.And("I go to \"my-blog/my-post\"");
#line 27
testRunner.Then("I should see \"<h1[^>]*>.*?My Post.*?</h1>\"");
#line 28
testRunner.And("I should see \"Hi there.\"");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("I can create a new blog with multiple blog posts each with the same title and uni" +
"que slugs are generated or given for said posts")]
public virtual void ICanCreateANewBlogWithMultipleBlogPostsEachWithTheSameTitleAndUniqueSlugsAreGeneratedOrGivenForSaidPosts()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create a new blog with multiple blog posts each with the same title and uni" +
"que slugs are generated or given for said posts", ((string[])(null)));
#line 30
this.ScenarioSetup(scenarioInfo);
#line 31
testRunner.Given("I have installed Orchard");
#line 32
testRunner.When("I go to \"admin/blogs/create\"");
#line hidden
TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table3.AddRow(new string[] {
"Routable.Title",
"My Blog"});
#line 33
testRunner.And("I fill in", ((string)(null)), table3);
#line 36
testRunner.And("I hit \"Save\"");
#line 37
testRunner.And("I go to \"admin/blogs/my-blog/posts/create\"");
#line hidden
TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table4.AddRow(new string[] {
"Routable.Title",
"My Post"});
table4.AddRow(new string[] {
"Body.Text",
"Hi there."});
#line 38
testRunner.And("I fill in", ((string)(null)), table4);
#line 42
testRunner.And("I hit \"Publish Now\"");
#line 43
testRunner.And("I go to \"my-blog/my-post\"");
#line 44
testRunner.Then("I should see \"<h1[^>]*>.*?My Post.*?</h1>\"");
#line 45
testRunner.And("I should see \"Hi there.\"");
#line 46
testRunner.When("I go to \"admin/blogs/my-blog/posts/create\"");
#line hidden
TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table5.AddRow(new string[] {
"Routable.Title",
"My Post"});
table5.AddRow(new string[] {
"Body.Text",
"Hi there, again."});
#line 47
testRunner.And("I fill in", ((string)(null)), table5);
#line 51
testRunner.And("I hit \"Publish Now\"");
#line 52
testRunner.And("I go to \"my-blog/my-post-2\"");
#line 53
testRunner.Then("I should see \"<h1[^>]*>.*?My Post.*?</h1>\"");
#line 54
testRunner.And("I should see \"Hi there, again.\"");
#line 55
testRunner.When("I go to \"admin/blogs/my-blog/posts/create\"");
#line hidden
TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table6.AddRow(new string[] {
"Routable.Title",
"My Post"});
table6.AddRow(new string[] {
"Routable.Slug",
"my-post"});
table6.AddRow(new string[] {
"Body.Text",
"Are you still there?"});
#line 56
testRunner.And("I fill in", ((string)(null)), table6);
#line 61
testRunner.And("I hit \"Publish Now\"");
#line 62
testRunner.And("I go to \"my-blog/my-post-3\"");
#line 63
testRunner.Then("I should see \"<h1[^>]*>.*?My Post.*?</h1>\"");
#line 64
testRunner.And("I should see \"Are you still there?\"");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("I can create a new blog and blog post and when I change the slug of the blog the " +
"path of the plog post is updated")]
public virtual void ICanCreateANewBlogAndBlogPostAndWhenIChangeTheSlugOfTheBlogThePathOfThePlogPostIsUpdated()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create a new blog and blog post and when I change the slug of the blog the " +
"path of the plog post is updated", ((string[])(null)));
#line 66
this.ScenarioSetup(scenarioInfo);
#line 67
testRunner.Given("I have installed Orchard");
#line 68
testRunner.When("I go to \"admin/blogs/create\"");
#line hidden
TechTalk.SpecFlow.Table table7 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table7.AddRow(new string[] {
"Routable.Title",
"My Blog"});
#line 69
testRunner.And("I fill in", ((string)(null)), table7);
#line 72
testRunner.And("I hit \"Save\"");
#line 73
testRunner.And("I go to \"my-blog\"");
#line 74
testRunner.Then("I should see \"<h1[^>]*>.*?My Blog.*?</h1>\"");
#line 75
testRunner.When("I go to \"admin/blogs/my-blog/posts/create\"");
#line hidden
TechTalk.SpecFlow.Table table8 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table8.AddRow(new string[] {
"Routable.Title",
"My Post"});
table8.AddRow(new string[] {
"Body.Text",
"Hi there."});
#line 76
testRunner.And("I fill in", ((string)(null)), table8);
#line 80
testRunner.And("I hit \"Publish Now\"");
#line 81
testRunner.And("I go to \"my-blog/my-post\"");
#line 82
testRunner.Then("I should see \"<h1[^>]*>.*?My Post.*?</h1>\"");
#line 83
testRunner.And("I should see \"Hi there.\"");
#line 84
testRunner.When("I go to \"admin/blogs/my-blog\"");
#line 85
testRunner.And("I follow \"Blog Properties\"");
#line hidden
TechTalk.SpecFlow.Table table9 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table9.AddRow(new string[] {
"Routable.Slug",
"my-other-blog"});
#line 86
testRunner.And("I fill in", ((string)(null)), table9);
#line 89
testRunner.And("I hit \"Save\"");
#line 90
testRunner.And("I go to \"my-other-blog\"");
#line 91
testRunner.Then("I should see \"<h1[^>]*>.*?My Blog.*?</h1>\"");
#line 92
testRunner.When("I go to \"my-other-blog/my-post\"");
#line 93
testRunner.Then("I should see \"<h1[^>]*>.*?My Post.*?</h1>\"");
#line 94
testRunner.And("I should see \"Hi there.\"");
#line hidden
testRunner.CollectScenarioErrors();
}
}
}
#endregion

View File

@@ -69,9 +69,10 @@ namespace Orchard.Specs.Hosting {
var targetModule = _tempSite.Combine(extensionFolder).Combine(extensionName);
sourceModule.ShallowCopy("*.txt", targetModule);
sourceModule.ShallowCopy("*.info", targetModule);
//sourceModule.ShallowCopy("*.csproj", targetModule);
//sourceModule.DeepCopy("*.cs", targetModule);
//sourceModule.DeepCopy("*.cs", targetModule);)
if (sourceModule.Combine("bin").IsDirectory) {
sourceModule.Combine("bin").ShallowCopy("*.dll", targetModule.Combine("bin"));

View File

@@ -127,6 +127,11 @@
<Compile Include="Bindings\CommandLine.cs" />
<Compile Include="Bindings\ContentRights.cs" />
<Compile Include="Bindings\OrchardSiteFactory.cs" />
<Compile Include="Blogs.feature.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Blogs.feature</DependentUpon>
</Compile>
<Compile Include="ContentRights.feature.cs">
<DependentUpon>ContentRights.feature</DependentUpon>
<AutoGen>True</AutoGen>
@@ -154,6 +159,11 @@
<DesignTime>True</DesignTime>
<DependentUpon>MultiTenancy.feature</DependentUpon>
</Compile>
<Compile Include="Pages.feature.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Pages.feature</DependentUpon>
</Compile>
<Compile Include="Util\PathExtensions.cs" />
<Compile Include="WebHosting.feature.cs">
<DependentUpon>WebHosting.feature</DependentUpon>
@@ -195,6 +205,10 @@
<Content Include="Hosting\Orchard.Web\Config\Sites.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<None Include="Blogs.feature">
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>Blogs.feature.cs</LastGenOutput>
</None>
<None Include="ContentRights.feature">
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>ContentRights.feature.cs</LastGenOutput>
@@ -220,6 +234,10 @@
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>MultiTenancy.feature.cs</LastGenOutput>
</None>
<None Include="Pages.feature">
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>Pages.feature.cs</LastGenOutput>
</None>
<None Include="WebHosting.feature">
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>WebHosting.feature.cs</LastGenOutput>

View File

@@ -0,0 +1,42 @@
Feature: Content Page management
In order to add content pages to my site
As an author
I want to create, publish and edit pages
Scenario: In the admin (menu) there is a link to create a Page
Given I have installed Orchard
When I go to "admin"
Then I should see "<a href="/Admin/Contents/Create/Page">Page</a>"
Scenario: I can create and publish a new Page
Given I have installed Orchard
When I go to "admin/contents/create/page"
And I fill in
| name | value |
| Routable.Title | Super Duper |
| Body.Text | This is super. |
And I hit "Publish Now"
And I go to "super-duper"
Then I should see "<h1[^>]*>.*?Super Duper.*?</h1>"
And I should see "This is super."
Scenario: If I create a page which gets a conflicting path generated its path is made to be unique
Given I have installed Orchard
When I go to "admin/contents/create/page"
And I fill in
| name | value |
| Routable.Title | Super Duper |
| Body.Text | This is super. |
And I hit "Publish Now"
And I go to "super-duper"
Then I should see "<h1[^>]*>.*?Super Duper.*?</h1>"
And I should see "This is super."
When I go to "admin/contents/create/page"
And I fill in
| name | value |
| Routable.Title | Super Duper |
| Body.Text | This is super number two. |
And I hit "Publish Now"
And I go to "super-duper-2"
Then I should see "<h1[^>]*>.*?Super Duper.*?</h1>"
And I should see "This is super number two."

168
src/Orchard.Specs/Pages.feature.cs generated Normal file
View File

@@ -0,0 +1,168 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.4.0.0
// Runtime Version:4.0.30319.1
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
// ------------------------------------------------------------------------------
#region Designer generated code
namespace Orchard.Specs
{
using TechTalk.SpecFlow;
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.4.0.0")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Content Page management")]
public partial class ContentPageManagementFeature
{
private static TechTalk.SpecFlow.ITestRunner testRunner;
#line 1 "Pages.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"), "Content Page management", "In order to add content pages to my site\r\nAs an author\r\nI want to create, publish" +
" and edit pages", GenerationTargetLanguage.CSharp, ((string[])(null)));
testRunner.OnFeatureStart(featureInfo);
}
[NUnit.Framework.TestFixtureTearDownAttribute()]
public virtual void FeatureTearDown()
{
testRunner.OnFeatureEnd();
testRunner = null;
}
public virtual void ScenarioSetup(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
{
testRunner.OnScenarioStart(scenarioInfo);
}
[NUnit.Framework.TearDownAttribute()]
public virtual void ScenarioTearDown()
{
testRunner.OnScenarioEnd();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("In the admin (menu) there is a link to create a Page")]
public virtual void InTheAdminMenuThereIsALinkToCreateAPage()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("In the admin (menu) there is a link to create a Page", ((string[])(null)));
#line 6
this.ScenarioSetup(scenarioInfo);
#line 7
testRunner.Given("I have installed Orchard");
#line 8
testRunner.When("I go to \"admin\"");
#line 9
testRunner.Then("I should see \"<a href=\"/Admin/Contents/Create/Page\">Page</a>\"");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("I can create and publish a new Page")]
public virtual void ICanCreateAndPublishANewPage()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create and publish a new Page", ((string[])(null)));
#line 11
this.ScenarioSetup(scenarioInfo);
#line 12
testRunner.Given("I have installed Orchard");
#line 13
testRunner.When("I go to \"admin/contents/create/page\"");
#line hidden
TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table1.AddRow(new string[] {
"Routable.Title",
"Super Duper"});
table1.AddRow(new string[] {
"Body.Text",
"This is super."});
#line 14
testRunner.And("I fill in", ((string)(null)), table1);
#line 18
testRunner.And("I hit \"Publish Now\"");
#line 19
testRunner.And("I go to \"super-duper\"");
#line 20
testRunner.Then("I should see \"<h1[^>]*>.*?Super Duper.*?</h1>\"");
#line 21
testRunner.And("I should see \"This is super.\"");
#line hidden
testRunner.CollectScenarioErrors();
}
[NUnit.Framework.TestAttribute()]
[NUnit.Framework.DescriptionAttribute("If I create a page which gets a conflicting path generated its path is made to be" +
" unique")]
public virtual void IfICreateAPageWhichGetsAConflictingPathGeneratedItsPathIsMadeToBeUnique()
{
TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("If I create a page which gets a conflicting path generated its path is made to be" +
" unique", ((string[])(null)));
#line 23
this.ScenarioSetup(scenarioInfo);
#line 24
testRunner.Given("I have installed Orchard");
#line 25
testRunner.When("I go to \"admin/contents/create/page\"");
#line hidden
TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table2.AddRow(new string[] {
"Routable.Title",
"Super Duper"});
table2.AddRow(new string[] {
"Body.Text",
"This is super."});
#line 26
testRunner.And("I fill in", ((string)(null)), table2);
#line 30
testRunner.And("I hit \"Publish Now\"");
#line 31
testRunner.And("I go to \"super-duper\"");
#line 32
testRunner.Then("I should see \"<h1[^>]*>.*?Super Duper.*?</h1>\"");
#line 33
testRunner.And("I should see \"This is super.\"");
#line 34
testRunner.When("I go to \"admin/contents/create/page\"");
#line hidden
TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] {
"name",
"value"});
table3.AddRow(new string[] {
"Routable.Title",
"Super Duper"});
table3.AddRow(new string[] {
"Body.Text",
"This is super number two."});
#line 35
testRunner.And("I fill in", ((string)(null)), table3);
#line 39
testRunner.And("I hit \"Publish Now\"");
#line 40
testRunner.And("I go to \"super-duper-2\"");
#line 41
testRunner.Then("I should see \"<h1[^>]*>.*?Super Duper.*?</h1>\"");
#line 42
testRunner.And("I should see \"This is super number two.\"");
#line hidden
testRunner.CollectScenarioErrors();
}
}
}
#endregion

View File

@@ -6,7 +6,7 @@ Feature: Setup
Scenario: Root request shows setup form
Given I have a clean site with
| extension | names |
| module | Orchard.Setup, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce |
| module | Orchard.Setup, Orchard.Pages, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce |
| core | Common, Contents, Dashboard, Feeds, HomePage, Messaging, Navigation, Routable, Scheduling, Settings, Shapes, XmlRpc |
| theme | SafeMode |
When I go to "/Default.aspx"
@@ -17,7 +17,7 @@ Scenario: Root request shows setup form
Scenario: Setup folder also shows setup form
Given I have a clean site with
| extension | names |
| module | Orchard.Setup, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce |
| module | Orchard.Setup, Orchard.Pages, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce |
| core | Common, Contents, Dashboard, Feeds, HomePage, Messaging, Navigation, Routable, Scheduling, Settings, Shapes, XmlRpc |
| theme | SafeMode |
When I go to "/Setup"
@@ -28,7 +28,7 @@ Scenario: Setup folder also shows setup form
Scenario: Some of the initial form values are required
Given I have a clean site with
| extension | names |
| module | Orchard.Setup, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce |
| module | Orchard.Setup, Orchard.Pages, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce |
| core | Common, Contents, Dashboard, Feeds, HomePage, Messaging, Navigation, Routable, Scheduling, Settings, Shapes, XmlRpc |
| theme | SafeMode |
When I go to "/Setup"
@@ -39,7 +39,7 @@ Scenario: Some of the initial form values are required
Scenario: Calling setup on a brand new install
Given I have a clean site with
| extension | names |
| module | Orchard.Setup, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLater, Orchard.Themes, Orchard.Modules, Orchard.Widgets, Orchard.jQuery, TinyMce |
| module | Orchard.Setup, Orchard.Pages, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLater, Orchard.Themes, Orchard.Modules, Orchard.Widgets, Orchard.jQuery, TinyMce |
| core | Common, Contents, Dashboard, Feeds, HomePage, Messaging, Navigation, Routable, Scheduling, Settings, Shapes, XmlRpc |
| theme | SafeMode, TheThemeMachine |
And I am on "/Setup"

View File

@@ -1,7 +1,7 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by SpecFlow (http://www.specflow.org/).
// SpecFlow Version:1.3.2.0
// SpecFlow Version:1.4.0.0
// Runtime Version:4.0.30319.1
//
// Changes to this file may cause incorrect behavior and will be lost if
@@ -14,7 +14,7 @@ namespace Orchard.Specs
using TechTalk.SpecFlow;
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.3.2.0")]
[System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.4.0.0")]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[NUnit.Framework.TestFixtureAttribute()]
[NUnit.Framework.DescriptionAttribute("Setup")]
@@ -31,7 +31,7 @@ namespace Orchard.Specs
{
testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Setup", "In order to install orchard\r\nAs a new user\r\nI want to setup a new site from the d" +
"efault screen", ((string[])(null)));
"efault screen", GenerationTargetLanguage.CSharp, ((string[])(null)));
testRunner.OnFeatureStart(featureInfo);
}
@@ -66,8 +66,8 @@ this.ScenarioSetup(scenarioInfo);
"names"});
table1.AddRow(new string[] {
"module",
"Orchard.Setup, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLat" +
"er, Orchard.Themes, Orchard.jQuery, TinyMce"});
"Orchard.Setup, Orchard.Pages, Orchard.Users, Orchard.Roles, Orchard.Comments, Orc" +
"hard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce"});
table1.AddRow(new string[] {
"core",
"Common, Contents, Dashboard, Feeds, HomePage, Messaging, Navigation, Routable, Sc" +
@@ -76,15 +76,15 @@ this.ScenarioSetup(scenarioInfo);
"theme",
"SafeMode"});
#line 7
testRunner.Given("I have a clean site with", ((string)(null)), table1);
testRunner.Given("I have a clean site with", ((string)(null)), table1);
#line 12
testRunner.When("I go to \"/Default.aspx\"");
testRunner.When("I go to \"/Default.aspx\"");
#line 13
testRunner.Then("I should see \"Welcome to Orchard\"");
testRunner.Then("I should see \"Welcome to Orchard\"");
#line 14
testRunner.And("I should see \"Finish Setup\"");
testRunner.And("I should see \"Finish Setup\"");
#line 15
testRunner.And("the status should be 200 \"OK\"");
testRunner.And("the status should be 200 \"OK\"");
#line hidden
testRunner.CollectScenarioErrors();
}
@@ -102,8 +102,8 @@ this.ScenarioSetup(scenarioInfo);
"names"});
table2.AddRow(new string[] {
"module",
"Orchard.Setup, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLat" +
"er, Orchard.Themes, Orchard.jQuery, TinyMce"});
"Orchard.Setup, Orchard.Pages, Orchard.Users, Orchard.Roles, Orchard.Comments, Orc" +
"hard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce"});
table2.AddRow(new string[] {
"core",
"Common, Contents, Dashboard, Feeds, HomePage, Messaging, Navigation, Routable, Sc" +
@@ -112,15 +112,15 @@ this.ScenarioSetup(scenarioInfo);
"theme",
"SafeMode"});
#line 18
testRunner.Given("I have a clean site with", ((string)(null)), table2);
testRunner.Given("I have a clean site with", ((string)(null)), table2);
#line 23
testRunner.When("I go to \"/Setup\"");
testRunner.When("I go to \"/Setup\"");
#line 24
testRunner.Then("I should see \"Welcome to Orchard\"");
testRunner.Then("I should see \"Welcome to Orchard\"");
#line 25
testRunner.And("I should see \"Finish Setup\"");
testRunner.And("I should see \"Finish Setup\"");
#line 26
testRunner.And("the status should be 200 \"OK\"");
testRunner.And("the status should be 200 \"OK\"");
#line hidden
testRunner.CollectScenarioErrors();
}
@@ -138,8 +138,8 @@ this.ScenarioSetup(scenarioInfo);
"names"});
table3.AddRow(new string[] {
"module",
"Orchard.Setup, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLat" +
"er, Orchard.Themes, Orchard.jQuery, TinyMce"});
"Orchard.Setup, Orchard.Pages, Orchard.Users, Orchard.Roles, Orchard.Comments, Orc" +
"hard.PublishLater, Orchard.Themes, Orchard.jQuery, TinyMce"});
table3.AddRow(new string[] {
"core",
"Common, Contents, Dashboard, Feeds, HomePage, Messaging, Navigation, Routable, Sc" +
@@ -148,16 +148,16 @@ this.ScenarioSetup(scenarioInfo);
"theme",
"SafeMode"});
#line 29
testRunner.Given("I have a clean site with", ((string)(null)), table3);
testRunner.Given("I have a clean site with", ((string)(null)), table3);
#line 34
testRunner.When("I go to \"/Setup\"");
testRunner.When("I go to \"/Setup\"");
#line 35
testRunner.And("I hit \"Finish Setup\"");
testRunner.And("I hit \"Finish Setup\"");
#line 36
testRunner.Then("I should see \"<input autofocus=\"autofocus\" class=\"input-validation-error\" id=\"Sit" +
testRunner.Then("I should see \"<input autofocus=\"autofocus\" class=\"input-validation-error\" id=\"Sit" +
"eName\" name=\"SiteName\" type=\"text\" value=\"\" />\"");
#line 37
testRunner.And("I should see \"<input class=\"input-validation-error\" id=\"AdminPassword\" name=\"Admi" +
testRunner.And("I should see \"<input class=\"input-validation-error\" id=\"AdminPassword\" name=\"Admi" +
"nPassword\" type=\"password\" />\"");
#line hidden
testRunner.CollectScenarioErrors();
@@ -176,8 +176,9 @@ this.ScenarioSetup(scenarioInfo);
"names"});
table4.AddRow(new string[] {
"module",
"Orchard.Setup, Orchard.Users, Orchard.Roles, Orchard.Comments, Orchard.PublishLat" +
"er, Orchard.Themes, Orchard.Modules, Orchard.Widgets, Orchard.jQuery, TinyMce"});
"Orchard.Setup, Orchard.Pages, Orchard.Users, Orchard.Roles, Orchard.Comments, Orc" +
"hard.PublishLater, Orchard.Themes, Orchard.Modules, Orchard.Widgets, Orchard.jQu" +
"ery, TinyMce"});
table4.AddRow(new string[] {
"core",
"Common, Contents, Dashboard, Feeds, HomePage, Messaging, Navigation, Routable, Sc" +
@@ -186,9 +187,9 @@ this.ScenarioSetup(scenarioInfo);
"theme",
"SafeMode, TheThemeMachine"});
#line 40
testRunner.Given("I have a clean site with", ((string)(null)), table4);
testRunner.Given("I have a clean site with", ((string)(null)), table4);
#line 45
testRunner.And("I am on \"/Setup\"");
testRunner.And("I am on \"/Setup\"");
#line hidden
TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] {
"name",
@@ -203,15 +204,15 @@ testRunner.And("I am on \"/Setup\"");
"ConfirmPassword",
"6655321"});
#line 46
testRunner.When("I fill in", ((string)(null)), table5);
testRunner.When("I fill in", ((string)(null)), table5);
#line 51
testRunner.And("I hit \"Finish Setup\"");
testRunner.And("I hit \"Finish Setup\"");
#line 52
testRunner.And("I go to \"/Default.aspx\"");
testRunner.And("I go to \"/Default.aspx\"");
#line 53
testRunner.Then("I should see \"My Site\"");
testRunner.Then("I should see \"My Site\"");
#line 54
testRunner.And("I should see \"Welcome, <strong>admin</strong>!\"");
testRunner.And("I should see \"Welcome, <strong>admin</strong>!\"");
#line hidden
testRunner.CollectScenarioErrors();
}

View File

@@ -6,14 +6,21 @@ using Orchard.ContentManagement.Aspects;
using Orchard.ContentManagement.Drivers;
using Orchard.Core.Containers.Models;
using Orchard.Core.Containers.ViewModels;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Localization;
using Orchard.UI.Notify;
namespace Orchard.Core.Containers.Drivers {
public class ContainablePartDriver : ContentPartDriver<ContainablePart> {
private readonly IContentManager _contentManager;
private readonly IRoutableService _routableService;
private readonly IOrchardServices _services;
public ContainablePartDriver(IContentManager contentManager) {
public ContainablePartDriver(IContentManager contentManager, IRoutableService routableService, IOrchardServices services) {
_contentManager = contentManager;
_routableService = routableService;
_services = services;
T = NullLocalizer.Instance;
}
@@ -37,9 +44,8 @@ namespace Orchard.Core.Containers.Drivers {
if (updater != null) {
var oldContainerId = model.ContainerId;
updater.TryUpdateModel(model, "Containable", null, null);
if (oldContainerId != model.ContainerId) {
if (oldContainerId != model.ContainerId)
commonPart.Container = _contentManager.Get(model.ContainerId, VersionOptions.Latest);
}
}
var containers = _contentManager.Query<ContainerPart, ContainerPartRecord>(VersionOptions.Latest).List();

View File

@@ -213,10 +213,7 @@ namespace Orchard.Core.Contents.Controllers {
if (!Services.Authorizer.Authorize(Permissions.PublishContent, contentItem, T("Couldn't create content")))
return new HttpUnauthorizedResult();
var isDraftable = contentItem.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable;
_contentManager.Create(
contentItem,
isDraftable ? VersionOptions.Draft : VersionOptions.Published);
_contentManager.Create(contentItem, VersionOptions.Draft);
var model = _contentManager.UpdateEditor(contentItem, this);
if (!ModelState.IsValid) {

View File

@@ -412,6 +412,8 @@
<Content Include="Containers\Views\EditorTemplates\Containable.cshtml" />
<Content Include="Containers\Views\Parts\ContainerWidget.cshtml" />
<Content Include="Containers\Views\EditorTemplates\ContainerCustom.cshtml" />
<Content Include="Routable\Views\Parts\RoutableTitle_Summary.cshtml" />
<Content Include="Routable\Views\Parts\RoutableTitle_SummaryAdmin.cshtml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />

View File

@@ -4,6 +4,7 @@ using System.Web.Mvc;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.Localization;
@@ -62,7 +63,7 @@ namespace Orchard.Core.Routable.Controllers {
contentItem = _contentManager.Get((int)id, VersionOptions.Latest);
if (contentItem == null) {
contentItem = _contentManager.New(contentType);
contentItem = _contentManager.Create(contentType, VersionOptions.Draft);
if (containerId != null) {
var containerItem = _contentManager.Get((int)containerId);
@@ -71,9 +72,10 @@ namespace Orchard.Core.Routable.Controllers {
}
_contentManager.UpdateEditor(contentItem, this);
_contentManager.Publish(contentItem);
_transactionManager.Cancel();
return Json(contentItem.As<IRoutableAspect>().Slug ?? slug);
return Json(contentItem.As<IRoutableAspect>().GetEffectiveSlug() ?? slug);
}

View File

@@ -20,7 +20,7 @@ namespace Orchard.Core.Routable.Drivers {
public RoutePartDriver(IOrchardServices services, IRoutableService routableService, IEnumerable<IHomePageProvider> homePageProviders) {
_services = services;
_routableService = routableService;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name); ;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name);
T = NullLocalizer.Instance;
}
@@ -41,8 +41,14 @@ namespace Orchard.Core.Routable.Drivers {
}
protected override DriverResult Display(RoutePart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_RoutableTitle",
() => shapeHelper.Parts_RoutableTitle(ContentPart: part, Title: part.Title, Path: part.Path));
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) {
@@ -54,18 +60,10 @@ namespace Orchard.Core.Routable.Drivers {
ContainerId = GetContainerId(part),
};
// TEMP: path format patterns replaces this logic
var path = part.Path;
var slug = part.Slug ?? "";
if (path != null && path.EndsWith(slug)) {
model.DisplayLeadingPath = path.Substring(0, path.Length - slug.Length);
}
else {
var containerPath = part.GetContainerPath();
model.DisplayLeadingPath = !string.IsNullOrWhiteSpace(containerPath)
? string.Format("{0}/", containerPath)
: "";
}
var containerPath = part.GetContainerPath();
model.DisplayLeadingPath = !string.IsNullOrWhiteSpace(containerPath)
? string.Format("{0}/", containerPath)
: "";
model.PromoteToHomePage = model.Id != 0 && part.Path != null && _routableHomePageProvider != null && _services.WorkContext.CurrentSite.HomePage == _routableHomePageProvider.GetSettingValue(model.Id);
return ContentShape("Parts_Routable_Edit",
@@ -73,34 +71,22 @@ namespace Orchard.Core.Routable.Drivers {
}
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;
if ( !_routableService.IsSlugValid(part.Slug) ) {
var slug = (part.Slug ?? String.Empty);
if ( slug.StartsWith(".") || slug.EndsWith(".") ) {
if ( slug.StartsWith(".") || slug.EndsWith(".") )
updater.AddModelError("Routable.Slug", T("The \".\" can't be used around routes."));
}
else {
else
updater.AddModelError("Routable.Slug", T("Please do not use any of the following characters in your slugs: \":\", \"?\", \"#\", \"[\", \"]\", \"@\", \"!\", \"$\", \"&\", \"'\", \"(\", \")\", \"*\", \"+\", \",\", \";\", \"=\". No spaces are allowed (please use dashes or underscores instead)."));
}
}
string originalSlug = part.Slug;
if (!_routableService.ProcessSlug(part)) {
_services.Notifier.Warning(T("Slugs in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
originalSlug, part.Slug, part.ContentItem.ContentType));
}
// TEMP: path format patterns replaces this logic
part.Path = part.GetPathWithSlug(part.Slug);
if (part.ContentItem.Id != 0 && model.PromoteToHomePage && _routableHomePageProvider != null) {
if (part.ContentItem.Id != 0 && model.PromoteToHomePage && _routableHomePageProvider != null)
_services.WorkContext.CurrentSite.HomePage = _routableHomePageProvider.GetSettingValue(part.ContentItem.Id);
}
return Editor(part, shapeHelper);
}

View File

@@ -24,25 +24,36 @@ namespace Orchard.Core.Routable.Handlers {
Action<RoutePart> processSlug = (
routable => {
var originalSlug = routable.Slug;
if (!_routableService.ProcessSlug(routable)) {
if (!_routableService.ProcessSlug(routable))
_services.Notifier.Warning(T("Slugs in conflict. \"{0}\" is already set for a previously created {2} so now it has the slug \"{1}\"",
originalSlug, routable.Slug, routable.ContentItem.ContentType));
}
// TEMP: path format patterns replaces this logic
routable.Path = routable.GetPathWithSlug(routable.Slug);
routable.Slug, routable.GetEffectiveSlug(), routable.ContentItem.ContentType));
});
OnGetDisplayShape<RoutePart>(SetModelProperties);
OnGetEditorShape<RoutePart>(SetModelProperties);
OnUpdateEditorShape<RoutePart>(SetModelProperties);
OnPublished<RoutePart>((context, routable) => {
OnPublished<RoutePart>((context, route) => {
var path = route.Path;
route.Path = route.GetPathWithSlug(route.Slug);
if (context.PublishingItemVersionRecord != null)
processSlug(routable);
if (!string.IsNullOrEmpty(routable.Path))
_routablePathConstraint.AddPath(routable.Path);
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);
});
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());

View File

@@ -17,25 +17,5 @@ namespace Orchard.Core.Routable.Models {
get { return Record.Path; }
set { Record.Path = value; }
}
public string GetContainerPath() {
var commonAspect = this.As<ICommonPart>();
if (commonAspect != null && commonAspect.Container != null) {
var routable = commonAspect.Container.As<IRoutableAspect>();
if (routable != null) {
return routable.Path;
}
}
return null;
}
public string GetPathWithSlug(string slug) {
// TEMP: path format patterns replaces this logic
var containerPath = GetContainerPath();
if (string.IsNullOrEmpty(containerPath)) {
return slug;
}
return containerPath + "/" + slug;
}
}
}

View File

@@ -1,9 +1,15 @@
<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="Header:5"/>
<Place Parts_RoutableTitle_Summary="Header:5"/>
</Match>
</Placement>

View File

@@ -1,16 +1,15 @@
using System;
using System.Collections.Generic;
using Orchard.Core.Routable.Models;
using System.Collections.Generic;
using Orchard.ContentManagement.Aspects;
namespace Orchard.Core.Routable.Services {
public interface IRoutableService : IDependency {
void FillSlugFromTitle<TModel>(TModel model) where TModel : RoutePart;
string GenerateUniqueSlug(RoutePart part, IEnumerable<string> existingPaths);
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<RoutePart> GetSimilarPaths(string path);
IEnumerable<IRoutableAspect> GetSimilarPaths(string path);
/// <summary>
/// Validates the given slug
@@ -21,7 +20,11 @@ namespace Orchard.Core.Routable.Services {
/// 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(RoutePart part);
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

@@ -45,7 +45,8 @@ namespace Orchard.Core.Routable.Services {
public void RemovePath(string path) {
lock (_syncLock) {
_paths.Remove(path);
if (path != null && _paths.ContainsKey(path))
_paths.Remove(path);
}
}

View File

@@ -3,6 +3,8 @@ 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.Models;
namespace Orchard.Core.Routable.Services {
@@ -13,7 +15,19 @@ namespace Orchard.Core.Routable.Services {
_contentManager = contentManager;
}
public void FillSlugFromTitle<TModel>(TModel model) where TModel : RoutePart {
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) {
itemRoute.ContentItem.VersionRecord.Published = false; // <- to force a republish
_contentManager.Publish(itemRoute.ContentItem);
}
}
public void FillSlugFromTitle<TModel>(TModel model) where TModel : IRoutableAspect {
if (!string.IsNullOrEmpty(model.Slug) || string.IsNullOrEmpty(model.Title))
return;
@@ -32,21 +46,20 @@ namespace Orchard.Core.Routable.Services {
model.Slug = slug.ToLowerInvariant();
}
public string GenerateUniqueSlug(RoutePart part, IEnumerable<string> existingPaths) {
var slugCandidate = part.Slug;
public string GenerateUniqueSlug(IRoutableAspect part, IEnumerable<string> existingPaths) {
if (existingPaths == null || !existingPaths.Contains(part.Path))
return slugCandidate;
return part.Slug;
int? version = existingPaths.Select(s => GetSlugVersion(slugCandidate, s)).OrderBy(i => i).LastOrDefault();
int? version = existingPaths.Select(s => GetSlugVersion(part.Path, s)).OrderBy(i => i).LastOrDefault();
return version != null
? string.Format("{0}-{1}", slugCandidate, version)
: slugCandidate;
? string.Format("{0}-{1}", part.Slug, version)
: part.Slug;
}
private static int? GetSlugVersion(string slugCandidate, string slug) {
private static int? GetSlugVersion(string path, string potentialConflictingPath) {
int v;
string[] slugParts = slug.Split(new []{slugCandidate}, StringSplitOptions.RemoveEmptyEntries);
string[] slugParts = potentialConflictingPath.Split(new[] { path }, StringSplitOptions.RemoveEmptyEntries);
if (slugParts.Length == 0)
return 2;
@@ -56,12 +69,12 @@ namespace Orchard.Core.Routable.Services {
: null;
}
public IEnumerable<RoutePart> GetSimilarPaths(string path) {
public IEnumerable<IRoutableAspect> GetSimilarPaths(string path) {
return
_contentManager.Query().Join<RoutePartRecord>()
.List()
.Select(i => i.As<RoutePart>())
.Where(routable => routable.Path != null && routable.Path.StartsWith(path, StringComparison.OrdinalIgnoreCase)) // todo: for some reason the filter doesn't work within the query, even without StringComparison or StartsWith
.Where(routable => routable.Path != null && routable.Path.StartsWith(path, StringComparison.OrdinalIgnoreCase))
.ToArray();
}
@@ -69,7 +82,7 @@ namespace Orchard.Core.Routable.Services {
return String.IsNullOrWhiteSpace(slug) || Regex.IsMatch(slug, @"^[^:?#\[\]@!$&'()*+,;=\s]+$") && !(slug.StartsWith(".") || slug.EndsWith("."));
}
public bool ProcessSlug(RoutePart part) {
public bool ProcessSlug(IRoutableAspect part) {
FillSlugFromTitle(part);
if (string.IsNullOrEmpty(part.Slug))
@@ -84,15 +97,48 @@ namespace Orchard.Core.Routable.Services {
if (pathsLikeThis.Count() > 0) {
var originalSlug = part.Slug;
//todo: (heskew) make auto-uniqueness optional
part.Slug = GenerateUniqueSlug(part, pathsLikeThis.Select(p => p.Path));
var newSlug = GenerateUniqueSlug(part, pathsLikeThis.Select(p => p.Path));
part.Path = part.GetPathWithSlug(newSlug);
part.Slug = newSlug;
if (originalSlug != part.Slug) {
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,6 +1 @@
@{
Orchard.ContentManagement.ContentItem contentItem = Model.ContentPart.ContentItem;
string title = Model.Title.ToString();
}
<h1>@Html.ItemDisplayLink(title, contentItem)</h1>
<h1>@Model.Title</h1>

View File

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

View File

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

View File

@@ -1,5 +1,7 @@
using System.Linq;
using Orchard.Blogs.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Localization;
using Orchard.UI.Navigation;
@@ -30,12 +32,12 @@ namespace Orchard.Blogs {
}
else if (singleBlog != null)
menu.Add(T("Manage Blog"), "1.0",
item => item.Action("Item", "BlogAdmin", new { area = "Orchard.Blogs", blogSlug = singleBlog.Slug }).Permission(Permissions.MetaListBlogs));
item => item.Action("Item", "BlogAdmin", new { area = "Orchard.Blogs", blogSlug = singleBlog.As<IRoutableAspect>().Path }).Permission(Permissions.MetaListBlogs));
if (singleBlog != null)
menu.Add(T("Create New Post"), "1.1",
item =>
item.Action("Create", "BlogPostAdmin", new { area = "Orchard.Blogs", blogSlug = singleBlog.Slug }).Permission(Permissions.PublishBlogPost));
item.Action("Create", "BlogPostAdmin", new { area = "Orchard.Blogs", blogSlug = singleBlog.As<IRoutableAspect>().Path }).Permission(Permissions.PublishBlogPost));
menu.Add(T("Create New Blog"), "1.2",
item =>

View File

@@ -8,6 +8,7 @@ using Orchard.ContentManagement.Aspects;
using Orchard.Core.Common.Models;
using Orchard.Core.Navigation.Models;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Security;
using Orchard.Blogs.Services;
using Orchard.Core.Navigation.Services;

View File

@@ -6,6 +6,7 @@ using Orchard.Blogs.Routing;
using Orchard.Blogs.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.DisplayManagement;
using Orchard.Localization;
@@ -71,18 +72,17 @@ namespace Orchard.Blogs.Controllers {
return View(model);
}
if (!blog.Has<IPublishingControlAspect>())
_contentManager.Publish(blog.ContentItem);
_contentManager.Publish(blog.ContentItem);
_blogSlugConstraint.AddSlug(blog.As<IRoutableAspect>().GetEffectiveSlug());
_blogSlugConstraint.AddSlug((string)model.Slug);
return Redirect(Url.BlogForAdmin((string)model.Slug));
return Redirect(Url.BlogForAdmin(blog));
}
public ActionResult Edit(string blogSlug) {
public ActionResult Edit(int id) {
if (!Services.Authorizer.Authorize(Permissions.ManageBlogs, T("Not allowed to edit blog")))
return new HttpUnauthorizedResult();
var blog = _blogService.Get(blogSlug);
var blog = _blogService.Get(id, VersionOptions.Latest);
if (blog == null)
return HttpNotFound();
@@ -91,20 +91,24 @@ namespace Orchard.Blogs.Controllers {
}
[HttpPost, ActionName("Edit")]
public ActionResult EditPOST(string blogSlug) {
public ActionResult EditPOST(int id) {
if (!Services.Authorizer.Authorize(Permissions.ManageBlogs, T("Couldn't edit blog")))
return new HttpUnauthorizedResult();
var blog = _blogService.Get(blogSlug);
var blog = _blogService.Get(id, VersionOptions.DraftRequired);
if (blog == null)
return HttpNotFound();
var model = Services.ContentManager.UpdateEditor(blog, this);
if (!ModelState.IsValid)
if (!ModelState.IsValid) {
Services.TransactionManager.Cancel();
return View(model);
}
_blogSlugConstraint.AddSlug(blog.Slug);
_contentManager.Publish(blog);
_blogSlugConstraint.AddSlug(blog.As<IRoutableAspect>().GetEffectiveSlug());
Services.Notifier.Information(T("Blog information updated"));
return Redirect(Url.BlogsForAdmin());
}

View File

@@ -1,9 +1,12 @@
using System;
using System.Reflection;
using System.Web.Mvc;
using Orchard.Blogs.Extensions;
using Orchard.Blogs.Models;
using Orchard.Blogs.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Contents.Settings;
using Orchard.Localization;
using Orchard.Mvc.AntiForgery;
using Orchard.UI.Admin;
@@ -39,7 +42,21 @@ namespace Orchard.Blogs.Controllers {
}
[HttpPost, ActionName("Create")]
[FormValueRequired("submit.Save")]
public ActionResult CreatePOST() {
return CreatePOST(contentItem => {
if (!contentItem.Has<IPublishingControlAspect>() && !contentItem.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
Services.ContentManager.Publish(contentItem);
});
}
[HttpPost, ActionName("Create")]
[FormValueRequired("submit.Publish")]
public ActionResult CreateAndPublishPOST() {
return CreatePOST(contentItem => Services.ContentManager.Publish(contentItem));
}
public ActionResult CreatePOST(Action<ContentItem> conditionallyPublish) {
if (!Services.Authorizer.Authorize(Permissions.EditBlogPost, T("Couldn't create blog post")))
return new HttpUnauthorizedResult();
@@ -55,11 +72,10 @@ namespace Orchard.Blogs.Controllers {
return View(model);
}
if (!blogPost.Has<IPublishingControlAspect>())
Services.ContentManager.Publish(blogPost.ContentItem);
conditionallyPublish(blogPost.ContentItem);
Services.Notifier.Information(T("Your {0} has been created.", blogPost.TypeDefinition.DisplayName));
return Redirect(Url.BlogPostEdit((string)model.Blog.Slug, (int)model.ContentItem.Id));
return Redirect(Url.BlogPostEdit(blogPost));
}
//todo: the content shape template has extra bits that the core contents module does not (remove draft functionality)
@@ -82,7 +98,21 @@ namespace Orchard.Blogs.Controllers {
}
[HttpPost, ActionName("Edit")]
public ActionResult EditPOST(string blogSlug, int postId) {
[FormValueRequired("submit.Save")]
public ActionResult EditPOST(string blogSlug, int postId, string returnUrl) {
return EditPOST(blogSlug, postId, returnUrl, contentItem => {
if (!contentItem.Has<IPublishingControlAspect>() && !contentItem.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
Services.ContentManager.Publish(contentItem);
});
}
[HttpPost, ActionName("Edit")]
[FormValueRequired("submit.Publish")]
public ActionResult EditAndPublishPOST(string blogSlug, int postId, string returnUrl) {
return EditPOST(blogSlug, postId, returnUrl, contentItem => Services.ContentManager.Publish(contentItem));
}
public ActionResult EditPOST(string blogSlug, int postId, string returnUrl, Action<ContentItem> conditionallyPublish) {
if (!Services.Authorizer.Authorize(Permissions.EditBlogPost, T("Couldn't edit blog post")))
return new HttpUnauthorizedResult();
@@ -102,8 +132,14 @@ namespace Orchard.Blogs.Controllers {
return View(model);
}
conditionallyPublish(blogPost.ContentItem);
Services.Notifier.Information(T("Your {0} has been saved.", blogPost.TypeDefinition.DisplayName));
return Redirect(Url.BlogPostEdit((BlogPostPart)model.ContentItem.Get(typeof(BlogPostPart))));
if (!String.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
return Redirect(Url.BlogPostEdit(blogPost));
}
[ValidateAntiForgeryTokenOrchard]
@@ -142,7 +178,7 @@ namespace Orchard.Blogs.Controllers {
ActionResult RedirectToEdit(IContent item) {
if (item == null || item.As<BlogPostPart>() == null)
return HttpNotFound();
return RedirectToAction("Edit", new { BlogSlug = item.As<BlogPostPart>().BlogPart.Slug, PostId = item.ContentItem.Id });
return RedirectToAction("Edit", new { BlogSlug = item.As<IRoutableAspect>().Path, PostId = item.ContentItem.Id });
}
[ValidateAntiForgeryTokenOrchard]
@@ -162,7 +198,7 @@ namespace Orchard.Blogs.Controllers {
_blogPostService.Delete(post);
Services.Notifier.Information(T("Blog post was successfully deleted"));
return Redirect(Url.BlogForAdmin(blogSlug));
return Redirect(Url.BlogForAdmin(blog));
}
[ValidateAntiForgeryTokenOrchard]
@@ -181,7 +217,7 @@ namespace Orchard.Blogs.Controllers {
_blogPostService.Publish(post);
Services.Notifier.Information(T("Blog post successfully published."));
return Redirect(Url.BlogForAdmin(blog.Slug));
return Redirect(Url.BlogForAdmin(blog));
}
[ValidateAntiForgeryTokenOrchard]
@@ -200,7 +236,7 @@ namespace Orchard.Blogs.Controllers {
_blogPostService.Unpublish(post);
Services.Notifier.Information(T("Blog post successfully unpublished."));
return Redirect(Url.BlogForAdmin(blog.Slug));
return Redirect(Url.BlogForAdmin(blog));
}
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
@@ -211,4 +247,17 @@ namespace Orchard.Blogs.Controllers {
ModelState.AddModelError(key, errorMessage.ToString());
}
}
public class FormValueRequiredAttribute : ActionMethodSelectorAttribute {
private readonly string _submitButtonName;
public FormValueRequiredAttribute(string submitButtonName) {
_submitButtonName = submitButtonName;
}
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
var value = controllerContext.HttpContext.Request.Form[_submitButtonName];
return !string.IsNullOrEmpty(value);
}
}
}

View File

@@ -1,5 +1,7 @@
using JetBrains.Annotations;
using Orchard.Blogs.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.ContentManagement.Drivers;
using Orchard.Environment.Extensions;
@@ -8,7 +10,7 @@ namespace Orchard.Blogs.Drivers {
[OrchardFeature("Orchard.Blogs.RemotePublishing")]
public class RemoteBlogPublishingDriver : ContentPartDriver<BlogPart> {
protected override DriverResult Display(BlogPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Blogs_RemotePublishing", shape => shape.Slug(part.Slug));
return ContentShape("Parts_Blogs_RemotePublishing", shape => shape.Path(part.As<IRoutableAspect>().Path));
}
}
}

View File

@@ -1,5 +1,8 @@
using System.Web.Mvc;
using Orchard.Blogs.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Routable.Services;
using Orchard.Mvc.Extensions;
namespace Orchard.Blogs.Extensions {
@@ -12,92 +15,68 @@ namespace Orchard.Blogs.Extensions {
return urlHelper.Action("List", "BlogAdmin", new {area = "Orchard.Blogs"});
}
public static string Blog(this UrlHelper urlHelper, string blogSlug) {
return urlHelper.Action("Item", "Blog", new {blogSlug, area = "Orchard.Blogs"});
public static string Blog(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.Action("Item", "Blog", new { blogSlug = blogPart.As<IRoutableAspect>().Path, area = "Orchard.Blogs" });
}
public static string BlogLiveWriterManifest(this UrlHelper urlHelper, string blogSlug) {
return urlHelper.AbsoluteAction(() => urlHelper.Action("LiveWriterManifest", "Blog", new { blogSlug, area = "Orchard.Blogs" }));
public static string BlogLiveWriterManifest(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.AbsoluteAction(() => urlHelper.Action("LiveWriterManifest", "Blog", new { blogSlug = blogPart.As<IRoutableAspect>().Path, area = "Orchard.Blogs" }));
}
public static string BlogRsd(this UrlHelper urlHelper, string blogSlug) {
return urlHelper.AbsoluteAction(() => urlHelper.Action("Rsd", "Blog", new { blogSlug, area = "Orchard.Blogs" }));
public static string BlogRsd(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.AbsoluteAction(() => urlHelper.Action("Rsd", "Blog", new { blogSlug = blogPart.As<IRoutableAspect>().Path, area = "Orchard.Blogs" }));
}
public static string BlogArchiveYear(this UrlHelper urlHelper, string blogSlug, int year) {
return urlHelper.Action("ListByArchive", "BlogPost", new { blogSlug, archiveData = year.ToString(), area = "Orchard.Blogs" });
public static string BlogArchiveYear(this UrlHelper urlHelper, BlogPart blogPart, int year) {
return urlHelper.Action("ListByArchive", "BlogPost", new { blogSlug = blogPart.As<IRoutableAspect>().Path, archiveData = year.ToString(), area = "Orchard.Blogs" });
}
public static string BlogArchiveMonth(this UrlHelper urlHelper, string blogSlug, int year, int month) {
return urlHelper.Action("ListByArchive", "BlogPost", new { blogSlug, archiveData = string.Format("{0}/{1}", year, month), area = "Orchard.Blogs" });
public static string BlogArchiveMonth(this UrlHelper urlHelper, BlogPart blogPart, int year, int month) {
return urlHelper.Action("ListByArchive", "BlogPost", new { blogSlug = blogPart.As<IRoutableAspect>().Path, archiveData = string.Format("{0}/{1}", year, month), area = "Orchard.Blogs" });
}
public static string BlogArchiveDay(this UrlHelper urlHelper, string blogSlug, int year, int month, int day) {
return urlHelper.Action("ListByArchive", "BlogPost", new {blogSlug, archiveData = string.Format("{0}/{1}/{2}", year, month, day), area = "Orchard.Blogs"});
public static string BlogArchiveDay(this UrlHelper urlHelper, BlogPart blogPart, int year, int month, int day) {
return urlHelper.Action("ListByArchive", "BlogPost", new { blogSlug = blogPart.As<IRoutableAspect>().Path, archiveData = string.Format("{0}/{1}/{2}", year, month, day), area = "Orchard.Blogs" });
}
public static string BlogForAdmin(this UrlHelper urlHelper, string blogSlug) {
return urlHelper.Action("Item", "BlogAdmin", new {blogSlug, area = "Orchard.Blogs"});
public static string BlogForAdmin(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.Action("Item", "BlogAdmin", new { blogSlug = blogPart.As<IRoutableAspect>().Path, area = "Orchard.Blogs" });
}
public static string BlogCreate(this UrlHelper urlHelper) {
return urlHelper.Action("Create", "BlogAdmin", new {area = "Orchard.Blogs"});
}
public static string BlogEdit(this UrlHelper urlHelper, string blogSlug) {
return urlHelper.Action("Edit", "BlogAdmin", new {blogSlug, area = "Orchard.Blogs"});
public static string BlogEdit(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.Action("Edit", "BlogAdmin", new { blogPart.Id, area = "Orchard.Blogs" });
}
public static string BlogRemove(this UrlHelper urlHelper, string blogSlug) {
return urlHelper.Action("Remove", "BlogAdmin", new {blogSlug, area = "Orchard.Blogs"});
}
public static string BlogPost(this UrlHelper urlHelper, BlogPostPart blogPostPart) {
return urlHelper.BlogPost(blogPostPart.BlogPart.Slug, blogPostPart.Slug);
}
public static string BlogPost(this UrlHelper urlHelper, string blogSlug, string postSlug) {
return urlHelper.Action("Item", "BlogPost", new {blogSlug, postSlug, area = "Orchard.Blogs"});
public static string BlogRemove(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.Action("Remove", "BlogAdmin", new { blogPart.Id, area = "Orchard.Blogs" });
}
public static string BlogPostCreate(this UrlHelper urlHelper, BlogPart blogPart) {
return urlHelper.BlogPostCreate(blogPart.Slug);
return urlHelper.Action("Create", "BlogPostAdmin", new { blogSlug = blogPart.As<IRoutableAspect>().Path, area = "Orchard.Blogs" });
}
public static string BlogPostCreate(this UrlHelper urlHelper, string blogSlug) {
return urlHelper.Action("Create", "BlogPostAdmin", new {blogSlug, area = "Orchard.Blogs"});
public static string BlogPost(this UrlHelper urlHelper, BlogPostPart blogPostPart) {
return urlHelper.Action("Item", "BlogPost", new { blogSlug = blogPostPart.BlogPart.As<IRoutableAspect>().Path, postSlug = blogPostPart.As<IRoutableAspect>().GetEffectiveSlug(), area = "Orchard.Blogs" });
}
public static string BlogPostEdit(this UrlHelper urlHelper, BlogPostPart blogPostPart) {
return urlHelper.BlogPostEdit(blogPostPart.BlogPart.Slug, blogPostPart.Id);
}
public static string BlogPostEdit(this UrlHelper urlHelper, string blogSlug, int postId) {
return urlHelper.Action("Edit", "BlogPostAdmin", new {blogSlug, postId, area = "Orchard.Blogs"});
return urlHelper.Action("Edit", "BlogPostAdmin", new { blogSlug = blogPostPart.BlogPart.As<IRoutableAspect>().Path, postId = blogPostPart.Id, area = "Orchard.Blogs" });
}
public static string BlogPostDelete(this UrlHelper urlHelper, BlogPostPart blogPostPart) {
return urlHelper.BlogPostDelete(blogPostPart.BlogPart.Slug, blogPostPart.Id);
}
public static string BlogPostDelete(this UrlHelper urlHelper, string blogSlug, int postId) {
return urlHelper.Action("Delete", "BlogPostAdmin", new {blogSlug, postId, area = "Orchard.Blogs"});
return urlHelper.Action("Delete", "BlogPostAdmin", new { blogSlug = blogPostPart.BlogPart.As<IRoutableAspect>().Path, postId = blogPostPart.Id, area = "Orchard.Blogs" });
}
public static string BlogPostPublish(this UrlHelper urlHelper, BlogPostPart blogPostPart) {
return urlHelper.BlogPostPublish(blogPostPart.BlogPart.Slug, blogPostPart.Id);
}
public static string BlogPostPublish(this UrlHelper urlHelper, string blogSlug, int postId) {
return urlHelper.Action("Publish", "BlogPostAdmin", new { blogSlug, postId, area = "Orchard.Blogs" });
return urlHelper.Action("Publish", "BlogPostAdmin", new { blogSlug = blogPostPart.BlogPart.As<IRoutableAspect>().Path, postId = blogPostPart.Id, area = "Orchard.Blogs" });
}
public static string BlogPostUnpublish(this UrlHelper urlHelper, BlogPostPart blogPostPart) {
return urlHelper.BlogPostUnpublish(blogPostPart.BlogPart.Slug, blogPostPart.Id);
}
public static string BlogPostUnpublish(this UrlHelper urlHelper, string blogSlug, int postId) {
return urlHelper.Action("Unpublish", "BlogPostAdmin", new { blogSlug, postId, area = "Orchard.Blogs" });
return urlHelper.Action("Unpublish", "BlogPostAdmin", new { blogSlug = blogPostPart.BlogPart.As<IRoutableAspect>().Path, postId = blogPostPart.Id, area = "Orchard.Blogs" });
}
}
}

View File

@@ -9,11 +9,6 @@ namespace Orchard.Blogs.Models {
set { this.As<RoutePart>().Title = value; }
}
public string Slug {
get { return this.As<RoutePart>().Slug; }
set { this.As<RoutePart>().Slug = value; }
}
public string Description {
get { return Record.Description; }
set { Record.Description = value; }

View File

@@ -35,15 +35,13 @@ namespace Orchard.Blogs {
},
new RouteDescriptor {
Route = new Route(
"Admin/Blogs/{blogSlug}/Edit",
"Admin/Blogs/{id}/Edit",
new RouteValueDictionary {
{"area", "Orchard.Blogs"},
{"controller", "BlogAdmin"},
{"action", "Edit"}
},
new RouteValueDictionary {
{"blogSlug", _blogSlugConstraint}
},
new RouteValueDictionary (),
new RouteValueDictionary {
{"area", "Orchard.Blogs"}
},
@@ -51,15 +49,13 @@ namespace Orchard.Blogs {
},
new RouteDescriptor {
Route = new Route(
"Admin/Blogs/{blogSlug}/Remove",
"Admin/Blogs/{id}/Remove",
new RouteValueDictionary {
{"area", "Orchard.Blogs"},
{"controller", "BlogAdmin"},
{"action", "Remove"}
},
new RouteValueDictionary {
{"blogSlug", _blogSlugConstraint}
},
new RouteValueDictionary (),
new RouteValueDictionary {
{"area", "Orchard.Blogs"}
},

View File

@@ -1,6 +1,8 @@
using System.Linq;
using JetBrains.Annotations;
using Orchard.Blogs.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Environment;
using Orchard.Tasks;
@@ -27,7 +29,7 @@ namespace Orchard.Blogs.Routing {
}
private void Refresh() {
_blogSlugConstraint.SetSlugs(_blogService.Get().Select(b => b.Slug));
_blogSlugConstraint.SetSlugs(_blogService.Get().Select(b => b.As<IRoutableAspect>().Path));
}
}
}

View File

@@ -3,9 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Orchard.Blogs.Models;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Common.Models;
using Orchard.ContentManagement;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Data;
using Orchard.Tasks.Scheduling;
@@ -27,8 +29,9 @@ namespace Orchard.Blogs.Services {
}
public BlogPostPart Get(BlogPart blogPart, string slug, VersionOptions versionOptions) {
var postSlug = blogPart.As<IRoutableAspect>().GetChildPath(slug);
return
_contentManager.Query(versionOptions, "BlogPost").Join<RoutePartRecord>().Where(rr => rr.Slug == slug).
_contentManager.Query(versionOptions, "BlogPost").Join<RoutePartRecord>().Where(rr => rr.Path == postSlug).
Join<CommonPartRecord>().Where(cr => cr.Container == blogPart.Record.ContentItemRecord).List().
SingleOrDefault().As<BlogPostPart>();
}

View File

@@ -4,6 +4,7 @@ using JetBrains.Annotations;
using Orchard.Blogs.Models;
using Orchard.Blogs.Routing;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Aspects;
using Orchard.Core.Routable.Models;
namespace Orchard.Blogs.Services {
@@ -17,12 +18,16 @@ namespace Orchard.Blogs.Services {
_blogSlugConstraint = blogSlugConstraint;
}
public BlogPart Get(string slug) {
public BlogPart Get(string path) {
return _contentManager.Query<BlogPart, BlogPartRecord>()
.Join<RoutePartRecord>().Where(rr => rr.Slug == slug)
.Join<RoutePartRecord>().Where(rr => rr.Path == path)
.List().FirstOrDefault();
}
public ContentItem Get(int id, VersionOptions versionOptions) {
return _contentManager.Get(id, versionOptions);
}
public IEnumerable<BlogPart> Get() {
return _contentManager.Query<BlogPart, BlogPartRecord>()
.Join<RoutePartRecord>()
@@ -32,7 +37,7 @@ namespace Orchard.Blogs.Services {
public void Delete(BlogPart blogPart) {
_contentManager.Remove(blogPart.ContentItem);
_blogSlugConstraint.RemoveSlug(blogPart.Slug);
_blogSlugConstraint.RemoveSlug(blogPart.As<IRoutableAspect>().Path);
}
}
}

View File

@@ -1,9 +1,11 @@
using System.Collections.Generic;
using Orchard.Blogs.Models;
using Orchard.ContentManagement;
namespace Orchard.Blogs.Services {
public interface IBlogService : IDependency {
BlogPart Get(string slug);
ContentItem Get(int id, VersionOptions versionOptions);
IEnumerable<BlogPart> Get();
void Delete(BlogPart blogPart);
}

View File

@@ -117,8 +117,9 @@ namespace Orchard.Blogs.Services {
var array = new XRpcArray();
foreach (var blog in _blogService.Get()) {
var thisBlog = blog;
array.Add(new XRpcStruct()
.Set("url", urlHelper.AbsoluteAction(() => urlHelper.Blog(blog.Slug)))
.Set("url", urlHelper.AbsoluteAction(() => urlHelper.Blog(thisBlog)))
.Set("blogid", blog.Id)
.Set("blogName", blog.Name));
}

View File

@@ -1,9 +1,10 @@
@using Orchard.Blogs.Extensions;
@using Orchard.Blogs.Models;
<h1 class="page-title">@Html.TitleForPage(T("Archives").Text, (string)Model.ArchiveData.Year.ToString(), (string)(Model.ArchiveData.Month > 0 ? new DateTime(Model.ArchiveData.Year, Model.ArchiveData.Month, 1).ToString("MMMM") : null), (string)(Model.ArchiveData.Day > 0 ? Model.ArchiveData.Day.ToString() : null))</h1>
<div class="archive-trail">
@T("Archives") /
@Html.Link((string)Model.ArchiveData.Year.ToString(), Url.BlogArchiveYear((string)Model.Blog.Slug, (int)Model.ArchiveData.Year))
@(new MvcHtmlString(Model.ArchiveData.Month > 0 ? string.Format(" / {0}", Html.Link((string)Model.ArchiveData.ToDateTime().ToString("MMMM"), Url.BlogArchiveMonth((string)Model.Blog.Slug,(int) Model.ArchiveData.Year, (int)Model.ArchiveData.Month))) : ""))
@(new MvcHtmlString(Model.ArchiveData.Day > 0 ? string.Format(" / {0}", Html.Link((string)Model.ArchiveData.Day.ToString(), Url.BlogArchiveDay((string)Model.Blog.Slug, (int)Model.ArchiveData.Year, (int)Model.ArchiveData.Month, (int)Model.ArchiveData.Day))) : ""))
@Html.Link((string)Model.ArchiveData.Year.ToString(), Url.BlogArchiveYear((BlogPart)Model.Blog, (int)Model.ArchiveData.Year))
@(new MvcHtmlString(Model.ArchiveData.Month > 0 ? string.Format(" / {0}", Html.Link((string)Model.ArchiveData.ToDateTime().ToString("MMMM"), Url.BlogArchiveMonth((BlogPart)Model.Blog, (int)Model.ArchiveData.Year, (int)Model.ArchiveData.Month))) : ""))
@(new MvcHtmlString(Model.ArchiveData.Day > 0 ? string.Format(" / {0}", Html.Link((string)Model.ArchiveData.Day.ToString(), Url.BlogArchiveDay((BlogPart)Model.Blog, (int)Model.ArchiveData.Year, (int)Model.ArchiveData.Month, (int)Model.ArchiveData.Day))) : ""))
</div>
@Display(Model.ContentItems)

View File

@@ -5,12 +5,13 @@
@{
Script.Require("ShapesBase");
ContentItem contentItem = Model.ContentItem;
BlogPart blog = (BlogPart)contentItem.Get(typeof(BlogPart));
var returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString();
}
<div class="summary" itemscope="itemscope" itemid="@contentItem.Id" itemtype="http://orchardproject.net/data/ContentItem">
<div class="properties">
<input type="checkbox" value="@contentItem.Id" name="itemIds"/>
<h3>@Html.Link((string)Model.Title, Url.BlogForAdmin((string)Model.Slug))</h3>
<h3>@Html.Link((string)Model.Title, Url.BlogForAdmin((BlogPart)blog))</h3>
@if (Model.Header != null) {
<div class="header">@Display(Model.Header)</div>
}
@@ -20,11 +21,11 @@
</div>
<div class="related">
@Display(Model.Actions)
<a href="@Url.Blog((string)Model.Slug)" title="@T("View")">@T("View")</a>@T(" | ")
<a href="@Url.BlogForAdmin((string)Model.Slug)" title="@T("List Posts")">@T("List Posts")</a>@T(" | ")
<a href="@Url.BlogPostCreate((BlogPart)Model.ContentItem.Get(typeof(BlogPart)))" title="@T("New Post")">@T("New Post")</a>@T(" | ")
<a href="@Url.BlogEdit((string)Model.Slug)" title="@T("Edit")">@T("Edit")</a>@T(" | ")
<a href="@Url.BlogRemove((string)Model.Slug)" title="@T("Remove")" itemprop="RemoveUrl UnsafeUrl">@T("Remove")</a>
<a href="@Url.Blog(blog)" title="@T("View")">@T("View")</a>@T(" | ")
<a href="@Url.BlogForAdmin(blog)" title="@T("List Posts")">@T("List Posts")</a>@T(" | ")
<a href="@Url.BlogPostCreate(blog)" title="@T("New Post")">@T("New Post")</a>@T(" | ")
<a href="@Url.BlogEdit(blog)" title="@T("Edit")">@T("Edit")</a>@T(" | ")
<a href="@Url.BlogRemove(blog)" title="@T("Remove")" itemprop="RemoveUrl UnsafeUrl">@T("Remove")</a>
</div>
@if (Model.Content != null) {
<div class="primary">@Display(Model.Content)</div>

View File

@@ -1,9 +1,10 @@
@using Orchard.Blogs.Extensions;
@using Orchard.Blogs.Models;
@{
Style.Require("BlogsAdmin");
}
@if (AuthorizedFor(Orchard.Blogs.Permissions.ManageBlogs)) {
<div class="item-properties actions">
<p><a href="@Url.BlogEdit((string)Model.ContentPart.Slug)" class="edit">@T("Blog Properties")</a></p>
<p><a href="@Url.BlogEdit((BlogPart)Model.ContentPart)" class="edit">@T("Blog Properties")</a></p>
</div>
}

View File

@@ -25,14 +25,14 @@
if (year != lastYear) {
<li class="previous">
<h4>@year <span>(@yearMonths.Sum(ym => ym.Value))</span></h4>
@Html.UnorderedList(yearMonths, (t, i) => Html.Link(string.Format("{0:MMMM} ({1})", t.Key.ToDateTime(), t.Value), Url.BlogArchiveMonth((string)Model.BlogPart.Slug, t.Key.Year, t.Key.Month)), "archiveMonthList")
@Html.UnorderedList(yearMonths, (t, i) => Html.Link(string.Format("{0:MMMM} ({1})", t.Key.ToDateTime(), t.Value), Url.BlogArchiveMonth((BlogPart)Model.BlogPart, t.Key.Year, t.Key.Month)), "archiveMonthList")
</li>
}
}
</ul>
}
else if (archives.Count() > 0) {
@Html.UnorderedList(archives, (t, i) => Html.Link(string.Format("{0:MMMM yyyy} ({1})", t.Key.ToDateTime(), t.Value), Url.BlogArchiveMonth((string)Model.BlogPart.Slug, t.Key.Year, t.Key.Month)), "archiveMonthList")
@Html.UnorderedList(archives, (t, i) => Html.Link(string.Format("{0:MMMM yyyy} ({1})", t.Key.ToDateTime(), t.Value), Url.BlogArchiveMonth((BlogPart)Model.BlogPart, t.Key.Year, t.Key.Month)), "archiveMonthList")
}
else {
<div class="message info">@T("None found")</div>

View File

@@ -1,6 +1,8 @@
@using Orchard.Blogs.Extensions;
@using Orchard.Blogs.Models;
@using Orchard.UI.Resources;
@{
RegisterLink(new LinkEntry { Rel = "wlwmanifest", Type = "application/wlwmanifest+xml", Href = Url.BlogLiveWriterManifest((string)Model.Slug) });
RegisterLink(new LinkEntry { Rel = "EditURI", Type = "application/rsd+xml", Title = "RSD", Href = Url.BlogRsd((string)Model.Slug) });
BlogPart blog = Model;
RegisterLink(new LinkEntry { Rel = "wlwmanifest", Type = "application/wlwmanifest+xml", Href = Url.BlogLiveWriterManifest(blog) });
RegisterLink(new LinkEntry { Rel = "EditURI", Type = "application/rsd+xml", Title = "RSD", Href = Url.BlogRsd(blog) });
}

View File

@@ -1,6 +1,7 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
@@ -9,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("Orchard")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyCopyright("Copyright <20> Outercurve Foundation 2009")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -30,5 +31,6 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("0.8.0.0")]
[assembly: AssemblyFileVersion("0.8.0.0")]
[assembly: SecurityTransparent]

View File

@@ -0,0 +1,19 @@
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Contents.Extensions;
using Orchard.Data.Migration;
namespace Orchard.Pages {
public class Migrations : DataMigrationImpl {
public int Create() {
ContentDefinitionManager.AlterTypeDefinition("Page",
cfg=>cfg
.WithPart("CommonPart")
.WithPart("PublishLaterPart")
.WithPart("RoutePart")
.WithPart("BodyPart")
.Creatable());
return 1;
}
}
}

View File

@@ -0,0 +1,12 @@
Name: Pages
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 0.8.0
OrchardVersion: 0.8.0
Description: Introduces a preconfigured page content type.
Features:
Orchard.Pages:
Description: A basic page content type.
Dependencies: Contents
Category: Content

View File

@@ -0,0 +1,96 @@
<?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>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{3420C92A-747F-4990-BA08-F2C9531E44AD}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Orchard.Pages</RootNamespace>
<AssemblyName>Orchard.Pages</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
</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="Microsoft.CSharp" />
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web.Services" />
<Reference Include="System.EnterpriseServices" />
</ItemGroup>
<ItemGroup>
<Compile Include="Migrations.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<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>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>False</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>6857</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>
</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>True</UseCustomServer>
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<!-- 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,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Orchard.Pages")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyProduct("Orchard")]
[assembly: AssemblyCopyright("Copyright © 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("0d852f97-2bc9-42ee-91b6-90e90dbbe6ca")]
// 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("0.8.0.0")]
[assembly: AssemblyFileVersion("0.8.0.0")]
[assembly: SecurityTransparent]

View File

@@ -86,6 +86,7 @@ namespace Orchard.Setup.Services {
"Orchard.Roles",
"TinyMce",
"PackagingServices",
"Orchard.Pages",
"Orchard.Modules",
"Orchard.Themes",
"Orchard.PublishLater",
@@ -213,7 +214,7 @@ namespace Orchard.Setup.Services {
//hackInstallationGenerator.GenerateInstallEvents();
var contentDefinitionManager = environment.Resolve<IContentDefinitionManager>();
//todo: (heskew) pull these definitions (and initial content creation) out into more appropriate modules
//todo: (heskew) pull these definitions (and initial content creation) out into a distribution configuration when we have that capability
contentDefinitionManager.AlterTypeDefinition("BlogPost", cfg => cfg
.WithPart("CommentsPart")
.WithPart("TagsPart")
@@ -222,13 +223,8 @@ namespace Orchard.Setup.Services {
.Indexed()
);
contentDefinitionManager.AlterTypeDefinition("Page", cfg => cfg
.WithPart("CommonPart")
.WithPart("PublishLaterPart")
.WithPart("RoutePart")
.WithPart("BodyPart")
.WithPart("TagsPart")
.WithPart("LocalizationPart")
.Creatable()
.Draftable()
.Indexed()
);

View File

@@ -94,6 +94,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.PublishLater", "Orc
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Lists", "Orchard.Web\Modules\Orchard.Lists\Orchard.Lists.csproj", "{137906EA-15FE-4AD8-A6A0-27528F0477D6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Pages", "Orchard.Web\Modules\Orchard.Pages\Orchard.Pages.csproj", "{3420C92A-747F-4990-BA08-F2C9531E44AD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
CodeCoverage|Any CPU = CodeCoverage|Any CPU
@@ -500,6 +502,16 @@ Global
{137906EA-15FE-4AD8-A6A0-27528F0477D6}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
{137906EA-15FE-4AD8-A6A0-27528F0477D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{137906EA-15FE-4AD8-A6A0-27528F0477D6}.Release|Any CPU.Build.0 = Release|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.Coverage|Any CPU.ActiveCfg = Release|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.Coverage|Any CPU.Build.0 = Release|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.FxCop|Any CPU.ActiveCfg = Release|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.FxCop|Any CPU.Build.0 = Release|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3420C92A-747F-4990-BA08-F2C9531E44AD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -530,6 +542,7 @@ Global
{EA4F1DA7-F2AB-4384-9AA4-9B756E2026B1} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{C889167C-E52C-4A65-A419-224B3D1B957D} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{137906EA-15FE-4AD8-A6A0-27528F0477D6} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{3420C92A-747F-4990-BA08-F2C9531E44AD} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5}
{ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
{F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}
{6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA}