diff --git a/src/Orchard.Specs/Bindings/WebAppHosting.cs b/src/Orchard.Specs/Bindings/WebAppHosting.cs index 10add5711..4f924047c 100644 --- a/src/Orchard.Specs/Bindings/WebAppHosting.cs +++ b/src/Orchard.Specs/Bindings/WebAppHosting.cs @@ -206,7 +206,7 @@ namespace Orchard.Specs.Bindings { [When(@"I fill in")] public void WhenIFillIn(Table table) { var inputs = _doc.DocumentNode - .SelectNodes("(//input|//textarea)") ?? Enumerable.Empty(); + .SelectNodes("(//input|//textarea|//select)") ?? Enumerable.Empty(); foreach (var row in table.Rows) { var r = row; @@ -244,7 +244,19 @@ namespace Orchard.Specs.Bindings { break; default: - input.Attributes.Add("value", row["value"]); + if (string.Equals(input.Name, "select", StringComparison.OrdinalIgnoreCase)) { + var options = input.ChildNodes; + foreach (var option in options) { + if (option.GetAttributeValue("value", "") == row["value"]) + option.Attributes.Add("selected", "selected"); + else if (option.Attributes.Contains("selected")) + option.Attributes.Remove("selected"); + } + + } + else { + input.Attributes.Add("value", row["value"]); + } break; } } diff --git a/src/Orchard.Specs/Orchard.Specs.csproj b/src/Orchard.Specs/Orchard.Specs.csproj index 495651fba..277249a31 100644 --- a/src/Orchard.Specs/Orchard.Specs.csproj +++ b/src/Orchard.Specs/Orchard.Specs.csproj @@ -196,6 +196,11 @@ True SiteCompilation.feature + + True + True + Users.feature + True True @@ -296,6 +301,10 @@ SpecFlowSingleFileGenerator Pages.feature.cs + + SpecFlowSingleFileGenerator + Users.feature.cs + SpecFlowSingleFileGenerator WebHosting.feature.cs diff --git a/src/Orchard.Specs/Users.feature b/src/Orchard.Specs/Users.feature new file mode 100644 index 000000000..8128b632d --- /dev/null +++ b/src/Orchard.Specs/Users.feature @@ -0,0 +1,262 @@ +Feature: Users + In order to prevent users module regressions + As a site owner + I want to create, search and modify user accounts + +@management +Scenario: There is only one user by default + Given I have installed Orchard + When I go to "admin/users" + Then I should see "Manage Users" + And I should see "]*>admin" + +@management +Scenario: I can create a new user + Given I have installed Orchard + When I go to "admin/users" + Then I should see "Manage Users" + When I follow "Add a new user" + And I fill in + | name | value | + | UserName | user1 | + | Email | user1@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + And I hit "Save" + And I am redirected + Then I should see "User created" + When I follow "Add a new user" + And I fill in + | name | value | + | UserName | user2 | + | Email | user2@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + | UserRoles.Roles[0].Granted | true | + And I hit "Save" + And I am redirected + Then I should see "User created" + And I should see "Adding role Administrator to user user2" + When I follow "Add a new user" + And I fill in + | name | value | + | UserName | user3 | + | Email | user3@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + | UserRoles.Roles[0].Granted | true | + | UserRoles.Roles[1].Granted | true | + | UserRoles.Roles[2].Granted | true | + | UserRoles.Roles[3].Granted | true | + | UserRoles.Roles[4].Granted | true | + And I hit "Save" + And I am redirected + Then I should see "User created" + And I should see "Adding role Administrator to user user3" + And I should see "Adding role Editor to user user3" + And I should see "Adding role Moderator to user user3" + And I should see "Adding role Author to user user3" + And I should see "Adding role Contributor to user user3" + When I follow "Add a new user" + And I hit "Save" + Then I should see "The UserName field is required." + Then I should see "The Email field is required." + Then I should see "The Password field is required." + Then I should see "The ConfirmPassword field is required." + When I go to "admin/users" + And I follow "Add a new user" + And I fill in + | name | value | + | UserName | user4 | + | Email | user4@domain.com | + | Password | a12345! | + | ConfirmPassword | a123456! | + And I hit "Save" + Then I should see "Password confirmation must match" + +@management +Scenario: I can edit an existing user + Given I have installed Orchard + When I go to "admin/users" + And I follow "Add a new user" + And I fill in + | name | value | + | UserName | user1 | + | Email | user1@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + And I hit "Save" + And I am redirected + Then I should see "User created" + When I fill in + | name | value | + | Options.Search | user1 | + And I hit "Filter" + Then I should see "]*>user1" + When I follow "Edit" + And I fill in + | name | value | + | UserName | user2 | + | Email | user2@domain.com | + And I hit "Save" + And I am redirected + Then I should see "User information updated" + And I should see "]*>user2" + And I should see "user2@domain.com" + +@management +Scenario: I should not be able to reuse an existing username or email + Given I have installed Orchard + When I go to "admin/users" +# create user1 + And I follow "Add a new user" + And I fill in + | name | value | + | UserName | user1 | + | Email | user1@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + And I hit "Save" + And I am redirected +# create user2 + And I follow "Add a new user" + And I fill in + | name | value | + | UserName | user2 | + | Email | user2@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + And I hit "Save" + And I am redirected + Then I should see "]*>user1" + And I should see "]*>user2" +# filtering on 'user1' to have only one Edit link to follow + When I fill in + | name | value | + | Options.Search | user1 | + And I hit "Filter" + Then I should see "]*>user1" + When I follow "Edit" + And I fill in + | name | value | + | UserName | user2 | + | Email | user1@domain.com | + And I hit "Save" + Then I should see "User with that username and/or email already exists." + When I fill in + | name | value | + | UserName | user1 | + | Email | user2@domain.com | + And I hit "Save" + Then I should see "User with that username and/or email already exists." + +@filtering +Scenario: I should not be able to filter users by name + Given I have installed Orchard + When I go to "admin/users" +# create user1 + And I follow "Add a new user" + And I fill in + | name | value | + | UserName | user1 | + | Email | user1@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + And I hit "Save" + And I am redirected +# create user2 + And I follow "Add a new user" + And I fill in + | name | value | + | UserName | user2 | + | Email | user2@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + And I hit "Save" + And I am redirected + Then I should see "]*>user1" + And I should see "]*>user2" + When I fill in + | name | value | + | Options.Search | user1 | + And I hit "Filter" + Then I should see "]*>user1" + And I should not see "]*>admin" + And I should not see "]*>user2" + When I fill in + | name | value | + | Options.Search | user1@domain.com | + And I hit "Filter" + Then I should see "]*>user1" + And I should not see "]*>admin" + And I should not see "]*>user2" + When I fill in + | name | value | + | Options.Search | @domain.com | + And I hit "Filter" + Then I should see "]*>user1" + And I should see "]*>user2" + And I should not see "]*>admin" + +@filtering +Scenario: I should be able to filter users by status + Given I have installed Orchard + When I go to "admin/users" +# create user1 + And I follow "Add a new user" + And I fill in + | name | value | + | UserName | user1 | + | Email | user1@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + And I hit "Save" + And I am redirected +# create user2 + And I follow "Add a new user" + And I fill in + | name | value | + | UserName | user2 | + | Email | user2@domain.com | + | Password | a12345! | + | ConfirmPassword | a12345! | + And I hit "Save" + And I am redirected + Then I should see "]*>user1" + And I should see "]*>user2" + When I fill in + | name | value | + | Options.Search | user1 | + And I hit "Filter" + Then I should see "]*>user1" + When I follow "Disable" + And I am redirected + Then I should see "User user1 disabled" + When I fill in + | name | value | + | Options.Filter | Pending | + And I hit "Filter" + Then I should see "]*>user1" + And I should not see "]*>user2" + And I should not see "]*>admin" + When I fill in + | name | value | + | Options.Filter | EmailPending | + And I hit "Filter" + Then I should not see "]*>user1" + And I should not see "]*>user2" + And I should not see "]*>admin" + When I fill in + | name | value | + | Options.Filter | Approved | + And I hit "Filter" + Then I should not see "]*>user1" + And I should see "]*>user2" + And I should see "]*>admin" + When I fill in + | name | value | + | Options.Filter | All | + And I hit "Filter" + Then I should see "]*>user1" + And I should see "]*>user2" + And I should see "]*>admin" diff --git a/src/Orchard.Specs/Users.feature.cs b/src/Orchard.Specs/Users.feature.cs new file mode 100644 index 000000000..8555b6616 --- /dev/null +++ b/src/Orchard.Specs/Users.feature.cs @@ -0,0 +1,715 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by SpecFlow (http://www.specflow.org/). +// SpecFlow Version:1.5.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. +// +// ------------------------------------------------------------------------------ +#region Designer generated code +namespace Orchard.Specs +{ + using TechTalk.SpecFlow; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.5.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [NUnit.Framework.TestFixtureAttribute()] + [NUnit.Framework.DescriptionAttribute("Users")] + public partial class UsersFeature + { + + private static TechTalk.SpecFlow.ITestRunner testRunner; + +#line 1 "Users.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"), "Users", "In order to prevent users module regressions\nAs a site owner\nI want to create, se" + + "arch and modify user accounts", 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("There is only one user by default")] + [NUnit.Framework.CategoryAttribute("management")] + public virtual void ThereIsOnlyOneUserByDefault() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("There is only one user by default", new string[] { + "management"}); +#line 7 +this.ScenarioSetup(scenarioInfo); +#line 8 + testRunner.Given("I have installed Orchard"); +#line 9 + testRunner.When("I go to \"admin/users\""); +#line 10 + testRunner.Then("I should see \"Manage Users\""); +#line 11 + testRunner.And("I should see \"]*>admin\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("I can create a new user")] + [NUnit.Framework.CategoryAttribute("management")] + public virtual void ICanCreateANewUser() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create a new user", new string[] { + "management"}); +#line 14 +this.ScenarioSetup(scenarioInfo); +#line 15 + testRunner.Given("I have installed Orchard"); +#line 16 + testRunner.When("I go to \"admin/users\""); +#line 17 + testRunner.Then("I should see \"Manage Users\""); +#line 18 + testRunner.When("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table1.AddRow(new string[] { + "UserName", + "user1"}); + table1.AddRow(new string[] { + "Email", + "user1@domain.com"}); + table1.AddRow(new string[] { + "Password", + "a12345!"}); + table1.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); +#line 19 + testRunner.And("I fill in", ((string)(null)), table1); +#line 25 + testRunner.And("I hit \"Save\""); +#line 26 + testRunner.And("I am redirected"); +#line 27 + testRunner.Then("I should see \"User created\""); +#line 28 + testRunner.When("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table2.AddRow(new string[] { + "UserName", + "user2"}); + table2.AddRow(new string[] { + "Email", + "user2@domain.com"}); + table2.AddRow(new string[] { + "Password", + "a12345!"}); + table2.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); + table2.AddRow(new string[] { + "UserRoles.Roles[0].Granted", + "true"}); +#line 29 + testRunner.And("I fill in", ((string)(null)), table2); +#line 36 + testRunner.And("I hit \"Save\""); +#line 37 + testRunner.And("I am redirected"); +#line 38 + testRunner.Then("I should see \"User created\""); +#line 39 + testRunner.And("I should see \"Adding role Administrator to user user2\""); +#line 40 + testRunner.When("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table3.AddRow(new string[] { + "UserName", + "user3"}); + table3.AddRow(new string[] { + "Email", + "user3@domain.com"}); + table3.AddRow(new string[] { + "Password", + "a12345!"}); + table3.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); + table3.AddRow(new string[] { + "UserRoles.Roles[0].Granted", + "true"}); + table3.AddRow(new string[] { + "UserRoles.Roles[1].Granted", + "true"}); + table3.AddRow(new string[] { + "UserRoles.Roles[2].Granted", + "true"}); + table3.AddRow(new string[] { + "UserRoles.Roles[3].Granted", + "true"}); + table3.AddRow(new string[] { + "UserRoles.Roles[4].Granted", + "true"}); +#line 41 + testRunner.And("I fill in", ((string)(null)), table3); +#line 52 + testRunner.And("I hit \"Save\""); +#line 53 + testRunner.And("I am redirected"); +#line 54 + testRunner.Then("I should see \"User created\""); +#line 55 + testRunner.And("I should see \"Adding role Administrator to user user3\""); +#line 56 + testRunner.And("I should see \"Adding role Editor to user user3\""); +#line 57 + testRunner.And("I should see \"Adding role Moderator to user user3\""); +#line 58 + testRunner.And("I should see \"Adding role Author to user user3\""); +#line 59 + testRunner.And("I should see \"Adding role Contributor to user user3\""); +#line 60 + testRunner.When("I follow \"Add a new user\""); +#line 61 + testRunner.And("I hit \"Save\""); +#line 62 + testRunner.Then("I should see \"The UserName field is required.\""); +#line 63 + testRunner.Then("I should see \"The Email field is required.\""); +#line 64 + testRunner.Then("I should see \"The Password field is required.\""); +#line 65 + testRunner.Then("I should see \"The ConfirmPassword field is required.\""); +#line 66 + testRunner.When("I go to \"admin/users\""); +#line 67 + testRunner.And("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table4.AddRow(new string[] { + "UserName", + "user4"}); + table4.AddRow(new string[] { + "Email", + "user4@domain.com"}); + table4.AddRow(new string[] { + "Password", + "a12345!"}); + table4.AddRow(new string[] { + "ConfirmPassword", + "a123456!"}); +#line 68 + testRunner.And("I fill in", ((string)(null)), table4); +#line 74 + testRunner.And("I hit \"Save\""); +#line 75 + testRunner.Then("I should see \"Password confirmation must match\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("I can edit an existing user")] + [NUnit.Framework.CategoryAttribute("management")] + public virtual void ICanEditAnExistingUser() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can edit an existing user", new string[] { + "management"}); +#line 78 +this.ScenarioSetup(scenarioInfo); +#line 79 + testRunner.Given("I have installed Orchard"); +#line 80 + testRunner.When("I go to \"admin/users\""); +#line 81 + testRunner.And("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table5.AddRow(new string[] { + "UserName", + "user1"}); + table5.AddRow(new string[] { + "Email", + "user1@domain.com"}); + table5.AddRow(new string[] { + "Password", + "a12345!"}); + table5.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); +#line 82 + testRunner.And("I fill in", ((string)(null)), table5); +#line 88 + testRunner.And("I hit \"Save\""); +#line 89 + testRunner.And("I am redirected"); +#line 90 + testRunner.Then("I should see \"User created\""); +#line hidden + TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table6.AddRow(new string[] { + "Options.Search", + "user1"}); +#line 91 + testRunner.When("I fill in", ((string)(null)), table6); +#line 94 + testRunner.And("I hit \"Filter\""); +#line 95 + testRunner.Then("I should see \"]*>user1\""); +#line 96 + testRunner.When("I follow \"Edit\""); +#line hidden + TechTalk.SpecFlow.Table table7 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table7.AddRow(new string[] { + "UserName", + "user2"}); + table7.AddRow(new string[] { + "Email", + "user2@domain.com"}); +#line 97 + testRunner.And("I fill in", ((string)(null)), table7); +#line 101 + testRunner.And("I hit \"Save\""); +#line 102 + testRunner.And("I am redirected"); +#line 103 + testRunner.Then("I should see \"User information updated\""); +#line 104 + testRunner.And("I should see \"]*>user2\""); +#line 105 + testRunner.And("I should see \"user2@domain.com\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("I should not be able to reuse an existing username or email")] + [NUnit.Framework.CategoryAttribute("management")] + public virtual void IShouldNotBeAbleToReuseAnExistingUsernameOrEmail() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I should not be able to reuse an existing username or email", new string[] { + "management"}); +#line 108 +this.ScenarioSetup(scenarioInfo); +#line 109 + testRunner.Given("I have installed Orchard"); +#line 110 + testRunner.When("I go to \"admin/users\""); +#line 112 + testRunner.And("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table8 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table8.AddRow(new string[] { + "UserName", + "user1"}); + table8.AddRow(new string[] { + "Email", + "user1@domain.com"}); + table8.AddRow(new string[] { + "Password", + "a12345!"}); + table8.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); +#line 113 + testRunner.And("I fill in", ((string)(null)), table8); +#line 119 + testRunner.And("I hit \"Save\""); +#line 120 + testRunner.And("I am redirected"); +#line 122 + testRunner.And("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table9 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table9.AddRow(new string[] { + "UserName", + "user2"}); + table9.AddRow(new string[] { + "Email", + "user2@domain.com"}); + table9.AddRow(new string[] { + "Password", + "a12345!"}); + table9.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); +#line 123 + testRunner.And("I fill in", ((string)(null)), table9); +#line 129 + testRunner.And("I hit \"Save\""); +#line 130 + testRunner.And("I am redirected"); +#line 131 + testRunner.Then("I should see \"]*>user1\""); +#line 132 + testRunner.And("I should see \"]*>user2\""); +#line hidden + TechTalk.SpecFlow.Table table10 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table10.AddRow(new string[] { + "Options.Search", + "user1"}); +#line 134 + testRunner.When("I fill in", ((string)(null)), table10); +#line 137 + testRunner.And("I hit \"Filter\""); +#line 138 + testRunner.Then("I should see \"]*>user1\""); +#line 139 + testRunner.When("I follow \"Edit\""); +#line hidden + TechTalk.SpecFlow.Table table11 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table11.AddRow(new string[] { + "UserName", + "user2"}); + table11.AddRow(new string[] { + "Email", + "user1@domain.com"}); +#line 140 + testRunner.And("I fill in", ((string)(null)), table11); +#line 144 + testRunner.And("I hit \"Save\""); +#line 145 + testRunner.Then("I should see \"User with that username and/or email already exists.\""); +#line hidden + TechTalk.SpecFlow.Table table12 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table12.AddRow(new string[] { + "UserName", + "user1"}); + table12.AddRow(new string[] { + "Email", + "user2@domain.com"}); +#line 146 + testRunner.When("I fill in", ((string)(null)), table12); +#line 150 + testRunner.And("I hit \"Save\""); +#line 151 + testRunner.Then("I should see \"User with that username and/or email already exists.\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("I should not be able to filter users by name")] + [NUnit.Framework.CategoryAttribute("filtering")] + public virtual void IShouldNotBeAbleToFilterUsersByName() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I should not be able to filter users by name", new string[] { + "filtering"}); +#line 154 +this.ScenarioSetup(scenarioInfo); +#line 155 + testRunner.Given("I have installed Orchard"); +#line 156 + testRunner.When("I go to \"admin/users\""); +#line 158 + testRunner.And("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table13 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table13.AddRow(new string[] { + "UserName", + "user1"}); + table13.AddRow(new string[] { + "Email", + "user1@domain.com"}); + table13.AddRow(new string[] { + "Password", + "a12345!"}); + table13.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); +#line 159 + testRunner.And("I fill in", ((string)(null)), table13); +#line 165 + testRunner.And("I hit \"Save\""); +#line 166 + testRunner.And("I am redirected"); +#line 168 + testRunner.And("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table14 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table14.AddRow(new string[] { + "UserName", + "user2"}); + table14.AddRow(new string[] { + "Email", + "user2@domain.com"}); + table14.AddRow(new string[] { + "Password", + "a12345!"}); + table14.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); +#line 169 + testRunner.And("I fill in", ((string)(null)), table14); +#line 175 + testRunner.And("I hit \"Save\""); +#line 176 + testRunner.And("I am redirected"); +#line 177 + testRunner.Then("I should see \"]*>user1\""); +#line 178 + testRunner.And("I should see \"]*>user2\""); +#line hidden + TechTalk.SpecFlow.Table table15 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table15.AddRow(new string[] { + "Options.Search", + "user1"}); +#line 179 + testRunner.When("I fill in", ((string)(null)), table15); +#line 182 + testRunner.And("I hit \"Filter\""); +#line 183 + testRunner.Then("I should see \"]*>user1\""); +#line 184 + testRunner.And("I should not see \"]*>admin\""); +#line 185 + testRunner.And("I should not see \"]*>user2\""); +#line hidden + TechTalk.SpecFlow.Table table16 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table16.AddRow(new string[] { + "Options.Search", + "user1@domain.com"}); +#line 186 + testRunner.When("I fill in", ((string)(null)), table16); +#line 189 + testRunner.And("I hit \"Filter\""); +#line 190 + testRunner.Then("I should see \"]*>user1\""); +#line 191 + testRunner.And("I should not see \"]*>admin\""); +#line 192 + testRunner.And("I should not see \"]*>user2\""); +#line hidden + TechTalk.SpecFlow.Table table17 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table17.AddRow(new string[] { + "Options.Search", + "@domain.com"}); +#line 193 + testRunner.When("I fill in", ((string)(null)), table17); +#line 196 + testRunner.And("I hit \"Filter\""); +#line 197 + testRunner.Then("I should see \"]*>user1\""); +#line 198 + testRunner.And("I should see \"]*>user2\""); +#line 199 + testRunner.And("I should not see \"]*>admin\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("I should be able to filter users by status")] + [NUnit.Framework.CategoryAttribute("filtering")] + public virtual void IShouldBeAbleToFilterUsersByStatus() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I should be able to filter users by status", new string[] { + "filtering"}); +#line 202 +this.ScenarioSetup(scenarioInfo); +#line 203 + testRunner.Given("I have installed Orchard"); +#line 204 + testRunner.When("I go to \"admin/users\""); +#line 206 + testRunner.And("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table18 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table18.AddRow(new string[] { + "UserName", + "user1"}); + table18.AddRow(new string[] { + "Email", + "user1@domain.com"}); + table18.AddRow(new string[] { + "Password", + "a12345!"}); + table18.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); +#line 207 + testRunner.And("I fill in", ((string)(null)), table18); +#line 213 + testRunner.And("I hit \"Save\""); +#line 214 + testRunner.And("I am redirected"); +#line 216 + testRunner.And("I follow \"Add a new user\""); +#line hidden + TechTalk.SpecFlow.Table table19 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table19.AddRow(new string[] { + "UserName", + "user2"}); + table19.AddRow(new string[] { + "Email", + "user2@domain.com"}); + table19.AddRow(new string[] { + "Password", + "a12345!"}); + table19.AddRow(new string[] { + "ConfirmPassword", + "a12345!"}); +#line 217 + testRunner.And("I fill in", ((string)(null)), table19); +#line 223 + testRunner.And("I hit \"Save\""); +#line 224 + testRunner.And("I am redirected"); +#line 225 + testRunner.Then("I should see \"]*>user1\""); +#line 226 + testRunner.And("I should see \"]*>user2\""); +#line hidden + TechTalk.SpecFlow.Table table20 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table20.AddRow(new string[] { + "Options.Search", + "user1"}); +#line 227 + testRunner.When("I fill in", ((string)(null)), table20); +#line 230 + testRunner.And("I hit \"Filter\""); +#line 231 + testRunner.Then("I should see \"]*>user1\""); +#line 232 + testRunner.When("I follow \"Disable\""); +#line 233 + testRunner.And("I am redirected"); +#line 234 + testRunner.Then("I should see \"User user1 disabled\""); +#line hidden + TechTalk.SpecFlow.Table table21 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table21.AddRow(new string[] { + "Options.Filter", + "Pending"}); +#line 235 + testRunner.When("I fill in", ((string)(null)), table21); +#line 238 + testRunner.And("I hit \"Filter\""); +#line 239 + testRunner.Then("I should see \"]*>user1\""); +#line 240 + testRunner.And("I should not see \"]*>user2\""); +#line 241 + testRunner.And("I should not see \"]*>admin\""); +#line hidden + TechTalk.SpecFlow.Table table22 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table22.AddRow(new string[] { + "Options.Filter", + "EmailPending"}); +#line 242 + testRunner.When("I fill in", ((string)(null)), table22); +#line 245 + testRunner.And("I hit \"Filter\""); +#line 246 + testRunner.Then("I should not see \"]*>user1\""); +#line 247 + testRunner.And("I should not see \"]*>user2\""); +#line 248 + testRunner.And("I should not see \"]*>admin\""); +#line hidden + TechTalk.SpecFlow.Table table23 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table23.AddRow(new string[] { + "Options.Filter", + "Approved"}); +#line 249 + testRunner.When("I fill in", ((string)(null)), table23); +#line 252 + testRunner.And("I hit \"Filter\""); +#line 253 + testRunner.Then("I should not see \"]*>user1\""); +#line 254 + testRunner.And("I should see \"]*>user2\""); +#line 255 + testRunner.And("I should see \"]*>admin\""); +#line hidden + TechTalk.SpecFlow.Table table24 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table24.AddRow(new string[] { + "Options.Filter", + "All"}); +#line 256 + testRunner.When("I fill in", ((string)(null)), table24); +#line 259 + testRunner.And("I hit \"Filter\""); +#line 260 + testRunner.Then("I should see \"]*>user1\""); +#line 261 + testRunner.And("I should see \"]*>user2\""); +#line 262 + testRunner.And("I should see \"]*>admin\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + } +} +#endregion diff --git a/src/Orchard.Tests.Modules/Indexing/LuceneIndexProviderTests.cs b/src/Orchard.Tests.Modules/Indexing/LuceneIndexProviderTests.cs index 41a43c632..2d69c727e 100644 --- a/src/Orchard.Tests.Modules/Indexing/LuceneIndexProviderTests.cs +++ b/src/Orchard.Tests.Modules/Indexing/LuceneIndexProviderTests.cs @@ -119,14 +119,23 @@ namespace Orchard.Tests.Modules.Indexing { [Test] public void PropertiesShouldNotBeLost() { _provider.CreateIndex("default"); - _provider.Store("default", _provider.New(42).Add("prop1", "value1").Store()); + _provider.Store("default", _provider.New(42) + .Add("prop1", "value1").Store() + .Add("prop2", 123).Store() + .Add("prop3", 123.456).Store() + .Add("prop4", new DateTime(2001,1,1,1,1,1,1)).Store() + .Add("prop5", true).Store() + ); var hit = _provider.CreateSearchBuilder("default").Get(42); Assert.IsNotNull(hit); Assert.That(hit.ContentItemId, Is.EqualTo(42)); Assert.That(hit.GetString("prop1"), Is.EqualTo("value1")); - + Assert.That(hit.GetInt("prop2"), Is.EqualTo(123)); + Assert.That(hit.GetDouble("prop3"), Is.EqualTo(123.456)); + Assert.That(hit.GetDateTime("prop4"), Is.EqualTo(new DateTime(2001, 1, 1, 1, 1, 1, 1))); + Assert.That(hit.GetBoolean("prop5"), Is.EqualTo(true)); } [Test] diff --git a/src/Orchard.Tests.Modules/Indexing/LuceneSearchBuilderTests.cs b/src/Orchard.Tests.Modules/Indexing/LuceneSearchBuilderTests.cs index 850e08598..1c198a5fe 100644 --- a/src/Orchard.Tests.Modules/Indexing/LuceneSearchBuilderTests.cs +++ b/src/Orchard.Tests.Modules/Indexing/LuceneSearchBuilderTests.cs @@ -177,6 +177,24 @@ namespace Orchard.Tests.Modules.Indexing { Assert.That(date[1].GetDateTime("date") < date[2].GetDateTime("date"), Is.True); } + [Test] + public void ShouldSortByNumber() { + _provider.CreateIndex("default"); + _provider.Store("default", _provider.New(1).Add("downloads", 111).Store()); + _provider.Store("default", _provider.New(2).Add("downloads", 2222).Store()); + _provider.Store("default", _provider.New(3).Add("downloads", 3).Store()); + + var number = _searchBuilder.SortBy("downloads").Search().ToList(); + Assert.That(number.Count(), Is.EqualTo(3)); + Assert.That(number[0].GetInt("downloads") > number[1].GetInt("downloads"), Is.True); + Assert.That(number[1].GetInt("downloads") > number[2].GetInt("downloads"), Is.True); + + number = _searchBuilder.SortBy("downloads").Ascending().Search().ToList(); + Assert.That(number.Count(), Is.EqualTo(3)); + Assert.That(number[0].GetInt("downloads") < number[1].GetInt("downloads"), Is.True); + Assert.That(number[1].GetInt("downloads") < number[2].GetInt("downloads"), Is.True); + } + [Test] public void ShouldEscapeSpecialChars() { _provider.CreateIndex("default"); diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index 0f42c0a5d..b7db352a3 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -166,7 +166,6 @@ - diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs deleted file mode 100644 index 16daa1b90..000000000 --- a/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using System.Xml.Linq; -using Autofac; -using Moq; -using NUnit.Framework; -using Orchard.Caching; -using Orchard.ContentManagement.MetaData; -using Orchard.ContentManagement.MetaData.Models; -using Orchard.ContentManagement.MetaData.Services; -using Orchard.Core.Settings.Metadata; -using Orchard.Data; -using Orchard.DisplayManagement; -using Orchard.DisplayManagement.Descriptors; -using Orchard.DisplayManagement.Implementation; -using Orchard.Environment; -using Orchard.ContentManagement; -using Orchard.ContentManagement.Handlers; -using Orchard.ContentManagement.Records; -using Orchard.Environment.Extensions; -using Orchard.Localization; -using Orchard.Messaging.Events; -using Orchard.Messaging.Services; -using Orchard.Security; -using Orchard.Security.Permissions; -using Orchard.Security.Providers; -using Orchard.Tests.Stubs; -using Orchard.UI.Notify; -using Orchard.Users.Controllers; -using Orchard.Users.Handlers; -using Orchard.Users.Models; -using Orchard.Users.Services; -using Orchard.Users.ViewModels; -using Orchard.Settings; -using Orchard.Core.Settings.Services; - -namespace Orchard.Tests.Modules.Users.Controllers { - [TestFixture] - public class AdminControllerTests : DatabaseEnabledTestsBase { - private AdminController _controller; - private Mock _authorizer; - - public override void Register(ContainerBuilder builder) { - builder.RegisterType().SingleInstance(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType(typeof(SettingsFormatter)) - .As(typeof(IMapper)) - .As(typeof(IMapper)); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As().InstancePerDependency(); - builder.RegisterType().As(); - builder.RegisterInstance(new Mock().Object); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterInstance(new Mock().Object); - builder.RegisterInstance(new Mock().Object); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled()); - - _authorizer = new Mock(); - builder.RegisterInstance(_authorizer.Object); - } - - protected override IEnumerable DatabaseTypes { - get { - return new[] { typeof(UserPartRecord), - typeof(ContentTypeRecord), - typeof(ContentItemRecord), - typeof(ContentItemVersionRecord), - }; - } - } - - public override void Init() { - base.Init(); - - var manager = _container.Resolve(); - - var userOne = manager.New("User"); - userOne.Record = new UserPartRecord { UserName = "one" }; - manager.Create(userOne.ContentItem); - - var userTwo = manager.New("User"); - userTwo.Record = new UserPartRecord { UserName = "two" }; - manager.Create(userTwo.ContentItem); - - var userThree = manager.New("User"); - userThree.Record = new UserPartRecord { UserName = "three" }; - manager.Create(userThree.ContentItem); - - _controller = _container.Resolve(); - - var mockHttpContext = new Mock(); - _controller.ControllerContext = new ControllerContext( - mockHttpContext.Object, - new RouteData( - new Route("foo", new MvcRouteHandler()), - new MvcRouteHandler()), - _controller); - } - - [Test] - public void IndexShouldReturnRowsForUsers() { - _authorizer.Setup(x => x.Authorize(It.IsAny(), It.IsAny())).Returns(true); - - var controller = _container.Resolve(); - var result = (ViewResult)controller.Index(); - var model = (UsersIndexViewModel)result.ViewData.Model; - - Assert.That(model.Rows, Is.Not.Null); - } - - - [Test] - [Ignore("Needs to instead be a specflow test.")] - public void CreateShouldAddUserAndRedirect() { - _authorizer.Setup(x => x.Authorize(It.IsAny(), It.IsAny())).Returns(true); - - var controller = _container.Resolve(); - ActionResult result = null; // controller.CreatePOST(new UserCreateViewModel { - // UserName = "four", - // Email = "six@example.org", - // Password = "five", - // ConfirmPassword = "five" - //}); - Assert.That(result, Is.TypeOf()); - - var redirect = (RedirectToRouteResult)result; - var id = Convert.ToInt32(redirect.RouteValues["id"]); - var manager = _container.Resolve(); - var user = manager.Get(id).As(); - Assert.That(user.UserName, Is.EqualTo("four")); - } - - [Test] - [Ignore("Needs fixing. Needs to instead be a specflow test.")] - public void EditShouldDisplayUserAndStoreChanges() { - _authorizer.Setup(x => x.Authorize(It.IsAny(), It.IsAny())).Returns(true); - - var repository = _container.Resolve>(); - var id = repository.Get(x => x.UserName == "two").Id; - var result = (ViewResult)_container.Resolve().Edit(id); - var model = (UserEditViewModel)result.ViewData.Model; - //Assert.That(model.UserName, Is.EqualTo("two")); - - var controller = _container.Resolve(); - controller.ValueProvider = Values.From(new { - UserName = "bubba", - Email = "hotep", - }); - var result2 = controller.EditPOST(id); - Assert.That(result2, Is.TypeOf()); - } - } -} diff --git a/src/Orchard.Web/Modules/Lucene/Models/LuceneDocumentIndex.cs b/src/Orchard.Web/Modules/Lucene/Models/LuceneDocumentIndex.cs index d76964f51..9cf9c395e 100644 --- a/src/Orchard.Web/Modules/Lucene/Models/LuceneDocumentIndex.cs +++ b/src/Orchard.Web/Modules/Lucene/Models/LuceneDocumentIndex.cs @@ -15,7 +15,7 @@ namespace Lucene.Models { private string _name; private string _stringValue; private int _intValue; - private float _floatValue; + private double _doubleValue; private bool _analyze; private bool _store; private bool _removeTags; @@ -46,7 +46,7 @@ namespace Lucene.Models { } public IDocumentIndex Add(string name, DateTime value) { - return Add(name, DateTools.DateToString(value, DateTools.Resolution.SECOND)); + return Add(name, DateTools.DateToString(value, DateTools.Resolution.MILLISECOND)); } public IDocumentIndex Add(string name, int value) { @@ -62,10 +62,10 @@ namespace Lucene.Models { return Add(name, value.ToString()); } - public IDocumentIndex Add(string name, float value) { + public IDocumentIndex Add(string name, double value) { PrepareForIndexing(); _name = name; - _floatValue = value; + _doubleValue = value; _typeCode = TypeCode.Single; IsDirty = true; return this; @@ -114,7 +114,7 @@ namespace Lucene.Models { case TypeCode.Single: Fields.Add(new NumericField(_name, _store ? Field.Store.YES : Field.Store.NO, - true).SetFloatValue(_floatValue)); + true).SetDoubleValue(_doubleValue)); break; case TypeCode.Empty: break; diff --git a/src/Orchard.Web/Modules/Lucene/Models/LuceneSearchHit.cs b/src/Orchard.Web/Modules/Lucene/Models/LuceneSearchHit.cs index 2c07a817a..897ed3b5c 100644 --- a/src/Orchard.Web/Modules/Lucene/Models/LuceneSearchHit.cs +++ b/src/Orchard.Web/Modules/Lucene/Models/LuceneSearchHit.cs @@ -20,12 +20,12 @@ namespace Lucene.Models { public int GetInt(string name) { var field = _doc.GetField(name); - return field == null ? 0 : NumericUtils.PrefixCodedToInt(field.StringValue()); + return field == null ? 0 : Int32.Parse(field.StringValue(), CultureInfo.InvariantCulture); } - public float GetFloat(string name) { + public double GetDouble(string name) { var field = _doc.GetField(name); - return field == null ? 0 : float.Parse(field.StringValue(), CultureInfo.InvariantCulture); + return field == null ? 0 : double.Parse(field.StringValue(), CultureInfo.InvariantCulture); } public bool GetBoolean(string name) { diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs index 4e3687d25..e80efac2f 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogAdminController.cs @@ -24,7 +24,7 @@ namespace Orchard.Blogs.Controllers { private readonly IBlogPostService _blogPostService; private readonly IContentManager _contentManager; private readonly ITransactionManager _transactionManager; - private readonly IBlogSlugConstraint _blogSlugConstraint; + private readonly IBlogPathConstraint _blogPathConstraint; private readonly ISiteService _siteService; public BlogAdminController( @@ -33,7 +33,7 @@ namespace Orchard.Blogs.Controllers { IBlogPostService blogPostService, IContentManager contentManager, ITransactionManager transactionManager, - IBlogSlugConstraint blogSlugConstraint, + IBlogPathConstraint blogPathConstraint, ISiteService siteService, IShapeFactory shapeFactory) { Services = services; @@ -41,7 +41,7 @@ namespace Orchard.Blogs.Controllers { _blogPostService = blogPostService; _contentManager = contentManager; _transactionManager = transactionManager; - _blogSlugConstraint = blogSlugConstraint; + _blogPathConstraint = blogPathConstraint; _siteService = siteService; T = NullLocalizer.Instance; Shape = shapeFactory; @@ -81,7 +81,7 @@ namespace Orchard.Blogs.Controllers { } _contentManager.Publish(blog.ContentItem); - _blogSlugConstraint.AddSlug(blog.As().GetEffectiveSlug()); + _blogPathConstraint.AddPath(blog.As().Path); return Redirect(Url.BlogForAdmin(blog)); } @@ -116,7 +116,7 @@ namespace Orchard.Blogs.Controllers { } _contentManager.Publish(blog); - _blogSlugConstraint.AddSlug(blog.As().GetEffectiveSlug()); + _blogPathConstraint.AddPath(blog.As().Path); Services.Notifier.Information(T("Blog information updated")); return Redirect(Url.BlogsForAdmin()); diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogController.cs index a200fa074..6e6dbbb44 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogController.cs @@ -20,7 +20,7 @@ namespace Orchard.Blogs.Controllers { private readonly IOrchardServices _services; private readonly IBlogService _blogService; private readonly IBlogPostService _blogPostService; - private readonly IBlogSlugConstraint _blogSlugConstraint; + private readonly IBlogPathConstraint _blogPathConstraint; private readonly IFeedManager _feedManager; private readonly IWorkContextAccessor _workContextAccessor; private readonly IHomePageProvider _routableHomePageProvider; @@ -30,7 +30,7 @@ namespace Orchard.Blogs.Controllers { IOrchardServices services, IBlogService blogService, IBlogPostService blogPostService, - IBlogSlugConstraint blogSlugConstraint, + IBlogPathConstraint blogPathConstraint, IFeedManager feedManager, IShapeFactory shapeFactory, IWorkContextAccessor workContextAccessor, @@ -39,7 +39,7 @@ namespace Orchard.Blogs.Controllers { _services = services; _blogService = blogService; _blogPostService = blogPostService; - _blogSlugConstraint = blogSlugConstraint; + _blogPathConstraint = blogPathConstraint; _feedManager = feedManager; _workContextAccessor = workContextAccessor; _siteService = siteService; @@ -64,13 +64,13 @@ namespace Orchard.Blogs.Controllers { return View((object)viewModel); } - public ActionResult Item(string blogSlug, PagerParameters pagerParameters) { + public ActionResult Item(string blogPath, PagerParameters pagerParameters) { Pager pager = new Pager(_siteService.GetSiteSettings(), pagerParameters); - var correctedSlug = _blogSlugConstraint.FindSlug(blogSlug); - if (correctedSlug == null) + var correctedPath = _blogPathConstraint.FindPath(blogPath); + if (correctedPath == null) return HttpNotFound(); - var blogPart = _blogService.Get(correctedSlug); + var blogPart = _blogService.Get(correctedPath); if (blogPart == null) return HttpNotFound(); diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostController.cs index ca681e7be..86e399152 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogPostController.cs @@ -36,12 +36,12 @@ namespace Orchard.Blogs.Controllers { public Localizer T { get; set; } //TODO: (erikpo) Should think about moving the slug parameters and get calls and null checks up into a model binder or action filter - public ActionResult Item(string blogSlug, string postSlug) { + public ActionResult Item(string blogPath, string postSlug) { if (!_services.Authorizer.Authorize(StandardPermissions.AccessFrontEnd, T("Couldn't view blog post"))) return new HttpUnauthorizedResult(); //TODO: (erikpo) Move looking up the current blog up into a modelbinder - var blogPart = _blogService.Get(blogSlug); + var blogPart = _blogService.Get(blogPath); if (blogPart == null) return HttpNotFound(); @@ -55,9 +55,9 @@ namespace Orchard.Blogs.Controllers { return View((object)model); } - public ActionResult ListByArchive(string blogSlug, string archiveData) { + public ActionResult ListByArchive(string blogPath, string archiveData) { //TODO: (erikpo) Move looking up the current blog up into a modelbinder - BlogPart blogPart = _blogService.Get(blogSlug); + BlogPart blogPart = _blogService.Get(blogPath); if (blogPart == null) return HttpNotFound(); diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/RemoteBlogPublishingController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/RemoteBlogPublishingController.cs index 225723114..3209438bc 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/RemoteBlogPublishingController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/RemoteBlogPublishingController.cs @@ -21,10 +21,10 @@ namespace Orchard.Blogs.Controllers { protected ILogger Logger { get; set; } - public ActionResult Rsd(string blogSlug) { + public ActionResult Rsd(string blogPath) { Logger.Debug("RSD requested"); - BlogPart blogPart = _blogService.Get(blogSlug); + BlogPart blogPart = _blogService.Get(blogPath); if (blogPart == null) return HttpNotFound(); diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Drivers/BlogArchivesPartDriver.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Drivers/BlogArchivesPartDriver.cs index 0c0406ea7..8a0ef5c4d 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Drivers/BlogArchivesPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Drivers/BlogArchivesPartDriver.cs @@ -1,5 +1,8 @@ -using Orchard.Blogs.Models; +using System.Linq; +using Orchard.Blogs.Models; +using Orchard.Blogs.Routing; using Orchard.Blogs.Services; +using Orchard.Blogs.ViewModels; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; @@ -7,18 +10,22 @@ namespace Orchard.Blogs.Drivers { public class BlogArchivesPartDriver : ContentPartDriver { private readonly IBlogService _blogService; private readonly IBlogPostService _blogPostService; + private readonly IBlogPathConstraint _blogPathConstraint; - public BlogArchivesPartDriver(IBlogService blogService, IBlogPostService blogPostService) { + public BlogArchivesPartDriver( + IBlogService blogService, + IBlogPostService blogPostService, + IBlogPathConstraint blogPathConstraint) { _blogService = blogService; _blogPostService = blogPostService; + _blogPathConstraint = blogPathConstraint; } protected override DriverResult Display(BlogArchivesPart part, string displayType, dynamic shapeHelper) { return ContentShape("Parts_Blogs_BlogArchives", () => { - BlogPart blog = null; - if (!string.IsNullOrWhiteSpace(part.ForBlog)) - blog = _blogService.Get(part.ForBlog); + var path = _blogPathConstraint.FindPath(part.ForBlog); + BlogPart blog = _blogService.Get(path); if (blog == null) return null; @@ -28,12 +35,21 @@ namespace Orchard.Blogs.Drivers { } protected override DriverResult Editor(BlogArchivesPart part, dynamic shapeHelper) { + var viewModel = new BlogArchivesViewModel { + Path = part.ForBlog, + Blogs = _blogService.Get().ToList().OrderBy(b => b.Name) + }; + return ContentShape("Parts_Blogs_BlogArchives_Edit", - () => shapeHelper.EditorTemplate(TemplateName: "Parts.Blogs.BlogArchives", Model: part, Prefix: Prefix)); + () => shapeHelper.EditorTemplate(TemplateName: "Parts.Blogs.BlogArchives", Model: viewModel, Prefix: Prefix)); } protected override DriverResult Editor(BlogArchivesPart part, IUpdateModel updater, dynamic shapeHelper) { - updater.TryUpdateModel(part, Prefix, null, null); + var viewModel = new BlogArchivesViewModel(); + if (updater.TryUpdateModel(viewModel, Prefix, null, null)) { + part.ForBlog = viewModel.Path; + } + return Editor(part, shapeHelper); } } diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Drivers/RecentBlogPostsPartDriver.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Drivers/RecentBlogPostsPartDriver.cs index 198eb908b..b00847f66 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Drivers/RecentBlogPostsPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Drivers/RecentBlogPostsPartDriver.cs @@ -1,7 +1,8 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using Orchard.Blogs.Models; +using Orchard.Blogs.Routing; using Orchard.Blogs.Services; +using Orchard.Blogs.ViewModels; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.Core.Common.Models; @@ -10,33 +11,30 @@ namespace Orchard.Blogs.Drivers { public class RecentBlogPostsPartDriver : ContentPartDriver { private readonly IBlogService _blogService; private readonly IContentManager _contentManager; + private readonly IBlogPathConstraint _blogPathConstraint; - public RecentBlogPostsPartDriver(IBlogService blogService, IContentManager contentManager) { + public RecentBlogPostsPartDriver( + IBlogService blogService, + IContentManager contentManager, + IBlogPathConstraint blogPathConstraint) { _blogService = blogService; _contentManager = contentManager; + _blogPathConstraint = blogPathConstraint; } protected override DriverResult Display(RecentBlogPostsPart part, string displayType, dynamic shapeHelper) { - IEnumerable blogPosts; + var path = _blogPathConstraint.FindPath(part.ForBlog); + BlogPart blog = _blogService.Get(path); - BlogPart blog = null; - if (!string.IsNullOrWhiteSpace(part.ForBlog)) - blog = _blogService.Get(part.ForBlog); + if (blog == null) { + return null; + } - if (blog != null) { - blogPosts = _contentManager.Query(VersionOptions.Published, "BlogPost") - .Join().Where(cr => cr.Container == blog.Record.ContentItemRecord) - .OrderByDescending(cr => cr.CreatedUtc) - .Slice(0, part.Count) - .Select(ci => ci.As()); - } - else { - blogPosts = _contentManager.Query(VersionOptions.Published, "BlogPost") - .Join() - .OrderByDescending(cr => cr.CreatedUtc) - .Slice(0, part.Count) - .Select(ci => ci.As()); - } + var blogPosts =_contentManager.Query(VersionOptions.Published, "BlogPost") + .Join().Where(cr => cr.Container == blog.Record.ContentItemRecord) + .OrderByDescending(cr => cr.CreatedUtc) + .Slice(0, part.Count) + .Select(ci => ci.As()); var list = shapeHelper.List(); list.AddRange(blogPosts.Select(bp => _contentManager.BuildDisplay(bp, "Summary"))); @@ -47,12 +45,23 @@ namespace Orchard.Blogs.Drivers { } protected override DriverResult Editor(RecentBlogPostsPart part, dynamic shapeHelper) { + var viewModel = new RecentBlogPostsViewModel { + Count = part.Count, + Path = part.ForBlog, + Blogs = _blogService.Get().ToList().OrderBy(b => b.Name) + }; + return ContentShape("Parts_Blogs_RecentBlogPosts_Edit", - () => shapeHelper.EditorTemplate(TemplateName: "Parts.Blogs.RecentBlogPosts", Model: part, Prefix: Prefix)); + () => shapeHelper.EditorTemplate(TemplateName: "Parts.Blogs.RecentBlogPosts", Model: viewModel, Prefix: Prefix)); } protected override DriverResult Editor(RecentBlogPostsPart part, IUpdateModel updater, dynamic shapeHelper) { - updater.TryUpdateModel(part, Prefix, null, null); + var viewModel = new RecentBlogPostsViewModel(); + if (updater.TryUpdateModel(viewModel, Prefix, null, null)) { + part.ForBlog = viewModel.Path; + part.Count = viewModel.Count; + } + return Editor(part, shapeHelper); } } diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Extensions/UrlHelperExtensions.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Extensions/UrlHelperExtensions.cs index 31ff8b11e..3180203f2 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Extensions/UrlHelperExtensions.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Extensions/UrlHelperExtensions.cs @@ -16,7 +16,7 @@ namespace Orchard.Blogs.Extensions { } public static string Blog(this UrlHelper urlHelper, BlogPart blogPart) { - return urlHelper.Action("Item", "Blog", new { blogSlug = blogPart.As().Path, area = "Orchard.Blogs" }); + return urlHelper.Action("Item", "Blog", new { blogPath = blogPart.As().Path, area = "Orchard.Blogs" }); } public static string BlogLiveWriterManifest(this UrlHelper urlHelper, BlogPart blogPart) { @@ -24,19 +24,19 @@ namespace Orchard.Blogs.Extensions { } public static string BlogRsd(this UrlHelper urlHelper, BlogPart blogPart) { - return urlHelper.AbsoluteAction(() => urlHelper.Action("Rsd", "RemoteBlogPublishing", new { blogSlug = blogPart.As().Path, area = "Orchard.Blogs" })); + return urlHelper.AbsoluteAction(() => urlHelper.Action("Rsd", "RemoteBlogPublishing", new { blogPath = blogPart.As().Path, area = "Orchard.Blogs" })); } public static string BlogArchiveYear(this UrlHelper urlHelper, BlogPart blogPart, int year) { - return urlHelper.Action("ListByArchive", "BlogPost", new { blogSlug = blogPart.As().Path, archiveData = year.ToString(), area = "Orchard.Blogs" }); + return urlHelper.Action("ListByArchive", "BlogPost", new { blogPath = blogPart.As().Path, archiveData = year.ToString(), 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().Path, archiveData = string.Format("{0}/{1}", year, month), area = "Orchard.Blogs" }); + return urlHelper.Action("ListByArchive", "BlogPost", new { blogPath = blogPart.As().Path, archiveData = string.Format("{0}/{1}", year, month), 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().Path, archiveData = string.Format("{0}/{1}/{2}", year, month, day), area = "Orchard.Blogs" }); + return urlHelper.Action("ListByArchive", "BlogPost", new { blogPath = blogPart.As().Path, archiveData = string.Format("{0}/{1}/{2}", year, month, day), area = "Orchard.Blogs" }); } public static string BlogForAdmin(this UrlHelper urlHelper, BlogPart blogPart) { @@ -60,7 +60,7 @@ namespace Orchard.Blogs.Extensions { } public static string BlogPost(this UrlHelper urlHelper, BlogPostPart blogPostPart) { - return urlHelper.Action("Item", "BlogPost", new { blogSlug = blogPostPart.BlogPart.As().Path, postSlug = blogPostPart.As().GetEffectiveSlug(), area = "Orchard.Blogs" }); + return urlHelper.Action("Item", "BlogPost", new { blogPath = blogPostPart.BlogPart.As().Path, postSlug = blogPostPart.As().GetEffectiveSlug(), area = "Orchard.Blogs" }); } public static string BlogPostEdit(this UrlHelper urlHelper, BlogPostPart blogPostPart) { diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Handlers/BlogPartHandler.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Handlers/BlogPartHandler.cs index d774be87e..a92a28ce0 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Handlers/BlogPartHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Handlers/BlogPartHandler.cs @@ -16,22 +16,22 @@ namespace Orchard.Blogs.Handlers { [UsedImplicitly] public class BlogPartHandler : ContentHandler { private readonly IWorkContextAccessor _workContextAccessor; - private readonly IBlogSlugConstraint _blogSlugConstraint; + private readonly IBlogPathConstraint _blogPathConstraint; private readonly IHomePageProvider _routableHomePageProvider; - public BlogPartHandler(IRepository repository, IWorkContextAccessor workContextAccessor, IEnumerable homePageProviders, IBlogSlugConstraint blogSlugConstraint) { + public BlogPartHandler(IRepository repository, IWorkContextAccessor workContextAccessor, IEnumerable homePageProviders, IBlogPathConstraint blogPathConstraint) { _workContextAccessor = workContextAccessor; - _blogSlugConstraint = blogSlugConstraint; + _blogPathConstraint = blogPathConstraint; _routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name); Filters.Add(StorageFilter.For(repository)); Action publishedHandler = (context, route) => { if (route.Is()) { if (route.ContentItem.Id != 0 && route.PromoteToHomePage) - _blogSlugConstraint.AddSlug(""); + _blogPathConstraint.AddPath(""); } else if (route.ContentItem.Id != 0 && route.PromoteToHomePage) { - _blogSlugConstraint.RemoveSlug(""); + _blogPathConstraint.RemovePath(""); } }; diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj b/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj index f5dbbc399..4fb25b07d 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Orchard.Blogs.csproj @@ -78,10 +78,10 @@ - + - - + + @@ -96,6 +96,8 @@ + + diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Routes.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Routes.cs index 6a8cc037a..aa8db7621 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Routes.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Routes.cs @@ -6,10 +6,10 @@ using Orchard.Mvc.Routes; namespace Orchard.Blogs { public class Routes : IRouteProvider { - private readonly IBlogSlugConstraint _blogSlugConstraint; + private readonly IBlogPathConstraint _blogPathConstraint; - public Routes(IBlogSlugConstraint blogSlugConstraint) { - _blogSlugConstraint = blogSlugConstraint; + public Routes(IBlogPathConstraint blogPathConstraint) { + _blogPathConstraint = blogPathConstraint; } public void GetRoutes(ICollection routes) { @@ -175,14 +175,31 @@ namespace Orchard.Blogs { }, new RouteDescriptor { Route = new Route( - "{blogSlug}/Archive/{*archiveData}", + "Archive/{*archiveData}", + new RouteValueDictionary { + {"blogPath", ""}, + {"area", "Orchard.Blogs"}, + {"controller", "BlogPost"}, + {"action", "ListByArchive"} + }, + new RouteValueDictionary { + {"archiveData", new IsArchiveConstraint()} + }, + new RouteValueDictionary { + {"area", "Orchard.Blogs"} + }, + new MvcRouteHandler()) + }, + new RouteDescriptor { + Route = new Route( + "{blogPath}/Archive/{*archiveData}", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogPost"}, {"action", "ListByArchive"} }, new RouteValueDictionary { - {"blogSlug", _blogSlugConstraint}, + {"blogPath", _blogPathConstraint}, {"archiveData", new IsArchiveConstraint()} }, new RouteValueDictionary { @@ -193,14 +210,14 @@ namespace Orchard.Blogs { new RouteDescriptor { Priority = 11, Route = new Route( - "{blogSlug}/rsd", + "{blogPath}/rsd", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "RemoteBlogPublishing"}, {"action", "Rsd"} }, new RouteValueDictionary { - {"blogSlug", _blogSlugConstraint} + {"blogPath", _blogPathConstraint} }, new RouteValueDictionary { {"area", "Orchard.Blogs"} @@ -210,14 +227,14 @@ namespace Orchard.Blogs { new RouteDescriptor { Priority = 11, Route = new Route( - "{blogSlug}/{postSlug}", + "{blogPath}/{postSlug}", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogPost"}, {"action", "Item"} }, new RouteValueDictionary { - {"blogSlug", _blogSlugConstraint} + {"blogPath", _blogPathConstraint} }, new RouteValueDictionary { {"area", "Orchard.Blogs"} @@ -227,15 +244,15 @@ namespace Orchard.Blogs { new RouteDescriptor { Priority = 11, Route = new Route( - "{blogSlug}", + "{blogPath}", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "Blog"}, {"action", "Item"}, - {"blogSlug", ""} + {"blogPath", ""} }, new RouteValueDictionary { - {"blogSlug", _blogSlugConstraint} + {"blogPath", _blogPathConstraint} }, new RouteValueDictionary { {"area", "Orchard.Blogs"} diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogSlugConstraint.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogPathConstraint.cs similarity index 54% rename from src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogSlugConstraint.cs rename to src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogPathConstraint.cs index 922d70b9d..5c1fbec23 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogSlugConstraint.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogPathConstraint.cs @@ -8,46 +8,55 @@ using Orchard.Logging; namespace Orchard.Blogs.Routing { [UsedImplicitly] - public class BlogSlugConstraint : IBlogSlugConstraint { + public class BlogPathConstraint : IBlogPathConstraint { /// /// Singleton object, per Orchard Shell instance. We need to protect concurrent access to the dictionary. /// private readonly object _syncLock = new object(); - private IDictionary _slugs = new Dictionary(); + private IDictionary _paths = new Dictionary(); - public BlogSlugConstraint() { + public BlogPathConstraint() { Logger = NullLogger.Instance; } public ILogger Logger { get; set; } - public void SetSlugs(IEnumerable slugs) { + public void SetPaths(IEnumerable paths) { // Make a copy to avoid performing potential lazy computation inside the lock - var slugsArray = slugs.ToArray(); + var pathArray = paths.ToArray(); lock (_syncLock) { - _slugs = slugsArray.Distinct(StringComparer.OrdinalIgnoreCase).ToDictionary(value => value, StringComparer.OrdinalIgnoreCase); + _paths = pathArray.Distinct(StringComparer.OrdinalIgnoreCase).ToDictionary(value => value, StringComparer.OrdinalIgnoreCase); } - Logger.Debug("Blog slugs: {0}", string.Join(", ", slugsArray)); + Logger.Debug("Blog paths: {0}", string.Join(", ", pathArray)); } - public string FindSlug(string slug) { + public string FindPath(string path) { lock (_syncLock) { string actual; - return _slugs.TryGetValue(slug, out actual) ? actual : slug; + // path can be null for homepage + path = path ?? ""; + + return _paths.TryGetValue(path, out actual) ? actual : path; } } - public void AddSlug(string slug) { + public void AddPath(string path) { lock (_syncLock) { - _slugs[slug] = slug; + // path can be null for homepage + path = path ?? ""; + + _paths[path] = path; } } - public void RemoveSlug(string slug) { + public void RemovePath(string path) { lock (_syncLock) { - _slugs.Remove(slug); + // path can be null for homepage + path = path ?? ""; + + _paths.Remove(path); } } @@ -60,7 +69,7 @@ namespace Orchard.Blogs.Routing { var parameterValue = Convert.ToString(value); lock (_syncLock) { - return _slugs.ContainsKey(parameterValue); + return _paths.ContainsKey(parameterValue); } } diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogSlugConstraintUpdator.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogPathConstraintUpdator.cs similarity index 60% rename from src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogSlugConstraintUpdator.cs rename to src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogPathConstraintUpdator.cs index b3a5e83f8..92e3a6e13 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogSlugConstraintUpdator.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Routing/BlogPathConstraintUpdator.cs @@ -8,12 +8,12 @@ using Orchard.Tasks; namespace Orchard.Blogs.Routing { [UsedImplicitly] - public class BlogSlugConstraintUpdator : IOrchardShellEvents, IBackgroundTask { - private readonly IBlogSlugConstraint _blogSlugConstraint; + public class BlogPathConstraintUpdator : IOrchardShellEvents, IBackgroundTask { + private readonly IBlogPathConstraint _blogPathConstraint; private readonly IBlogService _blogService; - public BlogSlugConstraintUpdator(IBlogSlugConstraint blogSlugConstraint, IBlogService blogService) { - _blogSlugConstraint = blogSlugConstraint; + public BlogPathConstraintUpdator(IBlogPathConstraint blogPathConstraint, IBlogService blogService) { + _blogPathConstraint = blogPathConstraint; _blogService = blogService; } @@ -29,7 +29,7 @@ namespace Orchard.Blogs.Routing { } private void Refresh() { - _blogSlugConstraint.SetSlugs(_blogService.Get().Select(b => b.As().Path)); + _blogPathConstraint.SetPaths(_blogService.Get().Select(b => b.As().Slug)); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Routing/IBlogPathConstraint.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Routing/IBlogPathConstraint.cs new file mode 100644 index 000000000..a3742cb29 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Routing/IBlogPathConstraint.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Web.Routing; + +namespace Orchard.Blogs.Routing { + public interface IBlogPathConstraint : IRouteConstraint, ISingletonDependency { + void SetPaths(IEnumerable paths); + string FindPath(string path); + void AddPath(string path); + void RemovePath(string path); + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Routing/IBlogSlugConstraint.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Routing/IBlogSlugConstraint.cs deleted file mode 100644 index 5c47fad3a..000000000 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Routing/IBlogSlugConstraint.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Web.Routing; - -namespace Orchard.Blogs.Routing { - public interface IBlogSlugConstraint : IRouteConstraint, ISingletonDependency { - void SetSlugs(IEnumerable slugs); - string FindSlug(string slug); - void AddSlug(string slug); - void RemoveSlug(string slug); - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Services/BlogPostService.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Services/BlogPostService.cs index 9f1af8756..8484591a5 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Services/BlogPostService.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Services/BlogPostService.cs @@ -29,9 +29,9 @@ namespace Orchard.Blogs.Services { } public BlogPostPart Get(BlogPart blogPart, string slug, VersionOptions versionOptions) { - var postSlug = blogPart.As().GetChildPath(slug); + var postPath = blogPart.As().GetChildPath(slug); return - _contentManager.Query(versionOptions, "BlogPost").Join().Where(rr => rr.Path == postSlug). + _contentManager.Query(versionOptions, "BlogPost").Join().Where(rr => rr.Path == postPath). Join().Where(cr => cr.Container == blogPart.Record.ContentItemRecord).List(). SingleOrDefault().As(); } diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Services/BlogService.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Services/BlogService.cs index c95182385..ee3853c2d 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Services/BlogService.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Services/BlogService.cs @@ -11,11 +11,11 @@ namespace Orchard.Blogs.Services { [UsedImplicitly] public class BlogService : IBlogService { private readonly IContentManager _contentManager; - private readonly IBlogSlugConstraint _blogSlugConstraint; + private readonly IBlogPathConstraint _blogPathConstraint; - public BlogService(IContentManager contentManager, IBlogSlugConstraint blogSlugConstraint) { + public BlogService(IContentManager contentManager, IBlogPathConstraint blogPathConstraint) { _contentManager = contentManager; - _blogSlugConstraint = blogSlugConstraint; + _blogPathConstraint = blogPathConstraint; } public BlogPart Get(string path) { @@ -39,9 +39,15 @@ namespace Orchard.Blogs.Services { .List(); } + public BlogPart GetFromSlug(string slug) { + return _contentManager.Query() + .Join().Where(rr => rr.Slug == slug) + .List().FirstOrDefault(); + } + public void Delete(ContentItem blog) { _contentManager.Remove(blog); - _blogSlugConstraint.RemoveSlug(blog.As().Path); + _blogPathConstraint.RemovePath(blog.As().Path); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/ViewModels/BlogArchivesViewModel.cs b/src/Orchard.Web/Modules/Orchard.Blogs/ViewModels/BlogArchivesViewModel.cs new file mode 100644 index 000000000..cd04428fe --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Blogs/ViewModels/BlogArchivesViewModel.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using Orchard.Blogs.Models; + +namespace Orchard.Blogs.ViewModels { + public class BlogArchivesViewModel { + public string Path { get; set; } + public IEnumerable Blogs { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/ViewModels/RecentBlogPostsViewModel.cs b/src/Orchard.Web/Modules/Orchard.Blogs/ViewModels/RecentBlogPostsViewModel.cs new file mode 100644 index 000000000..d576554ab --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Blogs/ViewModels/RecentBlogPostsViewModel.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Orchard.Blogs.Models; + +namespace Orchard.Blogs.ViewModels { + public class RecentBlogPostsViewModel { + public int Count { get; set; } + public string Path { get; set; } + public IEnumerable Blogs { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Views/EditorTemplates/Parts.Blogs.BlogArchives.cshtml b/src/Orchard.Web/Modules/Orchard.Blogs/Views/EditorTemplates/Parts.Blogs.BlogArchives.cshtml index 81ed5d01d..64204dc39 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Views/EditorTemplates/Parts.Blogs.BlogArchives.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Views/EditorTemplates/Parts.Blogs.BlogArchives.cshtml @@ -1,8 +1,17 @@ -@model Orchard.Blogs.Models.BlogArchivesPart +@model Orchard.Blogs.ViewModels.BlogArchivesViewModel + +@using Orchard.Blogs.Models; +@using Orchard.Core.Routable.Models; +@using Orchard.ContentManagement; +
- @Html.LabelFor(m => m.ForBlog, T("For Blog")) - @Html.TextBoxFor(m => m.ForBlog) - @T("Show the archives for which blog? Note: specify the blog's slug.") + @Html.LabelFor(m => m.Slug, T("For Blog")) + + @T("Select which blog you want to display the archives for")
\ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Views/EditorTemplates/Parts.Blogs.RecentBlogPosts.cshtml b/src/Orchard.Web/Modules/Orchard.Blogs/Views/EditorTemplates/Parts.Blogs.RecentBlogPosts.cshtml index b0e4bb09a..f54f2e36f 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Views/EditorTemplates/Parts.Blogs.RecentBlogPosts.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Views/EditorTemplates/Parts.Blogs.RecentBlogPosts.cshtml @@ -1,8 +1,18 @@ -@model Orchard.Blogs.Models.RecentBlogPostsPart +@model Orchard.Blogs.ViewModels.RecentBlogPostsViewModel + +@using Orchard.Blogs.Models; +@using Orchard.Core.Routable.Models; +@using Orchard.ContentManagement; +
- @Html.LabelFor(m => m.ForBlog, T("For Blog")) - @Html.TextBoxFor(m => m.ForBlog) + @Html.LabelFor(m => m.Path, T("For Blog")) + + @T("Select which blog you want to display the recent posts for")
@Html.LabelFor(m => m.Count, T("Count")) diff --git a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs index 9da562383..9cff17ea0 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs @@ -1,6 +1,10 @@ +using System.Collections.Generic; using System.Linq; using System.Web.Mvc; +using System.Web.Routing; using Orchard.ContentManagement; +using Orchard.Core.Common.Models; +using Orchard.Core.Contents.Controllers; using Orchard.Core.Settings.Models; using Orchard.DisplayManagement; using Orchard.Localization; @@ -12,6 +16,7 @@ using Orchard.Users.ViewModels; using Orchard.Mvc.Extensions; using System; using Orchard.Settings; +using Orchard.UI.Navigation; namespace Orchard.Users.Controllers { [ValidateInput(false)] @@ -39,24 +44,107 @@ namespace Orchard.Users.Controllers { public IOrchardServices Services { get; set; } public Localizer T { get; set; } - public ActionResult Index() { + public ActionResult Index(UserIndexOptions options, PagerParameters pagerParameters) { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to list users"))) return new HttpUnauthorizedResult(); + var pager = new Pager(_siteService.GetSiteSettings(), pagerParameters); + + // default options + if (options == null) + options = new UserIndexOptions(); + var users = Services.ContentManager - .Query() - .Where(x => x.UserName != null) - .List(); + .Query(); + + switch (options.Filter) { + case UsersFilter.Approved: + users = users.Where(u => u.RegistrationStatus == UserStatus.Approved); + break; + case UsersFilter.Pending: + users = users.Where(u => u.RegistrationStatus == UserStatus.Pending); + break; + case UsersFilter.EmailPending: + users = users.Where(u => u.EmailStatus == UserStatus.Approved); + break; + } + + if(!String.IsNullOrWhiteSpace(options.Search)) { + users = users.Where(u => u.UserName.Contains(options.Search) || u.Email.Contains(options.Search)); + } + + var pagerShape = Shape.Pager(pager).TotalItemCount(users.Count()); + + switch (options.Order) { + case UsersOrder.Name: + users = users.OrderBy(u => u.UserName); + break; + case UsersOrder.Email: + users = users.OrderBy(u => u.Email); + break; + } + + var results = users + .Slice(pager.GetStartIndex(), pager.PageSize) + .ToList(); var model = new UsersIndexViewModel { - Rows = users - .Select(x => new UsersIndexViewModel.Row { UserPart = x }) - .ToList() + Users = results + .Select(x => new UserEntry { User = x.Record }) + .ToList(), + Options = options, + Pager = pagerShape }; + // maintain previous route data when generating page links + var routeData = new RouteData(); + routeData.Values.Add("Options.Filter", options.Filter); + routeData.Values.Add("Options.Search", options.Search); + routeData.Values.Add("Options.Order", options.Order); + + pagerShape.RouteData(routeData); + return View(model); } + [HttpPost] + [FormValueRequired("submit.BulkEdit")] + public ActionResult Index(FormCollection input) { + if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage users"))) + return new HttpUnauthorizedResult(); + + var viewModel = new UsersIndexViewModel {Users = new List(), Options = new UserIndexOptions()}; + UpdateModel(viewModel); + + var checkedEntries = viewModel.Users.Where(c => c.IsChecked); + switch (viewModel.Options.BulkAction) { + case UsersBulkAction.None: + break; + case UsersBulkAction.Approve: + foreach (var entry in checkedEntries) { + Approve(entry.User.Id); + } + break; + case UsersBulkAction.Disable: + foreach (var entry in checkedEntries) { + Moderate(entry.User.Id); + } + break; + case UsersBulkAction.ChallengeEmail: + foreach (var entry in checkedEntries) { + SendChallengeEmail(entry.User.Id); + } + break; + case UsersBulkAction.Delete: + foreach (var entry in checkedEntries) { + Delete(entry.User.Id); + } + break; + } + + return Index(viewModel.Options, new PagerParameters()); + } + public ActionResult Create() { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage users"))) return new HttpUnauthorizedResult(); @@ -109,7 +197,7 @@ namespace Orchard.Users.Controllers { } Services.Notifier.Information(T("User created")); - return RedirectToAction("edit", new { user.Id }); + return RedirectToAction("Index"); } public ActionResult Edit(int id) { @@ -163,7 +251,7 @@ namespace Orchard.Users.Controllers { } Services.Notifier.Information(T("User information updated")); - return RedirectToAction("Edit", new { id }); + return RedirectToAction("Index"); } public ActionResult Delete(int id) { @@ -181,7 +269,7 @@ namespace Orchard.Users.Controllers { } else{ Services.ContentManager.Remove(user.ContentItem); - Services.Notifier.Information(T("User deleted")); + Services.Notifier.Information(T("User {0} deleted", user.UserName)); } } @@ -192,13 +280,13 @@ namespace Orchard.Users.Controllers { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage users"))) return new HttpUnauthorizedResult(); - var user = Services.ContentManager.Get(id); + var user = Services.ContentManager.Get(id); if ( user != null ) { _userService.SendChallengeEmail(user.As(), nonce => Url.AbsoluteAction(() => Url.Action("ChallengeEmail", "Account", new {Area = "Orchard.Users", nonce = nonce}))); + Services.Notifier.Information(T("Challenge email sent to {0}", user.UserName)); } - Services.Notifier.Information(T("Challenge email sent")); return RedirectToAction("Index"); } @@ -207,11 +295,11 @@ namespace Orchard.Users.Controllers { if (!Services.Authorizer.Authorize(StandardPermissions.SiteOwner, T("Not authorized to manage users"))) return new HttpUnauthorizedResult(); - var user = Services.ContentManager.Get(id); + var user = Services.ContentManager.Get(id); if ( user != null ) { user.As().RegistrationStatus = UserStatus.Approved; - Services.Notifier.Information(T("User approved")); + Services.Notifier.Information(T("User {0} approved", user.UserName)); } return RedirectToAction("Index"); diff --git a/src/Orchard.Web/Modules/Orchard.Users/ViewModels/UsersIndexViewModel.cs b/src/Orchard.Web/Modules/Orchard.Users/ViewModels/UsersIndexViewModel.cs index a77c65bee..051ee9da3 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/ViewModels/UsersIndexViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/ViewModels/UsersIndexViewModel.cs @@ -4,10 +4,40 @@ using Orchard.Users.Models; namespace Orchard.Users.ViewModels { public class UsersIndexViewModel { - public class Row { - public UserPart UserPart { get; set; } - } + public IList Users { get; set; } + public UserIndexOptions Options { get; set; } + public dynamic Pager { get; set; } + } - public IList Rows { get; set; } + public class UserEntry { + public UserPartRecord User { get; set; } + public bool IsChecked { get; set; } + } + + public class UserIndexOptions { + public string Search { get; set; } + public UsersOrder Order { get; set; } + public UsersFilter Filter { get; set; } + public UsersBulkAction BulkAction { get; set; } + } + + public enum UsersOrder { + Name, + Email + } + + public enum UsersFilter { + All, + Approved, + Pending, + EmailPending + } + + public enum UsersBulkAction { + None, + Delete, + Disable, + Approve, + ChallengeEmail } } diff --git a/src/Orchard.Web/Modules/Orchard.Users/Views/Admin/Index.cshtml b/src/Orchard.Web/Modules/Orchard.Users/Views/Admin/Index.cshtml index 684d319a4..245c6f713 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Views/Admin/Index.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Users/Views/Admin/Index.cshtml @@ -1,52 +1,101 @@ @model Orchard.Users.ViewModels.UsersIndexViewModel @using Orchard.Users.Models; +@using Orchard.Users.ViewModels; + +@{ + var userIndex = 0; + + var pageSizes = new List() { 10, 50, 100 }; + var defaultPageSize = WorkContext.CurrentSite.PageSize; + if(!pageSizes.Contains(defaultPageSize)) { + pageSizes.Add(defaultPageSize); + } +}

@Html.TitleForPage(T("Manage Users").ToString())

@using (Html.BeginFormAntiForgeryPost()) { @Html.ValidationSummary()
@Html.ActionLink(T("Add a new user").ToString(), "Create", new { }, new { @class = "button primaryAction" })
-
+ +
+ + + +
+
+ @Html.TextBoxFor(m => m.Options.Search, new { @class = "text"}) + + + + + + + + +
+
- - - - - + - + - @foreach (var row in Model.Rows) { - + @foreach (var entry in Model.Users) { + + - } + userIndex++; + }
 ↓ @T("Name") @T("Email")@T("") @T("Actions")
- @if(row.UserPart.RegistrationStatus == UserStatus.Approved && row.UserPart.EmailStatus == UserStatus.Approved) { + + + + @if(entry.User.RegistrationStatus == UserStatus.Approved && entry.User.EmailStatus == UserStatus.Approved) { @T( } else { - @T( + @T( } - @Html.ActionLink(row.UserPart.UserName, "Edit", new { row.UserPart.Id }) + @Html.ActionLink(entry.User.UserName, "Edit", new { entry.User.Id }) - @row.UserPart.Email + @entry.User.Email - @Html.ActionLink(T("Edit").ToString(), "Edit", new { row.UserPart.Id }) | - @Html.ActionLink(T("Remove").ToString(), "Delete", new { row.UserPart.Id }) | - @if(row.UserPart.RegistrationStatus == UserStatus.Pending) { - @Html.ActionLink(T("Approve").ToString(), "Approve", new { row.UserPart.Id }) + @Html.ActionLink(T("Edit").ToString(), "Edit", new { entry.User.Id }) | + @Html.ActionLink(T("Remove").ToString(), "Delete", new { entry.User.Id }) | + @if(entry.User.RegistrationStatus == UserStatus.Pending) { + @Html.ActionLink(T("Approve").ToString(), "Approve", new { entry.User.Id }) } else { - @Html.ActionLink(T("Disable").ToString(), "Moderate", new { row.UserPart.Id }) + @Html.ActionLink(T("Disable").ToString(), "Moderate", new { entry.User.Id }) } - @if ( row.UserPart.EmailStatus == UserStatus.Pending ) { | - @Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { row.UserPart.Id }) + @if ( entry.User.EmailStatus == UserStatus.Pending ) { | + @Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id }) }
+ @Display(Model.Pager)
} \ No newline at end of file diff --git a/src/Orchard.Web/Themes/TheAdmin/Views/Pager.cshtml b/src/Orchard.Web/Themes/TheAdmin/Views/Pager.cshtml index b8cb600b1..1816bdd30 100644 --- a/src/Orchard.Web/Themes/TheAdmin/Views/Pager.cshtml +++ b/src/Orchard.Web/Themes/TheAdmin/Views/Pager.cshtml @@ -1,4 +1,5 @@ @{ + var window = 7; // number of simultaneously displayed pages var nextText = HasText(Model.NextText) ? Model.NextText : T(">").Text; var previousText = HasText(Model.PreviousText) ? Model.PreviousText : T("<").Text; @@ -18,13 +19,36 @@ } var totalPageCount = (int) Math.Ceiling((double) Model.TotalItemCount / Model.PageSize); + var firstPage = Math.Max(1, (int)Model.Page - (window/2)); + var lastPage = Math.Min(totalPageCount, (int)Model.Page + (window/2)); Model.Classes.Add("pager"); Model.Classes.Add("group"); var tag = Tag(Model, "ul"); + + if(Model.RouteData != null) { + foreach(var rd in Model.RouteData.Values) { + routeData[rd.Key] = rd.Value; + } + } } + +@if (Model.TotalItemCount > 1) { + @T("Showing items {0} - {1} of {2}", (Model.Page-1)*(int)Model.PageSize + 1, Model.PageSize == 0 ? Model.TotalItemCount : Math.Min(Model.TotalItemCount, (Model.Page)*(int)Model.PageSize), Model.TotalItemCount) +} + @if (totalPageCount > 1) { + routeData["pageSize"] = Model.PageSize; @tag.StartElement + // first + if(firstPage > 1) { + if (routeData.ContainsKey("page")) { + routeData.Remove("page"); + } +
  • + @Html.ActionLink(T("<<").Text, (string)routeData["action"], (string)routeData["controller"], routeData, null) +
  • + } // previous page if(Model.Page > 1) { if (Model.Page == 2 && routeData.ContainsKey("page")) { @@ -38,7 +62,7 @@ } // page numbers - for (var p = 1; p <= totalPageCount; p++) { + for (var p = firstPage; p <= lastPage; p++) {
  • @if (p == Model.Page) { @p @@ -61,5 +85,12 @@ @Html.ActionLink((string)nextText, (string)routeData["action"], (string)routeData["controller"], routeData, null)
  • } + // last page + if(lastPage < totalPageCount) { + routeData["page"] = totalPageCount; +
  • + @Html.ActionLink(T(">>").Text, (string)routeData["action"], (string)routeData["controller"], routeData, null) +
  • + } @tag.EndElement } \ No newline at end of file diff --git a/src/Orchard/Indexing/IIndexDocument.cs b/src/Orchard/Indexing/IIndexDocument.cs index 56061a633..035653bdc 100644 --- a/src/Orchard/Indexing/IIndexDocument.cs +++ b/src/Orchard/Indexing/IIndexDocument.cs @@ -10,7 +10,7 @@ namespace Orchard.Indexing { IDocumentIndex Add(string name, DateTime value); IDocumentIndex Add(string name, int value); IDocumentIndex Add(string name, bool value); - IDocumentIndex Add(string name, float value); + IDocumentIndex Add(string name, double value); /// /// Stores the original value to the index. diff --git a/src/Orchard/Indexing/ISearchHit.cs b/src/Orchard/Indexing/ISearchHit.cs index 4555a1f8b..13da4a161 100644 --- a/src/Orchard/Indexing/ISearchHit.cs +++ b/src/Orchard/Indexing/ISearchHit.cs @@ -5,7 +5,7 @@ namespace Orchard.Indexing { float Score { get; } int GetInt(string name); - float GetFloat(string name); + double GetDouble(string name); bool GetBoolean(string name); string GetString(string name); DateTime GetDateTime(string name);