--HG--
branch : dev
This commit is contained in:
Suha Can
2011-01-31 17:23:30 -08:00
36 changed files with 1491 additions and 342 deletions

View File

@@ -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<HtmlNode>();
.SelectNodes("(//input|//textarea|//select)") ?? Enumerable.Empty<HtmlNode>();
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;
}
}

View File

@@ -196,6 +196,11 @@
<DesignTime>True</DesignTime>
<DependentUpon>SiteCompilation.feature</DependentUpon>
</Compile>
<Compile Include="Users.feature.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Users.feature</DependentUpon>
</Compile>
<Compile Include="UsingSpecFlow.feature.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@@ -296,6 +301,10 @@
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>Pages.feature.cs</LastGenOutput>
</None>
<None Include="Users.feature">
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>Users.feature.cs</LastGenOutput>
</None>
<None Include="WebHosting.feature">
<Generator>SpecFlowSingleFileGenerator</Generator>
<LastGenOutput>WebHosting.feature.cs</LastGenOutput>

View File

@@ -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 "<a[^>]*>admin</a>"
@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 "<a[^>]*>user1</a>"
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 "<a[^>]*>user2</a>"
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 "<a[^>]*>user1</a>"
And I should see "<a[^>]*>user2</a>"
# 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 "<a[^>]*>user1</a>"
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 "<a[^>]*>user1</a>"
And I should see "<a[^>]*>user2</a>"
When I fill in
| name | value |
| Options.Search | user1 |
And I hit "Filter"
Then I should see "<a[^>]*>user1</a>"
And I should not see "<a[^>]*>admin</a>"
And I should not see "<a[^>]*>user2</a>"
When I fill in
| name | value |
| Options.Search | user1@domain.com |
And I hit "Filter"
Then I should see "<a[^>]*>user1</a>"
And I should not see "<a[^>]*>admin</a>"
And I should not see "<a[^>]*>user2</a>"
When I fill in
| name | value |
| Options.Search | @domain.com |
And I hit "Filter"
Then I should see "<a[^>]*>user1</a>"
And I should see "<a[^>]*>user2</a>"
And I should not see "<a[^>]*>admin</a>"
@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 "<a[^>]*>user1</a>"
And I should see "<a[^>]*>user2</a>"
When I fill in
| name | value |
| Options.Search | user1 |
And I hit "Filter"
Then I should see "<a[^>]*>user1</a>"
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 "<a[^>]*>user1</a>"
And I should not see "<a[^>]*>user2</a>"
And I should not see "<a[^>]*>admin</a>"
When I fill in
| name | value |
| Options.Filter | EmailPending |
And I hit "Filter"
Then I should not see "<a[^>]*>user1</a>"
And I should not see "<a[^>]*>user2</a>"
And I should not see "<a[^>]*>admin</a>"
When I fill in
| name | value |
| Options.Filter | Approved |
And I hit "Filter"
Then I should not see "<a[^>]*>user1</a>"
And I should see "<a[^>]*>user2</a>"
And I should see "<a[^>]*>admin</a>"
When I fill in
| name | value |
| Options.Filter | All |
And I hit "Filter"
Then I should see "<a[^>]*>user1</a>"
And I should see "<a[^>]*>user2</a>"
And I should see "<a[^>]*>admin</a>"

715
src/Orchard.Specs/Users.feature.cs generated Normal file
View File

