diff --git a/src/Orchard.Specs/ContentTypes.feature b/src/Orchard.Specs/ContentTypes.feature
new file mode 100644
index 000000000..cb994d201
--- /dev/null
+++ b/src/Orchard.Specs/ContentTypes.feature
@@ -0,0 +1,84 @@
+Feature: Content Types
+ In order to add new types to my site
+ As an adminitrator
+ I want to create create content types
+
+Scenario: I can create a new content type
+ Given I have installed Orchard
+ And I have installed "Orchard.ContentTypes"
+ When I go to "Admin/ContentTypes"
+ Then I should see "]*>.*?Create new type"
+ When I go to "Admin/ContentTypes/Create"
+ And I fill in
+ | name | value |
+ | DisplayName | Event |
+ | Name | Event |
+ And I hit "Create"
+ And I go to "Admin/ContentTypes/"
+ Then I should see "Event"
+
+Scenario: I can't create a content type with an already existing name
+ Given I have installed Orchard
+ And I have installed "Orchard.ContentTypes"
+ When I go to "Admin/ContentTypes/Create"
+ And I fill in
+ | name | value |
+ | DisplayName | Event |
+ | Name | Event |
+ And I hit "Create"
+ And I go to "Admin/ContentTypes/"
+ Then I should see "Event"
+ When I go to "Admin/ContentTypes/Create"
+ And I fill in
+ | name | value |
+ | DisplayName | Event |
+ | Name | Event-2 |
+ And I hit "Create"
+ Then I should see "
]*>.*?New Content Type.*?
"
+ And I should see "validation-summary-errors"
+
+Scenario: I can't create a content type with an already existing technical name
+ Given I have installed Orchard
+ And I have installed "Orchard.ContentTypes"
+ When I go to "Admin/ContentTypes/Create"
+ And I fill in
+ | name | value |
+ | DisplayName | Dinner |
+ | Name | Dinner |
+ And I hit "Create"
+ And I go to "Admin/ContentTypes/"
+ Then I should see "Dinner"
+ When I go to "Admin/ContentTypes/Create"
+ And I fill in
+ | name | value |
+ | DisplayName | Dinner2 |
+ | Name | Dinner |
+ And I hit "Create"
+ Then I should see "
]*>.*?New Content Type.*?
"
+ And I should see "validation-summary-errors"
+
+Scenario: I can't rename a content type with an already existing name
+ Given I have installed Orchard
+ And I have installed "Orchard.ContentTypes"
+ When I go to "Admin/ContentTypes/Create"
+ And I fill in
+ | name | value |
+ | DisplayName | Dinner |
+ | Name | Dinner |
+ And I hit "Create"
+ And I go to "Admin/ContentTypes/"
+ Then I should see "Dinner"
+ When I go to "Admin/ContentTypes/Create"
+ And I fill in
+ | name | value |
+ | DisplayName | Event |
+ | Name | Event |
+ And I hit "Create"
+ And I go to "Admin/ContentTypes/"
+ Then I should see "Event"
+ When I go to "Admin/ContentTypes/Edit/Dinner"
+ And I fill in
+ | name | value |
+ | DisplayName | Event |
+ And I hit "Save"
+ Then I should see "validation-summary-errors"
diff --git a/src/Orchard.Specs/ContentTypes.feature.cs b/src/Orchard.Specs/ContentTypes.feature.cs
new file mode 100644
index 000000000..1d5dc2ee1
--- /dev/null
+++ b/src/Orchard.Specs/ContentTypes.feature.cs
@@ -0,0 +1,276 @@
+// ------------------------------------------------------------------------------
+//
+// This code was generated by SpecFlow (http://www.specflow.org/).
+// SpecFlow Version:1.3.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.3.0.0")]
+ [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [NUnit.Framework.TestFixtureAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Content Types")]
+ public partial class ContentTypesFeature
+ {
+
+ private static TechTalk.SpecFlow.ITestRunner testRunner;
+
+#line 1 "ContentTypes.feature"
+#line hidden
+
+ [NUnit.Framework.TestFixtureSetUpAttribute()]
+ public virtual void FeatureSetup()
+ {
+ testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
+ TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Content Types", "In order to add new types to my site\r\nAs an adminitrator\r\nI want to create create" +
+ " content types", ((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("I can create a new content type")]
+ public virtual void ICanCreateANewContentType()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can create a new content type", ((string[])(null)));
+#line 6
+this.ScenarioSetup(scenarioInfo);
+#line 7
+testRunner.Given("I have installed Orchard");
+#line 8
+testRunner.And("I have installed \"Orchard.ContentTypes\"");
+#line 9
+testRunner.When("I go to \"Admin/ContentTypes\"");
+#line 10
+testRunner.Then("I should see \"]*>.*?Create new type\"");
+#line 11
+testRunner.When("I go to \"Admin/ContentTypes/Create\"");
+#line hidden
+ TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] {
+ "name",
+ "value"});
+ table1.AddRow(new string[] {
+ "DisplayName",
+ "Event"});
+ table1.AddRow(new string[] {
+ "Name",
+ "Event"});
+#line 12
+testRunner.And("I fill in", ((string)(null)), table1);
+#line 16
+testRunner.And("I hit \"Create\"");
+#line 17
+testRunner.And("I go to \"Admin/ContentTypes/\"");
+#line 18
+testRunner.Then("I should see \"Event\"");
+#line hidden
+ testRunner.CollectScenarioErrors();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("I can\'t create a content type with an already existing name")]
+ public virtual void ICanTCreateAContentTypeWithAnAlreadyExistingName()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can\'t create a content type with an already existing name", ((string[])(null)));
+#line 20
+this.ScenarioSetup(scenarioInfo);
+#line 21
+testRunner.Given("I have installed Orchard");
+#line 22
+testRunner.And("I have installed \"Orchard.ContentTypes\"");
+#line 23
+testRunner.When("I go to \"Admin/ContentTypes/Create\"");
+#line hidden
+ TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] {
+ "name",
+ "value"});
+ table2.AddRow(new string[] {
+ "DisplayName",
+ "Event"});
+ table2.AddRow(new string[] {
+ "Name",
+ "Event"});
+#line 24
+testRunner.And("I fill in", ((string)(null)), table2);
+#line 28
+testRunner.And("I hit \"Create\"");
+#line 29
+testRunner.And("I go to \"Admin/ContentTypes/\"");
+#line 30
+testRunner.Then("I should see \"Event\"");
+#line 31
+testRunner.When("I go to \"Admin/ContentTypes/Create\"");
+#line hidden
+ TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] {
+ "name",
+ "value"});
+ table3.AddRow(new string[] {
+ "DisplayName",
+ "Event"});
+ table3.AddRow(new string[] {
+ "Name",
+ "Event-2"});
+#line 32
+testRunner.And("I fill in", ((string)(null)), table3);
+#line 36
+testRunner.And("I hit \"Create\"");
+#line 37
+testRunner.Then("I should see \"
]*>.*?New Content Type.*?
\"");
+#line 38
+testRunner.And("I should see \"validation-summary-errors\"");
+#line hidden
+ testRunner.CollectScenarioErrors();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("I can\'t create a content type with an already existing technical name")]
+ public virtual void ICanTCreateAContentTypeWithAnAlreadyExistingTechnicalName()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can\'t create a content type with an already existing technical name", ((string[])(null)));
+#line 40
+this.ScenarioSetup(scenarioInfo);
+#line 41
+testRunner.Given("I have installed Orchard");
+#line 42
+testRunner.And("I have installed \"Orchard.ContentTypes\"");
+#line 43
+testRunner.When("I go to \"Admin/ContentTypes/Create\"");
+#line hidden
+ TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] {
+ "name",
+ "value"});
+ table4.AddRow(new string[] {
+ "DisplayName",
+ "Dinner"});
+ table4.AddRow(new string[] {
+ "Name",
+ "Dinner"});
+#line 44
+testRunner.And("I fill in", ((string)(null)), table4);
+#line 48
+testRunner.And("I hit \"Create\"");
+#line 49
+testRunner.And("I go to \"Admin/ContentTypes/\"");
+#line 50
+testRunner.Then("I should see \"Dinner\"");
+#line 51
+testRunner.When("I go to \"Admin/ContentTypes/Create\"");
+#line hidden
+ TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] {
+ "name",
+ "value"});
+ table5.AddRow(new string[] {
+ "DisplayName",
+ "Dinner2"});
+ table5.AddRow(new string[] {
+ "Name",
+ "Dinner"});
+#line 52
+testRunner.And("I fill in", ((string)(null)), table5);
+#line 56
+testRunner.And("I hit \"Create\"");
+#line 57
+testRunner.Then("I should see \"
]*>.*?New Content Type.*?
\"");
+#line 58
+testRunner.And("I should see \"validation-summary-errors\"");
+#line hidden
+ testRunner.CollectScenarioErrors();
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("I can\'t rename a content type with an already existing name")]
+ public virtual void ICanTRenameAContentTypeWithAnAlreadyExistingName()
+ {
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can\'t rename a content type with an already existing name", ((string[])(null)));
+#line 60
+this.ScenarioSetup(scenarioInfo);
+#line 61
+testRunner.Given("I have installed Orchard");
+#line 62
+testRunner.And("I have installed \"Orchard.ContentTypes\"");
+#line 63
+testRunner.When("I go to \"Admin/ContentTypes/Create\"");
+#line hidden
+ TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] {
+ "name",
+ "value"});
+ table6.AddRow(new string[] {
+ "DisplayName",
+ "Dinner"});
+ table6.AddRow(new string[] {
+ "Name",
+ "Dinner"});
+#line 64
+testRunner.And("I fill in", ((string)(null)), table6);
+#line 68
+testRunner.And("I hit \"Create\"");
+#line 69
+testRunner.And("I go to \"Admin/ContentTypes/\"");
+#line 70
+testRunner.Then("I should see \"Dinner\"");
+#line 71
+testRunner.When("I go to \"Admin/ContentTypes/Create\"");
+#line hidden
+ TechTalk.SpecFlow.Table table7 = new TechTalk.SpecFlow.Table(new string[] {
+ "name",
+ "value"});
+ table7.AddRow(new string[] {
+ "DisplayName",
+ "Event"});
+ table7.AddRow(new string[] {
+ "Name",
+ "Event"});
+#line 72
+testRunner.And("I fill in", ((string)(null)), table7);
+#line 76
+testRunner.And("I hit \"Create\"");
+#line 77
+testRunner.And("I go to \"Admin/ContentTypes/\"");
+#line 78
+testRunner.Then("I should see \"Event\"");
+#line 79
+testRunner.When("I go to \"Admin/ContentTypes/Edit/Dinner\"");
+#line hidden
+ TechTalk.SpecFlow.Table table8 = new TechTalk.SpecFlow.Table(new string[] {
+ "name",
+ "value"});
+ table8.AddRow(new string[] {
+ "DisplayName",
+ "Event"});
+#line 80
+testRunner.And("I fill in", ((string)(null)), table8);
+#line 83
+testRunner.And("I hit \"Save\"");
+#line 84
+testRunner.Then("I should see \"validation-summary-errors\"");
+#line hidden
+ testRunner.CollectScenarioErrors();
+ }
+ }
+}
+#endregion
diff --git a/src/Orchard.Specs/Orchard.Specs.csproj b/src/Orchard.Specs/Orchard.Specs.csproj
index 748a34584..ef1f40764 100644
--- a/src/Orchard.Specs/Orchard.Specs.csproj
+++ b/src/Orchard.Specs/Orchard.Specs.csproj
@@ -142,6 +142,11 @@
TrueTrue
+
+ ContentTypes.feature
+ True
+ True
+ SiteCompilation.feature
@@ -228,6 +233,10 @@
SpecFlowSingleFileGeneratorContentRights.feature.cs
+
+ SpecFlowSingleFileGenerator
+ ContentTypes.feature.cs
+ SpecFlowSingleFileGeneratorSiteCompilation.feature.cs
diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Controllers/AdminController.cs
index f4d408d2e..61cc18316 100644
--- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Controllers/AdminController.cs
+++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Controllers/AdminController.cs
@@ -47,8 +47,12 @@ namespace Orchard.ContentTypes.Controllers {
if(String.IsNullOrWhiteSpace(viewModel.DisplayName)) {
ModelState.AddModelError("DisplayName", T("The Content Type name can't be empty.").ToString());
}
+
+ if ( _contentDefinitionService.GetTypes().Any(t => String.Equals(t.Name.Trim(), viewModel.Name.Trim(), StringComparison.OrdinalIgnoreCase)) ) {
+ ModelState.AddModelError("Name", T("A type with the same technical name already exists.").ToString());
+ }
- if(_contentDefinitionService.GetTypes().Any(t => t.DisplayName == viewModel.DisplayName)) {
+ if ( _contentDefinitionService.GetTypes().Any(t => String.Equals(t.DisplayName.Trim(), viewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase)) ) {
ModelState.AddModelError("DisplayName", T("A type with the same name already exists.").ToString());
}
@@ -57,13 +61,19 @@ namespace Orchard.ContentTypes.Controllers {
return View(viewModel);
}
- var typeViewModel = _contentDefinitionService.AddType(viewModel);
+ var contentTypeDefinition = _contentDefinitionService.AddType(viewModel.Name, viewModel.DisplayName);
+ var typeViewModel = new EditTypeViewModel(contentTypeDefinition);
+
Services.Notifier.Information(T("The \"{0}\" content type has been created.", typeViewModel.DisplayName));
return RedirectToAction("Edit", new { id = typeViewModel.Name });
}
+ public ActionResult ContentTypeName(string displayName) {
+ return Json(_contentDefinitionService.GenerateName(displayName));
+ }
+
public ActionResult Edit(string id) {
if (!Services.Authorizer.Authorize(Permissions.CreateContentTypes, T("Not allowed to edit a content type.")))
return new HttpUnauthorizedResult();
@@ -90,9 +100,18 @@ namespace Orchard.ContentTypes.Controllers {
TryUpdateModel(edited);
typeViewModel.DisplayName = edited.DisplayName;
+ if ( String.IsNullOrWhiteSpace(typeViewModel.DisplayName) ) {
+ ModelState.AddModelError("DisplayName", T("The Content Type name can't be empty.").ToString());
+ }
+
+ if ( _contentDefinitionService.GetTypes().Any(t => String.Equals(t.DisplayName.Trim(), typeViewModel.DisplayName.Trim(), StringComparison.OrdinalIgnoreCase) && !String.Equals(t.Name, id)) ) {
+ ModelState.AddModelError("DisplayName", T("A type with the same name already exists.").ToString());
+ }
+
if (!ModelState.IsValid)
return View(typeViewModel);
+
_contentDefinitionService.AlterType(typeViewModel, this);
if (!ModelState.IsValid) {
diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs
index 9c3d936af..de42386e3 100644
--- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs
+++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs
@@ -65,18 +65,24 @@ namespace Orchard.ContentTypes.Services {
return viewModel;
}
- public EditTypeViewModel AddType(CreateTypeViewModel typeViewModel) {
- var name = GenerateName(typeViewModel.DisplayName);
+ public ContentTypeDefinition AddType(string name, string displayName) {
+ if(String.IsNullOrWhiteSpace(displayName)) {
+ throw new ArgumentException("displayName");
+ }
- while (_contentDefinitionManager.GetTypeDefinition(name) != null)
+ if(String.IsNullOrWhiteSpace(name)) {
+ name = GenerateName(displayName);
+ }
+
+ while ( _contentDefinitionManager.GetTypeDefinition(name) != null )
name = VersionName(name);
- var contentTypeDefinition = new ContentTypeDefinition(name, typeViewModel.DisplayName);
+ var contentTypeDefinition = new ContentTypeDefinition(name, displayName);
_contentDefinitionManager.StoreTypeDefinition(contentTypeDefinition);
_contentDefinitionManager.AlterTypeDefinition(name,
cfg => cfg.Creatable().Draftable());
- return new EditTypeViewModel(contentTypeDefinition);
+ return contentTypeDefinition;
}
public void AlterType(EditTypeViewModel typeViewModel, IUpdateModel updateModel) {
@@ -210,20 +216,21 @@ namespace Orchard.ContentTypes.Services {
}
//gratuitously stolen from the RoutableService
- private static string GenerateName(string displayName) {
- if (string.IsNullOrWhiteSpace(displayName))
- return "";
+ public string GenerateName(string name) {
+ if ( string.IsNullOrWhiteSpace(name) )
+ return String.Empty;
- var name = displayName;
- //todo: might need to be made more restrictive depending on how name is used (like as an XML node name, for instance)
- var dissallowed = new Regex(@"[/:?#\[\]@!$&'()*+,;=\s]+");
+ var dissallowed = new Regex(@"[/:?#\[\]@!$&'()*+,;=\s\""<>]+");
- name = dissallowed.Replace(name, "-");
- name = name.Trim('-');
+ name = dissallowed.Replace(name, String.Empty);
+ name = name.Trim();
if (name.Length > 128)
name = name.Substring(0, 128);
+ while ( _contentDefinitionManager.GetTypeDefinition(name) != null )
+ name = VersionName(name);
+
return name;
}
diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/IContentDefinitionService.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/IContentDefinitionService.cs
index 70c59e4a7..44fc6b8ae 100644
--- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/IContentDefinitionService.cs
+++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/IContentDefinitionService.cs
@@ -1,17 +1,19 @@
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
+using Orchard.ContentManagement.MetaData.Models;
using Orchard.ContentTypes.ViewModels;
namespace Orchard.ContentTypes.Services {
public interface IContentDefinitionService : IDependency {
IEnumerable GetTypes();
EditTypeViewModel GetType(string name);
- EditTypeViewModel AddType(CreateTypeViewModel typeViewModel);
+ ContentTypeDefinition AddType(string name, string displayName);
void AlterType(EditTypeViewModel typeViewModel, IUpdateModel updater);
void RemoveType(string name);
void AddPartToType(string partName, string typeName);
void RemovePartFromType(string partName, string typeName);
+ string GenerateName(string displayName);
IEnumerable GetParts();
EditPartViewModel GetPart(string name);
diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/ViewModels/CreateTypeViewModel.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/ViewModels/CreateTypeViewModel.cs
index 29a57e6e6..a53768699 100644
--- a/src/Orchard.Web/Modules/Orchard.ContentTypes/ViewModels/CreateTypeViewModel.cs
+++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/ViewModels/CreateTypeViewModel.cs
@@ -1,5 +1,6 @@
namespace Orchard.ContentTypes.ViewModels {
public class CreateTypeViewModel {
public string DisplayName { get; set; }
+ public string Name { get; set; }
}
}
diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/Create.cshtml b/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/Create.cshtml
index fe3758232..847f21fa9 100644
--- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/Create.cshtml
+++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/Create.cshtml
@@ -1,10 +1,40 @@
@model Orchard.ContentTypes.ViewModels.CreateTypeViewModel
+
@using (Html.BeginFormAntiForgeryPost()) {
@Html.ValidationSummary()
}
\ No newline at end of file
+ }
+
+@using(Script.Foot()){
+
+}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/Edit.cshtml b/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/Edit.cshtml
index 22520e69c..7e50efb4b 100644
--- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/Edit.cshtml
+++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/Edit.cshtml
@@ -12,7 +12,7 @@
@Html.ValidationSummary()