diff --git a/Orchard.proj b/Orchard.proj index ca84ef19e..9aac282cd 100644 --- a/Orchard.proj +++ b/Orchard.proj @@ -276,13 +276,18 @@ $(StageFolder)\**\Modules\Lucene\**; $(StageFolder)\**\Modules\Orchard.ArchiveLater\**; $(StageFolder)\**\Modules\Orchard.CodeGeneration\**; + $(StageFolder)\**\Modules\Orchard.DesignerTools\**; $(StageFolder)\**\Modules\Orchard.Email\**; $(StageFolder)\**\Modules\Orchard.Experimental\**; + $(StageFolder)\**\Modules\Orchard.ImportExport\**; $(StageFolder)\**\Modules\Orchard.Indexing\**; + $(StageFolder)\**\Modules\Orchard.MediaPicker\**; $(StageFolder)\**\Modules\Orchard.Migrations\**; $(StageFolder)\**\Modules\Orchard.MultiTenancy\**; + $(StageFolder)\**\Modules\Orchard.Recipes\**; $(StageFolder)\**\Modules\Orchard.Scripting.Dlr\**; $(StageFolder)\**\Modules\Orchard.Search\**; + $(StageFolder)\**\Modules\Orchard.Warmup\**; " /> @@ -375,13 +380,18 @@ + + + + + diff --git a/src/Orchard.Specs/Bindings/WebAppHosting.cs b/src/Orchard.Specs/Bindings/WebAppHosting.cs index a261d505c..a2c6d09e2 100644 --- a/src/Orchard.Specs/Bindings/WebAppHosting.cs +++ b/src/Orchard.Specs/Bindings/WebAppHosting.cs @@ -222,6 +222,23 @@ namespace Orchard.Specs.Bindings { WhenIGoTo(urlPath); } + [When(@"I follow ""([^""]+)"" where class name has ""([^""]+)""")] + public void WhenIFollowClass(string linkText, string className) { + var link = _doc.DocumentNode + .SelectNodes("//a[@href]").Where(elt => + (elt.InnerText == linkText || + (elt.Attributes["title"] != null && elt.Attributes["title"].Value == linkText)) && + elt.Attributes["class"].Value.IndexOf(className, StringComparison.OrdinalIgnoreCase) != -1).SingleOrDefault(); + + if (link == null) { + throw new InvalidOperationException(string.Format("Could not find an anchor with matching text '{0}' and class '{1}'. Document: {2}", linkText, className, _doc.DocumentNode.InnerHtml)); + } + var href = link.Attributes["href"].Value; + var urlPath = HttpUtility.HtmlDecode(href); + + WhenIGoTo(urlPath); + } + [When(@"I fill in")] public void WhenIFillIn(Table table) { var inputs = _doc.DocumentNode diff --git a/src/Orchard.Specs/Blogs.feature b/src/Orchard.Specs/Blogs.feature index 6701fd81e..2afe21106 100644 --- a/src/Orchard.Specs/Blogs.feature +++ b/src/Orchard.Specs/Blogs.feature @@ -17,7 +17,7 @@ Scenario: I can create a new blog and blog post And I hit "Save" And I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" And I fill in | name | value | | Routable.Title | My Post | @@ -39,7 +39,7 @@ Scenario: I can create a new blog with multiple blog posts each with the same ti And I hit "Save" And I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" And I fill in | name | value | | Routable.Title | My Post | @@ -50,7 +50,7 @@ Scenario: I can create a new blog with multiple blog posts each with the same ti And I should see "Hi there." When I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" And I fill in | name | value | | Routable.Title | My Post | @@ -61,7 +61,7 @@ Scenario: I can create a new blog with multiple blog posts each with the same ti And I should see "Hi there, again." When I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" And I fill in | name | value | | Routable.Title | My Post | @@ -83,7 +83,7 @@ Scenario: I can create a new blog and blog post and when I change the slug of th Then I should see "]*>.*?My Blog.*?" When I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" And I fill in | name | value | | Routable.Title | My Post | @@ -114,7 +114,7 @@ Scenario: When viewing a blog the user agent is given an RSS feed of the blog's And I hit "Save" And I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" And I fill in | name | value | | Routable.Title | My Post | @@ -151,7 +151,7 @@ Scenario: The virtual path of my installation when not at the root is reflected And I hit "Save" And I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" Then I should see "http\://localhost/OrchardLocal/my-blog/" Scenario: The virtual path of my installation when at the root is reflected in the URL example for the slug field when creating a blog or blog post @@ -164,7 +164,7 @@ Scenario: The virtual path of my installation when at the root is reflected in t And I hit "Save" And I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" Then I should see "http\://localhost/my-blog/" Scenario: I set my blog to be the content for the home page and the posts for the blog be rooted to the app @@ -177,7 +177,7 @@ Scenario: I set my blog to be the content for the home page and the posts for th And I hit "Save" And I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" And I fill in | name | value | | Routable.Title | My Post | diff --git a/src/Orchard.Specs/Blogs.feature.cs b/src/Orchard.Specs/Blogs.feature.cs index 60245a6c4..a8bcbabff 100644 --- a/src/Orchard.Specs/Blogs.feature.cs +++ b/src/Orchard.Specs/Blogs.feature.cs @@ -1,7 +1,7 @@ // ------------------------------------------------------------------------------ // // This code was generated by SpecFlow (http://www.specflow.org/). -// SpecFlow Version:1.4.0.0 +// 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 @@ -14,7 +14,7 @@ namespace Orchard.Specs using TechTalk.SpecFlow; - [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.4.0.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.5.0.0")] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] [NUnit.Framework.DescriptionAttribute("Blog")] @@ -97,7 +97,7 @@ this.ScenarioSetup(scenarioInfo); #line 19 testRunner.And("I follow \"My Blog\""); #line 20 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line hidden TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -157,7 +157,7 @@ this.ScenarioSetup(scenarioInfo); #line 41 testRunner.And("I follow \"My Blog\""); #line 42 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line hidden TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -183,7 +183,7 @@ this.ScenarioSetup(scenarioInfo); #line 52 testRunner.And("I follow \"My Blog\""); #line 53 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line hidden TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -209,7 +209,7 @@ this.ScenarioSetup(scenarioInfo); #line 63 testRunner.And("I follow \"My Blog\""); #line 64 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line hidden TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -270,7 +270,7 @@ this.ScenarioSetup(scenarioInfo); #line 85 testRunner.And("I follow \"My Blog\""); #line 86 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line hidden TechTalk.SpecFlow.Table table8 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -349,7 +349,7 @@ this.ScenarioSetup(scenarioInfo); #line 116 testRunner.And("I follow \"My Blog\""); #line 117 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line hidden TechTalk.SpecFlow.Table table11 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -452,7 +452,7 @@ this.ScenarioSetup(scenarioInfo); #line 153 testRunner.And("I follow \"My Blog\""); #line 154 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line 155 testRunner.Then("I should see \"http\\://localhost/OrchardLocal/my-blog/\""); #line hidden @@ -490,7 +490,7 @@ this.ScenarioSetup(scenarioInfo); #line 166 testRunner.And("I follow \"My Blog\""); #line 167 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line 168 testRunner.Then("I should see \"http\\://localhost/my-blog/\""); #line hidden @@ -529,7 +529,7 @@ this.ScenarioSetup(scenarioInfo); #line 179 testRunner.And("I follow \"My Blog\""); #line 180 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line hidden TechTalk.SpecFlow.Table table16 = new TechTalk.SpecFlow.Table(new string[] { "name", diff --git a/src/Orchard.Specs/Comments.feature b/src/Orchard.Specs/Comments.feature index 8ac0badd4..d48b0ecbd 100644 --- a/src/Orchard.Specs/Comments.feature +++ b/src/Orchard.Specs/Comments.feature @@ -12,7 +12,7 @@ Scenario: HTML markup in any given comment is encoded And I hit "Save" And I go to "admin/blogs" And I follow "My Blog" - And I follow "New Post" + And I follow "New Post" where class name has "primaryAction" And I fill in | name | value | | Routable.Title | My Post | diff --git a/src/Orchard.Specs/Comments.feature.cs b/src/Orchard.Specs/Comments.feature.cs index d7110fc1b..4cb7ad366 100644 --- a/src/Orchard.Specs/Comments.feature.cs +++ b/src/Orchard.Specs/Comments.feature.cs @@ -1,7 +1,7 @@ // ------------------------------------------------------------------------------ // // This code was generated by SpecFlow (http://www.specflow.org/). -// SpecFlow Version:1.4.0.0 +// 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 @@ -14,7 +14,7 @@ namespace Orchard.Specs using TechTalk.SpecFlow; - [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.4.0.0")] + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.5.0.0")] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] [NUnit.Framework.DescriptionAttribute("Comments")] @@ -80,7 +80,7 @@ this.ScenarioSetup(scenarioInfo); #line 14 testRunner.And("I follow \"My Blog\""); #line 15 - testRunner.And("I follow \"New Post\""); + testRunner.And("I follow \"New Post\" where class name has \"primaryAction\""); #line hidden TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { "name", diff --git a/src/Orchard.Specs/Lists.feature b/src/Orchard.Specs/Lists.feature index cac77f999..c4cf4c895 100644 --- a/src/Orchard.Specs/Lists.feature +++ b/src/Orchard.Specs/Lists.feature @@ -1,11 +1,11 @@ -Feature: Lists - In order to add new lists to my site - As an administrator - I want to create lists - -Scenario: I can create a new list - Given I have installed Orchard - When I go to "Admin/Contents/Create/List" +Feature: Lists + In order to add new lists to my site + As an administrator + I want to create lists + +Scenario: I can create a new list + Given I have installed Orchard + When I go to "Admin/Contents/Create/List" And I fill in | name | value | | Routable.Title | MyList | @@ -13,25 +13,25 @@ Scenario: I can create a new list And I go to "Admin/Contents/List/List" Then I should see "MyList" -Scenario: I can add content items to a list - Given I have installed Orchard - And I have a containable content type "MyType" - When I go to "Admin/Contents/Create/List" +Scenario: I can add content items to a list + Given I have installed Orchard + And I have a containable content type "MyType" + When I go to "Admin/Contents/Create/List" And I fill in | name | value | | Routable.Title | MyList | And I hit "Save" And I go to "Admin/Contents/List/List" Then I should see "MyList" - When I follow "Contained Items" - Then I should see "The 'MyList' List has no content items." - When I follow "Create New Content" where href has "ReturnUrl" - Then I should see "MyType" - When I follow "MyType" where href has "ReturnUrl" + When I follow "Contained Items" + Then I should see "The 'MyList' List has no content items." + When I follow "Create New Content" where href has "ReturnUrl" + Then I should see "MyType" + When I follow "MyType" where href has "ReturnUrl" And I fill in | name | value | | Routable.Title | MyContentItem | And I hit "Save" - And I am redirected - Then I should see "Manage Content for MyList" - And I should see "MyContentItem" + And I am redirected + Then I should see "Manage MyList" + And I should see "MyContentItem" diff --git a/src/Orchard.Specs/Lists.feature.cs b/src/Orchard.Specs/Lists.feature.cs index 830812173..1ea82efa6 100644 --- a/src/Orchard.Specs/Lists.feature.cs +++ b/src/Orchard.Specs/Lists.feature.cs @@ -30,7 +30,8 @@ namespace Orchard.Specs public virtual void FeatureSetup() { testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner(); - TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Lists", "In order to add new lists to my site\nAs an administrator\nI want to create lists", GenerationTargetLanguage.CSharp, ((string[])(null))); + TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Lists", "In order to add new lists to my site\r\nAs an administrator\r\nI want to create lists" + + "", GenerationTargetLanguage.CSharp, ((string[])(null))); testRunner.OnFeatureStart(featureInfo); } @@ -60,9 +61,9 @@ namespace Orchard.Specs #line 6 this.ScenarioSetup(scenarioInfo); #line 7 - testRunner.Given("I have installed Orchard"); + testRunner.Given("I have installed Orchard"); #line 8 - testRunner.When("I go to \"Admin/Contents/Create/List\""); + testRunner.When("I go to \"Admin/Contents/Create/List\""); #line hidden TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -90,11 +91,11 @@ this.ScenarioSetup(scenarioInfo); #line 16 this.ScenarioSetup(scenarioInfo); #line 17 - testRunner.Given("I have installed Orchard"); + testRunner.Given("I have installed Orchard"); #line 18 - testRunner.And("I have a containable content type \"MyType\""); + testRunner.And("I have a containable content type \"MyType\""); #line 19 - testRunner.When("I go to \"Admin/Contents/Create/List\""); + testRunner.When("I go to \"Admin/Contents/Create/List\""); #line hidden TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -111,15 +112,15 @@ this.ScenarioSetup(scenarioInfo); #line 25 testRunner.Then("I should see \"MyList\""); #line 26 - testRunner.When("I follow \"Contained Items\""); + testRunner.When("I follow \"Contained Items\""); #line 27 - testRunner.Then("I should see \"The \'MyList\' List has no content items.\""); + testRunner.Then("I should see \"The \'MyList\' List has no content items.\""); #line 28 - testRunner.When("I follow \"Create New Content\" where href has \"ReturnUrl\""); + testRunner.When("I follow \"Create New Content\" where href has \"ReturnUrl\""); #line 29 - testRunner.Then("I should see \"MyType\""); + testRunner.Then("I should see \"MyType\""); #line 30 - testRunner.When("I follow \"MyType\" where href has \"ReturnUrl\""); + testRunner.When("I follow \"MyType\" where href has \"ReturnUrl\""); #line hidden TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -132,11 +133,11 @@ this.ScenarioSetup(scenarioInfo); #line 34 testRunner.And("I hit \"Save\""); #line 35 - testRunner.And("I am redirected"); + testRunner.And("I am redirected"); #line 36 - testRunner.Then("I should see \"Manage Content for MyList\""); + testRunner.Then("I should see \"Manage MyList\""); #line 37 - testRunner.And("I should see \"MyContentItem\""); + testRunner.And("I should see \"MyContentItem\""); #line hidden testRunner.CollectScenarioErrors(); } diff --git a/src/Orchard.Specs/Media.feature b/src/Orchard.Specs/Media.feature index 10d4f695a..1bd33b973 100644 --- a/src/Orchard.Specs/Media.feature +++ b/src/Orchard.Specs/Media.feature @@ -7,7 +7,7 @@ Scenario: Media admin is available Given I have installed Orchard And I have installed "Orchard.Media" When I go to "admin/media" - Then I should see "Manage Media Folders" + Then I should see "Media" And the status should be 200 "OK" Scenario: Creating a folder @@ -19,7 +19,7 @@ Scenario: Creating a folder | Name | Hello World | And I hit "Save" And I am redirected - Then I should see "Manage Media Folders" + Then I should see "Media" And I should see "Hello World" And the status should be 200 "OK" @@ -28,6 +28,6 @@ Scenario: Limited access And I have installed "Orchard.Media" When I go to "admin/media/edit?name=..\..\bin&mediaPath=..\..\bin" And I am redirected - Then I should see "Manage Media Folders" + Then I should see "Media" And I should see "Editing failed: Invalid path" And the status should be 200 "OK" diff --git a/src/Orchard.Specs/Media.feature.cs b/src/Orchard.Specs/Media.feature.cs index d6632b9a0..595abe83c 100644 --- a/src/Orchard.Specs/Media.feature.cs +++ b/src/Orchard.Specs/Media.feature.cs @@ -67,7 +67,7 @@ this.ScenarioSetup(scenarioInfo); #line 9 testRunner.When("I go to \"admin/media\""); #line 10 - testRunner.Then("I should see \"Manage Media Folders\""); + testRunner.Then("I should see \"Media\""); #line 11 testRunner.And("the status should be 200 \"OK\""); #line hidden @@ -101,7 +101,7 @@ this.ScenarioSetup(scenarioInfo); #line 21 testRunner.And("I am redirected"); #line 22 - testRunner.Then("I should see \"Manage Media Folders\""); + testRunner.Then("I should see \"Media\""); #line 23 testRunner.And("I should see \"Hello World\""); #line 24 @@ -126,7 +126,7 @@ this.ScenarioSetup(scenarioInfo); #line 30 testRunner.And("I am redirected"); #line 31 - testRunner.Then("I should see \"Manage Media Folders\""); + testRunner.Then("I should see \"Media\""); #line 32 testRunner.And("I should see \"Editing failed: Invalid path\""); #line 33 diff --git a/src/Orchard.Specs/Users.feature b/src/Orchard.Specs/Users.feature index 2de8d8a5b..a87e49b48 100644 --- a/src/Orchard.Specs/Users.feature +++ b/src/Orchard.Specs/Users.feature @@ -7,14 +7,14 @@ 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" + Then I should see "Users" And I should see "]*>admin" @management Scenario: I can create a new user Given I have installed Orchard When I go to "admin/users" - Then I should see "Manage Users" + Then I should see "Users" When I follow "Add a new user" And I fill in | name | value | diff --git a/src/Orchard.Specs/Users.feature.cs b/src/Orchard.Specs/Users.feature.cs index d6500e267..002561568 100644 --- a/src/Orchard.Specs/Users.feature.cs +++ b/src/Orchard.Specs/Users.feature.cs @@ -2,7 +2,7 @@ // // This code was generated by SpecFlow (http://www.specflow.org/). // SpecFlow Version:1.5.0.0 -// Runtime Version:4.0.30319.225 +// Runtime Version:4.0.30319.1 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -67,7 +67,7 @@ this.ScenarioSetup(scenarioInfo); #line 9 testRunner.When("I go to \"admin/users\""); #line 10 - testRunner.Then("I should see \"Manage Users\""); + testRunner.Then("I should see \"Users\""); #line 11 testRunner.And("I should see \"]*>admin\""); #line hidden @@ -88,7 +88,7 @@ this.ScenarioSetup(scenarioInfo); #line 16 testRunner.When("I go to \"admin/users\""); #line 17 - testRunner.Then("I should see \"Manage Users\""); + testRunner.Then("I should see \"Users\""); #line 18 testRunner.When("I follow \"Add a new user\""); #line hidden diff --git a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs index 22b00929e..eb2a0e57f 100644 --- a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs +++ b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs @@ -1,4 +1,6 @@ using System; +using System.Globalization; +using System.Threading; using System.Xml.Linq; using Autofac; using Moq; @@ -122,5 +124,18 @@ namespace Orchard.Tests.Modules.Users.Services { Assert.That(username, Is.EqualTo("foo")); Assert.That(validateByUtc, Is.GreaterThan(_clock.UtcNow)); } + + [Test] + public void VerifyUserUnicityTurkishTest() { + CultureInfo turkishCulture = new CultureInfo("tr-TR"); + Thread.CurrentThread.CurrentCulture = turkishCulture; + + // Create user lower case + _membershipService.CreateUser(new CreateUserParams("admin", "66554321", "foo@bar.com", "", "", true)); + _container.Resolve().ContentManager.Flush(); + + // Verify unicity with upper case which with turkish coallition would yeld admin with an i without the dot and therefore generate a different user name + Assert.That(_userService.VerifyUserUnicity("ADMIN", "differentfoo@bar.com"), Is.False); // should fail + } } } diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 86688816c..4027f9a8b 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -273,6 +273,7 @@ + diff --git a/src/Orchard.Tests/UI/Resources/ResourceManagerTests.cs b/src/Orchard.Tests/UI/Resources/ResourceManagerTests.cs new file mode 100644 index 000000000..e8f3e73a3 --- /dev/null +++ b/src/Orchard.Tests/UI/Resources/ResourceManagerTests.cs @@ -0,0 +1,133 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using Autofac; +using NUnit.Framework; +using Orchard.DisplayManagement.Implementation; +using Orchard.Tests.Stubs; +using Orchard.UI.Admin; +using Orchard.UI.Resources; + +namespace Orchard.Tests.UI.Resources { + [TestFixture] + public class ResourceManagerTests { + private IContainer _container; + private IResourceManager _resourceManager; + private TestManifestProvider _testManifest; + private string _appPath = "/AppPath/"; + + private class TestManifestProvider : IResourceManifestProvider { + public Action DefineManifest { get; set; } + + public TestManifestProvider() { + + } + public void BuildManifests(ResourceManifestBuilder builder) { + var manifest = builder.Add(); + if (DefineManifest != null) { + DefineManifest(manifest); + } + } + } + + private void VerifyPaths(string resourceType, RequireSettings defaultSettings, string expectedPaths) { + defaultSettings = defaultSettings ?? new RequireSettings(); + var requiredResources = _resourceManager.BuildRequiredResources(resourceType); + var renderedResources = string.Join(",", requiredResources.Select(context => context.GetResourceUrl(defaultSettings, _appPath)).ToArray()); + Assert.That(renderedResources, Is.EqualTo(expectedPaths)); + } + + [SetUp] + public void Init() { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().As().SingleInstance(); + _container = builder.Build(); + _resourceManager = _container.Resolve(); + _testManifest = _container.Resolve() as TestManifestProvider; + } + + [Test] + public void ReleasePathIsTheDefaultPath() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.min.js", "script1.js"); + }; + _resourceManager.Require("script", "Script1"); + VerifyPaths("script", null, "script1.min.js"); + } + + [Test] + public void DebugPathIsUsedWithDebugMode() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.min.js", "script1.js"); + }; + _resourceManager.Require("script", "Script1"); + VerifyPaths("script", new RequireSettings { DebugMode = true }, "script1.js"); + } + + [Test] + public void ReleasePathIsUsedWhenNoDebugPath() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.min.js"); + }; + _resourceManager.Require("script", "Script1"); + VerifyPaths("script", new RequireSettings { DebugMode = true }, "script1.min.js"); + } + + [Test] + public void DefaultSettingsAreOverriddenByUseDebugMode() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.min.js", "script1.js"); + }; + _resourceManager.Require("script", "Script1").UseDebugMode(); + VerifyPaths("script", new RequireSettings { DebugMode = false }, "script1.js"); + } + + [Test] + public void CdnPathIsUsedInCdnMode() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.js").SetCdn("http://cdn/script1.min.js"); + }; + _resourceManager.Require("script", "Script1"); + VerifyPaths("script", new RequireSettings { CdnMode = true }, "http://cdn/script1.min.js"); + } + + [Test] + public void CdnDebugPathIsUsedInCdnModeAndDebugMode() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.js").SetCdn("http://cdn/script1.min.js", "http://cdn/script1.js"); + }; + _resourceManager.Require("script", "Script1"); + VerifyPaths("script", new RequireSettings { CdnMode = true, DebugMode = true }, "http://cdn/script1.js"); + } + + [Test] + public void DebugPathIsUsedInCdnModeAndDebugModeAndThereIsNoCdnDebugPath() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.min.js", "script1.js").SetCdn("http://cdn/script1.min.js"); + }; + _resourceManager.Require("script", "Script1"); + VerifyPaths("script", new RequireSettings { CdnMode = true, DebugMode = true }, "script1.js"); + } + + [Test] + public void DependenciesAreAutoIncluded() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.min.js"); + m.DefineResource("script", "Script2").SetUrl("script2.min.js").SetDependencies("Script1"); + }; + _resourceManager.Require("script", "Script2"); + VerifyPaths("script", null, "script1.min.js,script2.min.js"); + } + + [Test] + public void DependenciesAssumeTheirParentUseDebugModeSetting() { + _testManifest.DefineManifest = m => { + m.DefineResource("script", "Script1").SetUrl("script1.min.js", "script1.js"); + m.DefineResource("script", "Script2").SetUrl("script2.min.js", "script2.js").SetDependencies("Script1"); + }; + _resourceManager.Require("script", "Script2").UseDebugMode(); + VerifyPaths("script", new RequireSettings { DebugMode = false }, "script1.js,script2.js"); + } + } +} diff --git a/src/Orchard.Web/Core/Containers/Drivers/ContainablePartDriver.cs b/src/Orchard.Web/Core/Containers/Drivers/ContainablePartDriver.cs index 65666c63f..c2b79ecc9 100644 --- a/src/Orchard.Web/Core/Containers/Drivers/ContainablePartDriver.cs +++ b/src/Orchard.Web/Core/Containers/Drivers/ContainablePartDriver.cs @@ -4,6 +4,7 @@ using System.Web.Mvc; using Orchard.ContentManagement; using Orchard.ContentManagement.Aspects; using Orchard.ContentManagement.Drivers; +using Orchard.Core.Common.Models; using Orchard.Core.Containers.Models; using Orchard.Core.Containers.ViewModels; using Orchard.Localization; @@ -27,7 +28,7 @@ namespace Orchard.Core.Containers.Drivers { return ContentShape( "Parts_Containable_Edit", () => { - var commonPart = part.As(); + var commonPart = part.As(); var model = new ContainableViewModel(); if (commonPart != null && commonPart.Container != null) { @@ -37,10 +38,12 @@ namespace Orchard.Core.Containers.Drivers { if (updater != null) { var oldContainerId = model.ContainerId; updater.TryUpdateModel(model, "Containable", null, null); - if (oldContainerId != model.ContainerId) + if (oldContainerId != model.ContainerId) { if (commonPart != null) { - commonPart.Container = _contentManager.Get(model.ContainerId, VersionOptions.Latest); + var containerItem = _contentManager.Get(model.ContainerId, VersionOptions.Latest); + commonPart.Record.Container = containerItem == null ? null : containerItem.Record; } + } } // note: string.isnullorempty not being recognized by linq-to-nhibernate hence the inline or @@ -50,7 +53,7 @@ namespace Orchard.Core.Containers.Drivers { var listItems = new[] { new SelectListItem { Text = T("(None)").Text, Value = "0" } } .Concat(containers.Select(x => new SelectListItem { Value = Convert.ToString(x.Id), - Text = x.ContentItem.TypeDefinition.DisplayName + ": " + x.As().Title, + Text = x.ContentItem.TypeDefinition.DisplayName + ": " + _contentManager.GetItemMetadata(x.ContentItem).DisplayText, Selected = x.Id == model.ContainerId, })) .ToList(); diff --git a/src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml b/src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml index 52d1f8ad9..71aaa0c3d 100644 --- a/src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml +++ b/src/Orchard.Web/Core/Containers/Views/Item/Display.cshtml @@ -1,4 +1,9 @@ -@Display(Model.ContentItems) +@{ + IEnumerable items = Model.ContentItems; + Model.ContentItems.Classes.Add("content-items"); + Model.ContentItems.Classes.Add("list-items"); +} +@Display(items) @if (Model.ShowPager) { @Display(Model.Pager) } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Settings/Views/Admin/Index.cshtml b/src/Orchard.Web/Core/Settings/Views/Admin/Index.cshtml index c5c22fb18..f20c11a9f 100644 --- a/src/Orchard.Web/Core/Settings/Views/Admin/Index.cshtml +++ b/src/Orchard.Web/Core/Settings/Views/Admin/Index.cshtml @@ -1,7 +1,6 @@ @using Orchard.ContentManagement @{ - GroupInfo groupInfo = Model.GroupInfo; - Layout.Title = (groupInfo != null ? T("Settings for {0}", groupInfo.Name) : T("General Settings")).ToString(); + Layout.Title = T("Settings").ToString(); } @using (Html.BeginFormAntiForgeryPost()) { diff --git a/src/Orchard.Web/Core/Settings/Views/EditorTemplates/Parts.Settings.SiteSettingsPart.cshtml b/src/Orchard.Web/Core/Settings/Views/EditorTemplates/Parts.Settings.SiteSettingsPart.cshtml index fc6d61a59..d41a5e723 100644 --- a/src/Orchard.Web/Core/Settings/Views/EditorTemplates/Parts.Settings.SiteSettingsPart.cshtml +++ b/src/Orchard.Web/Core/Settings/Views/EditorTemplates/Parts.Settings.SiteSettingsPart.cshtml @@ -8,7 +8,7 @@ }, "Id", "Text", (int)Model.ResourceDebugMode); }
- @T("Enter Settings") + @T("General")
@Html.EditorFor(m => m.SiteName) diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Views/EditorTemplates/Parts.Comments.SiteSettings.cshtml b/src/Orchard.Web/Modules/Orchard.Comments/Views/EditorTemplates/Parts.Comments.SiteSettings.cshtml index d10fd3998..1a418d81f 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Views/EditorTemplates/Parts.Comments.SiteSettings.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Comments/Views/EditorTemplates/Parts.Comments.SiteSettings.cshtml @@ -2,7 +2,7 @@ @using Orchard.Comments.Models;
- @T("Enter Settings") + @T("Comments")
@Html.EditorFor(m => m.ModerateComments) diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/orchard-designertools-shapetracing.js b/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/orchard-designertools-shapetracing.js index b12dd051a..251028ad3 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/orchard-designertools-shapetracing.js +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Scripts/orchard-designertools-shapetracing.js @@ -4,7 +4,6 @@ if (!window.shapeTracingMetadataHost) { window.shapeTracingMetadataHost.placement = { 'n/a': 'n/a' }; - window.shapeTracingMetadataHost.references = {}; } jQuery(function ($) { @@ -37,7 +36,7 @@ jQuery(function ($) { var previousSize = 0; // represents the arrow to add to any collpasible container - var glyph = $(''); + var glyph = ''; // ensure the ghost has always the same size as the container // and the container is always positionned correctly @@ -118,7 +117,7 @@ jQuery(function ($) { $('#shape-tracing-resize-handle').addClass('ui-resizable-handle ui-resizable-n'); shapeTracingContainer.resizable({ handles: { n: '#shape-tracing-resize-handle' }, - grid: 20, // mitigates the number of calls to syncResize(), and aligns to the line height + grid: 20, // mitigates the number of calls to syncResize() resize: function () { shapeTracingEnabled = false }, stop: function () { shapeTracingEnabled = true } }); @@ -192,27 +191,34 @@ jQuery(function ($) { shapeTracingWindowTree.append(shapes); // add the expand/collapse logic to the shapes tree - shapeTracingWindowTree.find('li:has(ul:has(li))').prepend(glyph); + shapeTracingWindowTree.find('li:has(ul:has(li))').prepend($(glyph)); // collapse all sub uls shapeTracingWindowTree.find('ul ul').toggle(false); // expands a list of shapes in the tree var openExpando = function (expando) { - if (expando.hasClass("closed") || expando.hasClass("closing")) { - expando.siblings('ul').slideDown(100, function () { expando.removeClass("opening").removeClass("closed").addClass("open"); }); - expando.addClass("opening"); + if (expando.hasClass("closed")) { + expando.siblings('ul').toggle(true); + expando.removeClass("closed").addClass("open"); } } // collapses a list of shapes in the tree var closeExpando = function (expando) { - if (!expando.hasClass("closed") && !expando.hasClass("closing")) { - expando.siblings('ul').slideUp(100, function () { expando.removeClass("closing").removeClass("open").addClass("closed"); }); - expando.addClass("closing"); + if (expando.hasClass("open")) { + expando.siblings('ul').toggle(false); + expando.removeClass("open").addClass("closed"); } } + shapeTracingWindow.add(shapeTracingResizeHandle).hover(function () { + shapeTracingOverlay.hide(); + }, function () { + shapeTracingOverlay.show(); + } + ); + //create an overlay on shapes' descendants var overlayTarget = null; $('[shape-id]').add(shapeTracingOverlay).mousemove( @@ -316,7 +322,7 @@ jQuery(function ($) { $("[shape-id-meta]").detach().prependTo(shapeTracingWindowContent); // add the expand/collapse logic to the shape model - shapeTracingWindowContent.find('li:has(ul:has(li))').prepend(glyph); + shapeTracingWindowContent.find('li:has(ul:has(li))').prepend($(glyph)); // collapse all sub uls shapeTracingWindowContent.find('ul ul').toggle(false); @@ -328,7 +334,7 @@ jQuery(function ($) { shapeTracingTabsShape.addClass('selected'); // remove old content - shapeTracingMetaContent.empty(); + shapeTracingMetaContent.children().remove(); // render the template if (currentShape && shapeTracingMetadataHost[currentShape]) { @@ -338,10 +344,14 @@ jQuery(function ($) { shapeTracingBreadcrumb.text(''); // create collapsible containers - shapeTracingMetaContent.find('li:has(ul:has(li))').prepend(glyph); + shapeTracingMetaContent.find('li:has(ul:has(li))').prepend($(glyph)); shapeTracingMetaContent.find('ul ul').toggle(false); shapeTracingMetaContent.find('.expando-glyph-container').click(expandCollapseExpando); + $('#activeTemplate').click(function () { + displayTabTemplate(); + }); + defaultTab = displayTabShape; }; @@ -358,7 +368,7 @@ jQuery(function ($) { shapeTracingTabsModel.addClass('selected'); // remove old content - shapeTracingMetaContent.empty(); + shapeTracingMetaContent.children().remove(); // render the template if (currentShape && shapeTracingMetadataHost[currentShape]) { @@ -368,7 +378,7 @@ jQuery(function ($) { shapeTracingBreadcrumb.text(''); // create collapsible containers - shapeTracingMetaContent.find('li:has(ul:has(li))').prepend(glyph); + shapeTracingMetaContent.find('li:has(ul:has(li))').prepend($(glyph)); shapeTracingMetaContent.find('ul ul').toggle(false); shapeTracingMetaContent.find('.expando-glyph-container').click(expandCollapseExpando); @@ -407,7 +417,7 @@ jQuery(function ($) { }); // open the root node (Model) - openExpando(shapeTracingMetaContent.find('.expando-glyph-container:first')) + shapeTracingMetaContent.find('.expando-glyph-container:first').click(); defaultTab = displayTabModel; }; @@ -423,7 +433,7 @@ jQuery(function ($) { shapeTracingTabsPlacement.addClass('selected'); // remove old content - shapeTracingMetaContent.empty(); + shapeTracingMetaContent.children().remove(); // render the template if (currentShape && shapeTracingMetadataHost[currentShape]) { @@ -450,7 +460,7 @@ jQuery(function ($) { shapeTracingTabsTemplate.addClass('selected'); // remove old content - shapeTracingMetaContent.empty(); + shapeTracingMetaContent.children().remove(); // render the template if (currentShape && shapeTracingMetadataHost[currentShape]) { @@ -476,7 +486,7 @@ jQuery(function ($) { shapeTracingTabsHtml.addClass('selected'); // remove old content - shapeTracingMetaContent.empty(); + shapeTracingMetaContent.children().remove(); // render the template if (currentShape && shapeTracingMetadataHost[currentShape]) { @@ -507,7 +517,7 @@ jQuery(function ($) { // hooks the click event on expandos var expandCollapseExpando = function () { var _this = $(this); - if (_this.hasClass("closed") || _this.hasClass("closing")) { + if (_this.hasClass("closed")) { openExpando(_this); } else { diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ObjectDumper.cs b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ObjectDumper.cs index 24edc99a4..b46486d44 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ObjectDumper.cs +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ObjectDumper.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Collections; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using System.Xml.Linq; using ClaySharp; using ClaySharp.Behaviors; @@ -11,25 +10,22 @@ using Orchard.ContentManagement; using Orchard.DisplayManagement; namespace Orchard.DesignerTools.Services { - + public class ObjectDumper { private const int MaxStringLength = 60; private readonly Stack _parents = new Stack(); private readonly Stack _currents = new Stack(); private readonly int _levels; - private readonly Dictionary _local; - private readonly Dictionary _global; private readonly XDocument _xdoc; private XElement _current; // object/key/dump - public ObjectDumper(int levels, Dictionary local, Dictionary global) { + public ObjectDumper(int levels) + { _levels = levels; - _local = local; - _global = global; _xdoc = new XDocument(); _xdoc.Add(_current = new XElement("ul")); } @@ -40,6 +36,7 @@ namespace Orchard.DesignerTools.Services { } _parents.Push(o); + // starts a new container EnterNode("li"); @@ -51,20 +48,11 @@ namespace Orchard.DesignerTools.Services { DumpValue(o, name); } else { - int hashCode = RuntimeHelpers.GetHashCode(o); - // if the object has already been processed, return a named ref to it - if (_global.ContainsKey(hashCode)) { - _current.Add( - new XElement("h1", new XText(name)), - new XElement("span", FormatType(o)), - new XElement("a", new XAttribute("href", hashCode.ToString())) - ); - } - else { - _global.Add(hashCode, _current); - _local.Add(hashCode, _current); - DumpObject(o, name); + if (_parents.Count >= _levels) { + return _current; } + + DumpObject(o, name); } } finally { @@ -87,29 +75,31 @@ namespace Orchard.DesignerTools.Services { _current.Add( new XElement("h1", new XText(name)), new XElement("span", FormatType(o)) - ); + ); + + EnterNode("ul"); - if (_parents.Count >= _levels) { - return; - } + try { + if (o is IDictionary) { + DumpDictionary((IDictionary) o); + } + else if (o is IShape) { + DumpShape((IShape) o); - if (o is IDictionary) { - DumpDictionary((IDictionary)o); - } - else if (o is IShape) { - DumpShape((IShape)o); - - // a shape can also be IEnumerable - if (o is IEnumerable) { + // a shape can also be IEnumerable + if (o is IEnumerable) { + DumpEnumerable((IEnumerable) o); + } + } + else if (o is IEnumerable) { DumpEnumerable((IEnumerable) o); } + else { + DumpMembers(o); + } } - else if (o is IEnumerable) - { - DumpEnumerable((IEnumerable)o); - } - else { - DumpMembers(o); + finally { + RestoreCurrentNode(); } } @@ -124,55 +114,46 @@ namespace Orchard.DesignerTools.Services { return; } - EnterNode("ul"); - - try{ - foreach (var member in members) { - if (o is ContentItem && member.Name == "ContentManager" - || o is Delegate) { - continue; - } - SafeCall(() => DumpMember(o, member)); + foreach (var member in members) { + if (o is ContentItem && member.Name == "ContentManager" + || o is Delegate) { + continue; } + SafeCall(() => DumpMember(o, member)); + } - // process ContentItem.Parts specifically - foreach (var member in members) { - if (o is ContentItem && member.Name == "Parts") { - foreach (var part in ((ContentItem) o).Parts) { - SafeCall(() => Dump(part, part.PartDefinition.Name)); + // process ContentItem.Parts specifically + foreach (var member in members) { + if (o is ContentItem && member.Name == "Parts") { + foreach (var part in ((ContentItem) o).Parts) { + // ignore contentparts like ContentPart + if(part.GetType().IsGenericType) { + continue; } - } - } - foreach (var member in members) { - // process ContentPart.Fields specifically - if (o is ContentPart && member.Name == "Fields") { - foreach (var field in ((ContentPart) o).Fields) { - SafeCall(() => Dump(field, field.Name)); - } + SafeCall(() => Dump(part, part.PartDefinition.Name)); } } } - finally { - RestoreCurrentNode(); + + foreach (var member in members) { + // process ContentPart.Fields specifically + if (o is ContentPart && member.Name == "Fields") { + foreach (var field in ((ContentPart) o).Fields) { + SafeCall(() => Dump(field, field.Name)); + } + } } } private void DumpEnumerable(IEnumerable enumerable) { - if(!enumerable.GetEnumerator().MoveNext()) { + if (!enumerable.GetEnumerator().MoveNext()) { return; } - EnterNode("ul"); - - try { - int i = 0; - foreach (var child in enumerable) { - Dump(child, string.Format("[{0}]", i++)); - } - } - finally { - RestoreCurrentNode(); + int i = 0; + foreach (var child in enumerable) { + Dump(child, string.Format("[{0}]", i++)); } } @@ -181,21 +162,14 @@ namespace Orchard.DesignerTools.Services { return; } - EnterNode("ul"); - - try { - foreach (var key in dictionary.Keys) { - Dump(dictionary[key], string.Format("[\"{0}\"]", key)); - } - } - finally { - RestoreCurrentNode(); + foreach (var key in dictionary.Keys) { + Dump(dictionary[key], string.Format("[\"{0}\"]", key)); } } private void DumpShape(IShape shape) { - var b = ((IClayBehaviorProvider)(dynamic)shape).Behavior as ClayBehaviorCollection; + var b = ((IClayBehaviorProvider) (dynamic) shape).Behavior as ClayBehaviorCollection; if (b == null) return; @@ -216,19 +190,12 @@ namespace Orchard.DesignerTools.Services { return; } - EnterNode("ul"); - - try { - foreach (var key in props.Keys) { - // ignore private members (added dynmically by the shape wrapper) - if (key.ToString().StartsWith("_")) { - continue; - } - Dump(props[key], key.ToString()); + foreach (var key in props.Keys) { + // ignore private members (added dynmically by the shape wrapper) + if (key.ToString().StartsWith("_")) { + continue; } - } - finally { - RestoreCurrentNode(); + Dump(props[key], key.ToString()); } } @@ -305,9 +272,10 @@ namespace Orchard.DesignerTools.Services { _current = _currents.Pop(); } - private void EnterNode(string tag) { + private XElement EnterNode(string tag) { SaveCurrentNode(); _current.Add(_current = new XElement(tag)); + return _current; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ShapeTracingFactory.cs b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ShapeTracingFactory.cs index 80530069e..268459701 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ShapeTracingFactory.cs +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ShapeTracingFactory.cs @@ -1,8 +1,7 @@ -using System.Collections.Generic; +using System; using System.Linq; using System.Text; using System.Web.Routing; -using System.Runtime.CompilerServices; using System.Xml.Linq; using Orchard.DisplayManagement.Descriptors; using Orchard.DisplayManagement.Implementation; @@ -23,7 +22,6 @@ namespace Orchard.DesignerTools.Services { private readonly IWebSiteFolder _webSiteFolder; private readonly IAuthorizer _authorizer; private int _shapeId; - private readonly Dictionary _dumped = new Dictionary(1000); public ShapeTracingFactory( WorkContext workContext, @@ -76,10 +74,12 @@ namespace Orchard.DesignerTools.Services { } shapeMetadata.Wrappers.Add("ShapeTracingWrapper"); + shapeMetadata.OnDisplaying(OnDisplaying); } } + public void Displaying(ShapeDisplayingContext context) {} - public void Displaying(ShapeDisplayingContext context) { + public void OnDisplaying(ShapeDisplayingContext context) { if (!IsActivable()) { return; } @@ -96,24 +96,18 @@ namespace Orchard.DesignerTools.Services { var descriptor = shapeTable.Descriptors[shapeMetadata.Type]; // dump the Shape's content - var local = new Dictionary(); - new ObjectDumper(6, local, _dumped).Dump(context.Shape, "Model"); - context.Shape.Reference = RuntimeHelpers.GetHashCode(context.Shape); + var dump = new ObjectDumper(6).Dump(context.Shape, "Model"); var sb = new StringBuilder(); - context.Shape.LocalReferences = new Dictionary(); - foreach (var key in local.Keys) { - sb.Clear(); - ConvertToJSon(local[key], sb); - ((Dictionary) context.Shape.LocalReferences)[key] = sb.ToString(); - } + ConvertToJSon(dump, sb); + shape._Dump = sb.ToString(); shape.Template = null; shape.OriginalTemplate = descriptor.BindingSource; foreach (var extension in new[] { ".cshtml", ".aspx" }) { - foreach (var alternate in shapeMetadata.Alternates.Reverse()) { - var alternateFilename = currentTheme.Location + "/" + currentTheme.Id + "/Views/" + alternate.Replace("__", "-").Replace("_", ".") + extension; + foreach (var alternate in shapeMetadata.Alternates.Reverse().Concat(new [] {shapeMetadata.Type}) ) { + var alternateFilename = FormatShapeFilename(alternate, shapeMetadata.Type, shapeMetadata.DisplayType, currentTheme.Location + "/" + currentTheme.Id, extension); if (_webSiteFolder.FileExists(alternateFilename)) { shape.Template = alternateFilename; } @@ -158,47 +152,59 @@ namespace Orchard.DesignerTools.Services { public void Displayed(ShapeDisplayedContext context) { } - private static void ConvertToJSon(XElement x, StringBuilder sb) { + public static void ConvertToJSon(XElement x, StringBuilder sb) { if(x == null) { return; } switch (x.Name.ToString()) { case "ul" : + var first = true; foreach(var li in x.Elements()) { + if (!first) sb.Append(","); ConvertToJSon(li, sb); - sb.Append(","); + first = false; } break; case "li": var name = x.Element("h1").Value; var value = x.Element("span").Value; - sb.AppendFormat("name: \"{0}\", ", FormatJsonValue(name)); - sb.AppendFormat("value: \"{0}\"", FormatJsonValue(value)); - - var a = x.Element("a"); - if (a != null) { - sb.AppendFormat(", children: shapeTracingMetadataHost.references[{0}]", a.Attribute("href").Value); - } + sb.AppendFormat("\"name\": \"{0}\", ", FormatJsonValue(name)); + sb.AppendFormat("\"value\": \"{0}\"", FormatJsonValue(value)); var ul = x.Element("ul"); - if (ul != null) { - sb.Append(", children: ["); + if (ul != null && ul.Descendants().Any()) { + sb.Append(", \"children\": ["); + first = true; foreach (var li in ul.Elements()) { - sb.Append("{ "); + sb.Append(first ? "{ " : ", {"); ConvertToJSon(li, sb); - sb.Append(" },"); + sb.Append(" }"); + first = false; } sb.Append("]"); } + break; } } private static string FormatJsonValue(string value) { // replace " by \" in json strings - return value.Replace("\"", @"\"""); + return value.Replace(@"\", @"\\").Replace("\"", @"\""").Replace("\r\n", @"\n").Replace("\n", @"\n"); } + + private static string FormatShapeFilename(string shape, string shapeType, string displayType, string themePrefix, string extension) { + + if (!String.IsNullOrWhiteSpace(displayType)) { + if (shape.StartsWith(shapeType + "_" + displayType)) { + shape = shapeType + shape.Substring(shapeType.Length + displayType.Length + 1) + "_" + displayType; + } + } + + return themePrefix + "/Views/" + shape.Replace("__", "-").Replace("_", ".") + extension; + } + } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/TemplatesFilter.cs b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/TemplatesFilter.cs index 4d5cc3900..351b02c7e 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/TemplatesFilter.cs +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/TemplatesFilter.cs @@ -1,16 +1,22 @@ using System.Web.Mvc; +using System.Web.Routing; using Orchard.DisplayManagement; using Orchard.Mvc.Filters; +using Orchard.Security; +using Orchard.UI.Admin; namespace Orchard.DesignerTools.Services { public class TemplatesFilter : FilterProvider, IResultFilter { - private readonly IWorkContextAccessor _workContextAccessor; + private readonly WorkContext _workContext; + private readonly IAuthorizer _authorizer; private readonly dynamic _shapeFactory; public TemplatesFilter( - IWorkContextAccessor workContextAccessor, + WorkContext workContext, + IAuthorizer authorizer, IShapeFactory shapeFactory) { - _workContextAccessor = workContextAccessor; + _workContext = workContext; + _authorizer = authorizer; _shapeFactory = shapeFactory; } @@ -19,12 +25,27 @@ namespace Orchard.DesignerTools.Services { if (!(filterContext.Result is ViewResult)) return; - var ctx = _workContextAccessor.GetContext(); - var tail = ctx.Layout.Tail; + if(!IsActivable()) { + return; + } + + var tail = _workContext.Layout.Tail; tail.Add(_shapeFactory.ShapeTracingTemplates()); } public void OnResultExecuted(ResultExecutedContext filterContext) { } + + private bool IsActivable() { + // activate on front-end only + if (AdminFilter.IsApplied(new RequestContext(_workContext.HttpContext, new RouteData()))) + return false; + + // if not logged as a site owner, still activate if it's a local request (development machine) + if (!_authorizer.Authorize(StandardPermissions.SiteOwner)) + return _workContext.HttpContext.Request.IsLocal; + + return true; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/orchard-designertools-shapetracing.css b/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/orchard-designertools-shapetracing.css index 121fb55cb..8b54b755c 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/orchard-designertools-shapetracing.css +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Styles/orchard-designertools-shapetracing.css @@ -75,7 +75,7 @@ button.create-template, button.create-template:hover, background-image:hover { clear:both; display:block; font-size:10pt; - font-family:Segoe; + font-family:Segoe UI,Trebuchet,Arial,Sans-Serif;; left: 0px; bottom: 0px; position:fixed; @@ -321,10 +321,14 @@ ul#shape-tracing-tabs { #shape-tracing-window-content .grid-display div.type, #shape-tracing-window-content .grid-display div.value { position:absolute; - left:66%; + left:40%; white-space:nowrap; } +#shape-tracing-window-content div.model div.type, #shape-tracing-window-content div.model div.value { + left:66%; +} + #shape-tracing-breadcrumb { background:rgba(236, 241, 242, 1.0); border-bottom:1px solid rgba(182, 188, 189, 1.0); diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Views/ShapeTracingMeta.cshtml b/src/Orchard.Web/Modules/Orchard.DesignerTools/Views/ShapeTracingMeta.cshtml index a52d4d669..4616422b1 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Views/ShapeTracingMeta.cshtml +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Views/ShapeTracingMeta.cshtml @@ -1,8 +1,22 @@ @using Orchard.Utility.Extensions; @functions { - string FormatShapeFilename(string type, string themeId) { - return "~/Themes/" + themeId + "/Views/" + type.Replace("__", "-").Replace("_", ".") + ".cshtml"; + string FormatShapeName(string shape) { + string shapeType = Model.ShapeType; + string displayType = Model.DisplayType; + + if(!String.IsNullOrWhiteSpace(displayType)) { + if(shape.StartsWith(shapeType + "_" + displayType)) { + shape = shapeType + shape.Substring(shapeType.Length + displayType.Length + 1) + "_" + displayType; + } + } + + return shape.Replace("__", "-").Replace("_", "."); + } + + string FormatShapeFilename(string shape, string themeId) { + shape = FormatShapeName(shape); + return "~/Themes/" + themeId + "/Views/" + shape.Replace("__", "-").Replace("_", ".") + ".cshtml"; } string RemoveBeacons(string htmlContent) { @@ -20,10 +34,6 @@