@@ -0,0 +1,715 @@
// ------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
// ------------------------------------------------------------------------------
#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 \"<a[^>]*>admin</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#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 \"<a[^>]*>user2</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 132
testRunner.And("I should see \"<a[^>]*>user2</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 178
testRunner.And("I should see \"<a[^>]*>user2</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 184
testRunner.And("I should not see \"<a[^>]*>admin</a>\"");
#line 185
testRunner.And("I should not see \"<a[^>]*>user2</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 191
testRunner.And("I should not see \"<a[^>]*>admin</a>\"");
#line 192
testRunner.And("I should not see \"<a[^>]*>user2</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 198
testRunner.And("I should see \"<a[^>]*>user2</a>\"");
#line 199
testRunner.And("I should not see \"<a[^>]*>admin</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 226
testRunner.And("I should see \"<a[^>]*>user2</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 240
testRunner.And("I should not see \"<a[^>]*>user2</a>\"");
#line 241
testRunner.And("I should not see \"<a[^>]*>admin</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 247
testRunner.And("I should not see \"<a[^>]*>user2</a>\"");
#line 248
testRunner.And("I should not see \"<a[^>]*>admin</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 254
testRunner.And("I should see \"<a[^>]*>user2</a>\"");
#line 255
testRunner.And("I should see \"<a[^>]*>admin</a>\"");
#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 \"<a[^>]*>user1</a>\"");
#line 261
testRunner.And("I should see \"<a[^>]*>user2</a>\"");
#line 262
testRunner.And("I should see \"<a[^>]*>admin</a>\"");
#line hidden
testRunner.CollectScenarioErrors();
}
}
}
#endregion

View File

@@ -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]

View File

@@ -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");

View File

@@ -166,7 +166,6 @@
<Compile Include="Users\Services\UserServiceTests.cs" />
<Compile Include="Users\ShellSettingsUtility.cs" />
<Compile Include="Values.cs" />
<Compile Include="Users\Controllers\AdminControllerTests.cs" />
<Compile Include="Users\Services\MembershipServiceTests.cs" />
<Compile Include="Widgets\RuleEngine\UrlRuleProviderTest.cs" />
<Compile Include="Widgets\Services\WidgetsServiceTest.cs" />

View File

@@ -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<IAuthorizer> _authorizer;
public override void Register(ContainerBuilder builder) {
builder.RegisterType<AdminController>().SingleInstance();
builder.RegisterType<SiteService>().As<ISiteService>();
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<DefaultContentManager>().As<IContentManager>();
builder.RegisterType(typeof(SettingsFormatter))
.As(typeof(IMapper<XElement, SettingsDictionary>))
.As(typeof(IMapper<SettingsDictionary, XElement>));
builder.RegisterType<ContentDefinitionManager>().As<IContentDefinitionManager>();
builder.RegisterType<DefaultContentManagerSession>().As<IContentManagerSession>();
builder.RegisterType<DefaultContentQuery>().As<IContentQuery>().InstancePerDependency();
builder.RegisterType<DefaultMessageManager>().As<IMessageManager>();
builder.RegisterInstance(new Mock<IMessageEventHandler>().Object);
builder.RegisterType<MembershipService>().As<IMembershipService>();
builder.RegisterType<UserService>().As<IUserService>();
builder.RegisterType<UserPartHandler>().As<IContentHandler>();
builder.RegisterType<StubWorkContextAccessor>().As<IWorkContextAccessor>();
builder.RegisterType<OrchardServices>().As<IOrchardServices>();
builder.RegisterType<TransactionManager>().As<ITransactionManager>();
builder.RegisterType<DefaultShapeTableManager>().As<IShapeTableManager>();
builder.RegisterType<DefaultShapeFactory>().As<IShapeFactory>();
builder.RegisterType<StubExtensionManager>().As<IExtensionManager>();
builder.RegisterInstance(new Mock<INotifier>().Object);
builder.RegisterInstance(new Mock<IContentDisplay>().Object);
builder.RegisterType<StubCacheManager>().As<ICacheManager>();
builder.RegisterType<Signals>().As<ISignals>();
builder.RegisterType<DefaultEncryptionService>().As<IEncryptionService>();
builder.RegisterInstance(ShellSettingsUtility.CreateEncryptionEnabled());
_authorizer = new Mock<IAuthorizer>();
builder.RegisterInstance(_authorizer.Object);
}
protected override IEnumerable<Type> DatabaseTypes {
get {
return new[] { typeof(UserPartRecord),
typeof(ContentTypeRecord),
typeof(ContentItemRecord),
typeof(ContentItemVersionRecord),
};
}
}
public override void Init() {
base.Init();
var manager = _container.Resolve<IContentManager>();
var userOne = manager.New<UserPart>("User");
userOne.Record = new UserPartRecord { UserName = "one" };
manager.Create(userOne.ContentItem);
var userTwo = manager.New<UserPart>("User");
userTwo.Record = new UserPartRecord { UserName = "two" };
manager.Create(userTwo.ContentItem);
var userThree = manager.New<UserPart>("User");
userThree.Record = new UserPartRecord { UserName = "three" };
manager.Create(userThree.ContentItem);
_controller = _container.Resolve<AdminController>();
var mockHttpContext = new Mock<HttpContextBase>();
_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<Permission>(), It.IsAny<LocalizedString>())).Returns(true);
var controller = _container.Resolve<AdminController>();
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<Permission>(), It.IsAny<LocalizedString>())).Returns(true);
var controller = _container.Resolve<AdminController>();
ActionResult result = null; // controller.CreatePOST(new UserCreateViewModel {
// UserName = "four",
// Email = "six@example.org",
// Password = "five",
// ConfirmPassword = "five"
//});
Assert.That(result, Is.TypeOf<RedirectToRouteResult>());
var redirect = (RedirectToRouteResult)result;
var id = Convert.ToInt32(redirect.RouteValues["id"]);
var manager = _container.Resolve<IContentManager>();
var user = manager.Get(id).As<IUser>();
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<Permission>(), It.IsAny<LocalizedString>())).Returns(true);
var repository = _container.Resolve<IRepository<UserPartRecord>>();
var id = repository.Get(x => x.UserName == "two").Id;
var result = (ViewResult)_container.Resolve<AdminController>().Edit(id);
var model = (UserEditViewModel)result.ViewData.Model;
//Assert.That(model.UserName, Is.EqualTo("two"));
var controller = _container.Resolve<AdminController>();
controller.ValueProvider = Values.From(new {
UserName = "bubba",
Email = "hotep",
});
var result2 = controller.EditPOST(id);
Assert.That(result2, Is.TypeOf<RedirectToRouteResult>());
}
}
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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<IRoutableAspect>().GetEffectiveSlug());
_blogPathConstraint.AddPath(blog.As<IRoutableAspect>().Path);
return Redirect(Url.BlogForAdmin(blog));
}
@@ -116,7 +116,7 @@ namespace Orchard.Blogs.Controllers {
}
_contentManager.Publish(blog);
_blogSlugConstraint.AddSlug(blog.As<IRoutableAspect>().GetEffectiveSlug());
_blogPathConstraint.AddPath(blog.As<IRoutableAspect>().Path);
Services.Notifier.Information(T("Blog information updated"));
return Redirect(Url.BlogsForAdmin());

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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<BlogArchivesPart> {
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);
}
}

View File

@@ -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<RecentBlogPostsPart> {
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<BlogPostPart> 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<CommonPartRecord>().Where(cr => cr.Container == blog.Record.ContentItemRecord)
.OrderByDescending(cr => cr.CreatedUtc)
.Slice(0, part.Count)
.Select(ci => ci.As<BlogPostPart>());
}
else {
blogPosts = _contentManager.Query(VersionOptions.Published, "BlogPost")
.Join<CommonPartRecord>()
.OrderByDescending(cr => cr.CreatedUtc)
.Slice(0, part.Count)
.Select(ci => ci.As<BlogPostPart>());
}
var blogPosts =_contentManager.Query(VersionOptions.Published, "BlogPost")
.Join<CommonPartRecord>().Where(cr => cr.Container == blog.Record.ContentItemRecord)
.OrderByDescending(cr => cr.CreatedUtc)
.Slice(0, part.Count)
.Select(ci => ci.As<BlogPostPart>());
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);
}
}

View File

@@ -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<IRoutableAspect>().Path, area = "Orchard.Blogs" });
return urlHelper.Action("Item", "Blog", new { blogPath = blogPart.As<IRoutableAspect>().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<IRoutableAspect>().Path, area = "Orchard.Blogs" }));
return urlHelper.AbsoluteAction(() => urlHelper.Action("Rsd", "RemoteBlogPublishing", new { blogPath = blogPart.As<IRoutableAspect>().Path, 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" });
return urlHelper.Action("ListByArchive", "BlogPost", new { blogPath = blogPart.As<IRoutableAspect>().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<IRoutableAspect>().Path, archiveData = string.Format("{0}/{1}", year, month), area = "Orchard.Blogs" });
return urlHelper.Action("ListByArchive", "BlogPost", new { blogPath = blogPart.As<IRoutableAspect>().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<IRoutableAspect>().Path, archiveData = string.Format("{0}/{1}/{2}", year, month, day), area = "Orchard.Blogs" });
return urlHelper.Action("ListByArchive", "BlogPost", new { blogPath = blogPart.As<IRoutableAspect>().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<IRoutableAspect>().Path, postSlug = blogPostPart.As<IRoutableAspect>().GetEffectiveSlug(), area = "Orchard.Blogs" });
return urlHelper.Action("Item", "BlogPost", new { blogPath = blogPostPart.BlogPart.As<IRoutableAspect>().Path, postSlug = blogPostPart.As<IRoutableAspect>().GetEffectiveSlug(), area = "Orchard.Blogs" });
}
public static string BlogPostEdit(this UrlHelper urlHelper, BlogPostPart blogPostPart) {

View File

@@ -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<BlogPartRecord> repository, IWorkContextAccessor workContextAccessor, IEnumerable<IHomePageProvider> homePageProviders, IBlogSlugConstraint blogSlugConstraint) {
public BlogPartHandler(IRepository<BlogPartRecord> repository, IWorkContextAccessor workContextAccessor, IEnumerable<IHomePageProvider> homePageProviders, IBlogPathConstraint blogPathConstraint) {
_workContextAccessor = workContextAccessor;
_blogSlugConstraint = blogSlugConstraint;
_blogPathConstraint = blogPathConstraint;
_routableHomePageProvider = homePageProviders.SingleOrDefault(p => p.GetProviderName() == RoutableHomePageProvider.Name);
Filters.Add(StorageFilter.For(repository));
Action<PublishContentContext, RoutePart> publishedHandler = (context, route) => {
if (route.Is<BlogPart>()) {
if (route.ContentItem.Id != 0 && route.PromoteToHomePage)
_blogSlugConstraint.AddSlug("");
_blogPathConstraint.AddPath("");
}
else if (route.ContentItem.Id != 0 && route.PromoteToHomePage) {
_blogSlugConstraint.RemoveSlug("");
_blogPathConstraint.RemovePath("");
}
};

View File

@@ -78,10 +78,10 @@
<Compile Include="Handlers\BlogPartArchiveHandler.cs" />
<Compile Include="Models\BlogPartArchiveRecord.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Routing\IBlogSlugConstraint.cs" />
<Compile Include="Routing\IBlogPathConstraint.cs" />
<Compile Include="Routing\IsArchiveConstraint.cs" />
<Compile Include="Routing\BlogSlugConstraint.cs" />
<Compile Include="Routing\BlogSlugConstraintUpdator.cs" />
<Compile Include="Routing\BlogPathConstraint.cs" />
<Compile Include="Routing\BlogPathConstraintUpdator.cs" />
<Compile Include="Security\BlogAuthorizationEventHandler.cs" />
<Compile Include="Services\BlogService.cs" />
<Compile Include="Controllers\BlogController.cs" />
@@ -96,6 +96,8 @@
<Compile Include="Services\IBlogPostService.cs" />
<Compile Include="Services\IBlogService.cs" />
<Compile Include="Services\XmlRpcHandler.cs" />
<Compile Include="ViewModels\BlogArchivesViewModel.cs" />
<Compile Include="ViewModels\RecentBlogPostsViewModel.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Content\Admin\images\draft.gif" />

View File

@@ -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<RouteDescriptor> 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"}

View File

@@ -8,46 +8,55 @@ using Orchard.Logging;
namespace Orchard.Blogs.Routing {
[UsedImplicitly]
public class BlogSlugConstraint : IBlogSlugConstraint {
public class BlogPathConstraint : IBlogPathConstraint {
/// <summary>
/// Singleton object, per Orchard Shell instance. We need to protect concurrent access to the dictionary.
/// </summary>
private readonly object _syncLock = new object();
private IDictionary<string, string> _slugs = new Dictionary<string, string>();
private IDictionary<string, string> _paths = new Dictionary<string, string>();
public BlogSlugConstraint() {
public BlogPathConstraint() {
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public void SetSlugs(IEnumerable<string> slugs) {
public void SetPaths(IEnumerable<string> 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);
}
}

View File

@@ -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<IRoutableAspect>().Path));
_blogPathConstraint.SetPaths(_blogService.Get().Select(b => b.As<IRoutableAspect>().Slug));
}
}
}

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Web.Routing;
namespace Orchard.Blogs.Routing {
public interface IBlogPathConstraint : IRouteConstraint, ISingletonDependency {
void SetPaths(IEnumerable<string> paths);
string FindPath(string path);
void AddPath(string path);
void RemovePath(string path);
}
}

View File

@@ -1,11 +0,0 @@
using System.Collections.Generic;
using System.Web.Routing;
namespace Orchard.Blogs.Routing {
public interface IBlogSlugConstraint : IRouteConstraint, ISingletonDependency {
void SetSlugs(IEnumerable<string> slugs);
string FindSlug(string slug);
void AddSlug(string slug);
void RemoveSlug(string slug);
}
}

View File

@@ -29,9 +29,9 @@ namespace Orchard.Blogs.Services {
}
public BlogPostPart Get(BlogPart blogPart, string slug, VersionOptions versionOptions) {
var postSlug = blogPart.As<IRoutableAspect>().GetChildPath(slug);
var postPath = blogPart.As<IRoutableAspect>().GetChildPath(slug);
return
_contentManager.Query(versionOptions, "BlogPost").Join<RoutePartRecord>().Where(rr => rr.Path == postSlug).
_contentManager.Query(versionOptions, "BlogPost").Join<RoutePartRecord>().Where(rr => rr.Path == postPath).
Join<CommonPartRecord>().Where(cr => cr.Container == blogPart.Record.ContentItemRecord).List().
SingleOrDefault().As<BlogPostPart>();
}

View File

@@ -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<BlogPart, BlogPartRecord>()
.Join<RoutePartRecord>().Where(rr => rr.Slug == slug)
.List().FirstOrDefault();
}
public void Delete(ContentItem blog) {
_contentManager.Remove(blog);
_blogSlugConstraint.RemoveSlug(blog.As<IRoutableAspect>().Path);
_blogPathConstraint.RemovePath(blog.As<IRoutableAspect>().Path);
}
}
}

View File

@@ -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<BlogPart> Blogs { get; set; }
}
}

View File

@@ -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<BlogPart> Blogs { get; set; }
}
}

View File

@@ -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;
<fieldset>
<div>
@Html.LabelFor(m => m.ForBlog, T("For Blog"))
@Html.TextBoxFor(m => m.ForBlog)
<span class="hint">@T("Show the archives for which blog? Note: specify the blog's slug.")</span>
@Html.LabelFor(m => m.Slug, T("For Blog"))
<select id="@Html.FieldIdFor(m => m.Slug)" name="@Html.FieldNameFor(m => m.Slug)">
@foreach(BlogPart blog in Model.Blogs) {
@Html.SelectOption(Model.Slug, blog.As<RoutePart>().Slug, blog.Name)
}
</select>
<span class="hint">@T("Select which blog you want to display the archives for")</span>
</div>
</fieldset>

View File

@@ -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;
<fieldset>
<div>
@Html.LabelFor(m => m.ForBlog, T("For Blog"))
@Html.TextBoxFor(m => m.ForBlog)
@Html.LabelFor(m => m.Path, T("For Blog"))
<select id="@Html.FieldIdFor(m => m.Path)" name="@Html.FieldNameFor(m => m.Path)">
@foreach(BlogPart blog in Model.Blogs) {
@Html.SelectOption(Model.Path, blog.As<RoutePart>().Path, blog.Name)
}
</select>
<span class="hint">@T("Select which blog you want to display the recent posts for")</span>
</div>
<div>
@Html.LabelFor(m => m.Count, T("Count"))

View File

@@ -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<UserPart, UserPartRecord>()
.Where(x => x.UserName != null)
.List();
.Query<UserPart, UserPartRecord>();
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<UserEntry>(), 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<IUser>(id);
if ( user != null ) {
_userService.SendChallengeEmail(user.As<UserPart>(), 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<IUser>(id);
if ( user != null ) {
user.As<UserPart>().RegistrationStatus = UserStatus.Approved;
Services.Notifier.Information(T("User approved"));
Services.Notifier.Information(T("User {0} approved", user.UserName));
}
return RedirectToAction("Index");

View File

@@ -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<UserEntry> Users { get; set; }
public UserIndexOptions Options { get; set; }
public dynamic Pager { get; set; }
}
public IList<Row> 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
}
}

View File

@@ -1,52 +1,101 @@
@model Orchard.Users.ViewModels.UsersIndexViewModel
@using Orchard.Users.Models;
@using Orchard.Users.ViewModels;
@{
var userIndex = 0;
var pageSizes = new List<int?>() { 10, 50, 100 };
var defaultPageSize = WorkContext.CurrentSite.PageSize;
if(!pageSizes.Contains(defaultPageSize)) {
pageSizes.Add(defaultPageSize);
}
}
<h1>@Html.TitleForPage(T("Manage Users").ToString()) </h1>
@using (Html.BeginFormAntiForgeryPost()) {
@Html.ValidationSummary()
<div class="manage">@Html.ActionLink(T("Add a new user").ToString(), "Create", new { }, new { @class = "button primaryAction" })</div>
<fieldset class="bulk-actions">
<label for="publishActions">@T("Actions:")</label>
<select id="publishActions" name="@Html.NameOf(m => m.Options.BulkAction)">
@Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.None, T("Choose action...").ToString())
@Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Approve, T("Approve").ToString())
@Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Disable, T("Disable").ToString())
@Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.ChallengeEmail, T("Send challenge E-mail").ToString())
@Html.SelectOption(Model.Options.BulkAction, UsersBulkAction.Delete, T("Remove").ToString())
</select>
<button type="submit" name="submit.BulkEdit" value="@T("Apply")">@T("Apply")</button>
</fieldset>
<fieldset class="bulk-actions">
@Html.TextBoxFor(m => m.Options.Search, new { @class = "text"})
<label for="filterResults">@T("Filter:")</label>
<select id="filterResults" name="@Html.NameOf(m => m.Options.Filter)">
@Html.SelectOption(Model.Options.Filter, UsersFilter.All, T("All Users").ToString())
@Html.SelectOption(Model.Options.Filter, UsersFilter.Approved, T("Approved Users").ToString())
@Html.SelectOption(Model.Options.Filter, UsersFilter.Pending, T("Pending Users").ToString())
@Html.SelectOption(Model.Options.Filter, UsersFilter.EmailPending, T("Pending Emails").ToString())
</select>
<label for="filterResults">@T("Sort by:")</label>
<select id="filterResults" name="@Html.NameOf(m => m.Options.Order)">
@Html.SelectOption(Model.Options.Order, UsersOrder.Name, T("Name").ToString())
@Html.SelectOption(Model.Options.Order, UsersOrder.Email, T("Email").ToString())
</select>
<input type="hidden" name="Page" value="1" />
<label for="pageSize">@T("Show:")</label>
<select id="pageSize" name="PageSize">
@Html.SelectOption((int)Model.Pager.PageSize, 0, T("All").ToString())
@foreach(int size in pageSizes.OrderBy(p => p)) {
@Html.SelectOption((int)Model.Pager.PageSize, size, size.ToString())
}
<select>
<button type="submit" name="submit.Filter" value="@T("Filter")">@T("Filter")</button>
</fieldset>
<fieldset>
<table class="items">
<colgroup>
<col id="Name" />
<col id="Email" />
<col id="Edit" />
</colgroup>
<thead>
<tr>
<th scope="col">&nbsp;&darr;</th>
<th scope="col">@T("Name")</th>
<th scope="col">@T("Email")</th>
<th scope="col">@T("") </th>
<th scope="col">@T("Actions")</th>
</tr>
</thead>
@foreach (var row in Model.Rows) {
<tr>
@foreach (var entry in Model.Users) {
<tr>
<td>
@if(row.UserPart.RegistrationStatus == UserStatus.Approved && row.UserPart.EmailStatus == UserStatus.Approved) {
<input type="hidden" value="@Model.Users[userIndex].User.Id" name="@Html.NameOf(m => m.Users[userIndex].User.Id)"/>
<input type="checkbox" value="true" name="@Html.NameOf(m => m.Users[userIndex].IsChecked)"/>
</td>
<td>
@if(entry.User.RegistrationStatus == UserStatus.Approved && entry.User.EmailStatus == UserStatus.Approved) {
<img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/online.gif") " alt="@T("Approved") " title="@T("User is approved") " />
}
else {
<img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/offline.gif") " alt="@T("Moderated") " title="@if(row.UserPart.EmailStatus == UserStatus.Approved) { @T("User is moderated") } else { @T("E-mail validation is pending") }" />
<img class="icon" src="@Href("~/Modules/Orchard.Users/Content/Admin/images/offline.gif") " alt="@T("Moderated") " title="@if(entry.User.EmailStatus == UserStatus.Approved) { @T("User is moderated") } else { @T("E-mail validation is pending") }" />
}
@Html.ActionLink(row.UserPart.UserName, "Edit", new { row.UserPart.Id })
@Html.ActionLink(entry.User.UserName, "Edit", new { entry.User.Id })
</td>
<td>
@row.UserPart.Email
@entry.User.Email
</td>
<td>
@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 ) { <text>|</text>
@Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { row.UserPart.Id })
@if ( entry.User.EmailStatus == UserStatus.Pending ) { <text>|</text>
@Html.ActionLink(T("Send challenge E-mail").ToString(), "SendChallengeEmail", new { entry.User.Id })
}
</td>
</tr>
}
userIndex++;
}
</table>
@Display(Model.Pager)
</fieldset>
}

View File

@@ -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) {
<span class="page-results">@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)</span>
}
@if (totalPageCount > 1) {
routeData["pageSize"] = Model.PageSize;
@tag.StartElement
// first
if(firstPage > 1) {
if (routeData.ContainsKey("page")) {
routeData.Remove("page");
}
<li class="page-first">
@Html.ActionLink(T("<<").Text, (string)routeData["action"], (string)routeData["controller"], routeData, null)
</li>
}
// previous page
if(Model.Page > 1) {
if (Model.Page == 2 && routeData.ContainsKey("page")) {
@@ -38,7 +62,7 @@
</li>
}
// page numbers
for (var p = 1; p <= totalPageCount; p++) {
for (var p = firstPage; p <= lastPage; p++) {
<li class="page-@p">
@if (p == Model.Page) {
<span>@p</span>
@@ -61,5 +85,12 @@
@Html.ActionLink((string)nextText, (string)routeData["action"], (string)routeData["controller"], routeData, null)
</li>
}
// last page
if(lastPage < totalPageCount) {
routeData["page"] = totalPageCount;
<li class="page-last">
@Html.ActionLink(T(">>").Text, (string)routeData["action"], (string)routeData["controller"], routeData, null)
</li>
}
@tag.EndElement
}

View File

@@ -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);
/// <summary>
/// Stores the original value to the index.

View File

@@ -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);