diff --git a/Orchard.proj b/Orchard.proj index 5dfb5a3e7..9aac282cd 100644 --- a/Orchard.proj +++ b/Orchard.proj @@ -388,7 +388,7 @@ - + diff --git a/src/Orchard.Specs/Bindings/HtmlNodeExtensions.cs b/src/Orchard.Specs/Bindings/HtmlNodeExtensions.cs new file mode 100644 index 000000000..0a8b4281c --- /dev/null +++ b/src/Orchard.Specs/Bindings/HtmlNodeExtensions.cs @@ -0,0 +1,11 @@ +using HtmlAgilityPack; + +namespace Orchard.Specs.Bindings { + public static class HtmlNodeExtensions { + public static string GetOptionValue(this HtmlNode node) { + return node.Attributes.Contains("value") + ? node.GetAttributeValue("value", "") + : node.NextSibling != null && node.NextSibling.NodeType == HtmlNodeType.Text ? node.NextSibling.InnerText : ""; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Specs/Bindings/WebAppHosting.cs b/src/Orchard.Specs/Bindings/WebAppHosting.cs index d88d0707e..a2c6d09e2 100644 --- a/src/Orchard.Specs/Bindings/WebAppHosting.cs +++ b/src/Orchard.Specs/Bindings/WebAppHosting.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Web; using Castle.Core.Logging; using HtmlAgilityPack; @@ -181,21 +182,21 @@ namespace Orchard.Specs.Bindings { Host.HostName = host; Details = Host.SendRequest(urlPath); _doc = new HtmlDocument(); - _doc.Load(new StringReader(Details.ResponseText)); + _doc.Load(new StringReader(Regex.Replace(Details.ResponseText, @">\s+<", "><"))); } [When(@"I go to ""(.*)""")] public void WhenIGoTo(string urlPath) { Details = Host.SendRequest(urlPath); _doc = new HtmlDocument(); - _doc.Load(new StringReader(Details.ResponseText)); + _doc.Load(new StringReader(Regex.Replace(Details.ResponseText, @">\s+<", "><"))); } [When(@"I follow ""([^""]*)""")] public void WhenIFollow(string linkText) { var link = _doc.DocumentNode .SelectNodes("//a") - .SingleOrDefault(elt => elt.InnerText == linkText) + .SingleOrDefault(elt => elt.InnerHtml == linkText) ?? _doc.DocumentNode .SelectSingleNode(string.Format("//a[@title='{0}']", linkText)); @@ -208,7 +209,7 @@ namespace Orchard.Specs.Bindings { public void WhenIFollow(string linkText, string hrefFilter) { var link = _doc.DocumentNode .SelectNodes("//a[@href]").Where(elt => - (elt.InnerText == linkText || + (elt.InnerHtml == linkText || (elt.Attributes["title"] != null && elt.Attributes["title"].Value == linkText)) && elt.Attributes["href"].Value.IndexOf(hrefFilter, StringComparison.OrdinalIgnoreCase) != -1).SingleOrDefault(); @@ -280,9 +281,9 @@ namespace Orchard.Specs.Bindings { break; default: if (string.Equals(input.Name, "select", StringComparison.OrdinalIgnoreCase)) { - var options = input.ChildNodes; + var options = input.Descendants("option"); foreach (var option in options) { - if (option.GetAttributeValue("value", "") == row["value"]) + if (option.GetAttributeValue("value", "") == row["value"] || (option.NextSibling.NodeType == HtmlNodeType.Text && option.NextSibling.InnerText == row["value"])) option.Attributes.Add("selected", "selected"); else if (option.Attributes.Contains("selected")) option.Attributes.Remove("selected"); @@ -303,7 +304,7 @@ namespace Orchard.Specs.Bindings { .SelectSingleNode(string.Format("(//input[@type='submit'][@value='{0}']|//button[@type='submit'][text()='{0}'])", submitText)); var form = Form.LocateAround(submit); - var urlPath = form.Start.GetAttributeValue("action", Details.UrlPath); + var urlPath = HttpUtility.HtmlDecode(form.Start.GetAttributeValue("action", Details.UrlPath)); var inputs = form.Children @@ -316,18 +317,18 @@ namespace Orchard.Specs.Bindings { // select all elements form.Children.SelectMany(elt => elt.DescendantsAndSelf("select")).Where(elt => elt.Name.Equals("select", StringComparison.OrdinalIgnoreCase)) // group them by their name with value that comes from first of: - // (1) value of option with 'selected' attribute, + // (1) value of option with 'selecturlPath.Replace("127.0.0.1", "localhost")ed' attribute, // (2) value of first option (none have 'selected'), // (3) empty value (e.g. select with no options) .GroupBy( sel => sel.GetAttributeValue("name", sel.GetAttributeValue("id", "")), - sel => (sel.Descendants("option").SingleOrDefault(opt => opt.Attributes["selected"] != null) ?? sel.Descendants("option").FirstOrDefault() ?? new HtmlNode(HtmlNodeType.Element, _doc, 0)).GetAttributeValue("value", ""))) + sel => (sel.Descendants("option").SingleOrDefault(opt => opt.Attributes["selected"] != null) ?? sel.Descendants("option").FirstOrDefault() ?? new HtmlNode(HtmlNodeType.Element, _doc, 0)).GetOptionValue())) .ToDictionary(elt => elt.Key, elt => (IEnumerable)elt); if (submit.Attributes.Contains("name")) inputs.Add(submit.GetAttributeValue("name", ""), new[] {submit.GetAttributeValue("value", "yes")}); - - Details = Host.SendRequest(urlPath, inputs); + + Details = Host.SendRequest(urlPath, inputs, form.Start.GetAttributeValue("method", "GET").ToUpperInvariant()); _doc = new HtmlDocument(); _doc.Load(new StringReader(Details.ResponseText)); } @@ -336,10 +337,10 @@ namespace Orchard.Specs.Bindings { public void WhenIAmRedirected() { var urlPath = ""; if (Details.ResponseHeaders.TryGetValue("Location", out urlPath)) { - WhenIGoTo(urlPath); + WhenIGoTo(urlPath.Replace("http://127.0.0.1/", "")); } else { - Assert.Fail("No Location header returned"); + Assert.Fail("Expected to be redirected but no Location header returned"); } } diff --git a/src/Orchard.Specs/Hosting/RequestExtensions.cs b/src/Orchard.Specs/Hosting/RequestExtensions.cs index 5b0b584a9..039aeee0f 100644 --- a/src/Orchard.Specs/Hosting/RequestExtensions.cs +++ b/src/Orchard.Specs/Hosting/RequestExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Net; using System.Text; using System.Text.RegularExpressions; using System.Web; @@ -11,7 +12,7 @@ using Orchard.Specs.Util; namespace Orchard.Specs.Hosting { public static class RequestExtensions { - public static RequestDetails SendRequest(this WebHost webHost, string urlPath, IDictionary> postData) { + public static RequestDetails SendRequest(this WebHost webHost, string urlPath, IDictionary> postData, string requestMethod = null) { var physicalPath = Bleroy.FluentPath.Path.Get(webHost.PhysicalDirectory); @@ -28,7 +29,7 @@ namespace Orchard.Specs.Hosting { int queryIndex = urlPath.IndexOf('?'); if (queryIndex >= 0) { details.UrlPath = urlPath.Substring(0, queryIndex); - details.Query = HttpUtility.UrlDecode(urlPath.Substring(queryIndex + 1)); + details.Query = urlPath.Substring(queryIndex + 1); } details.Page = (isHomepage ? "" : physicalPath.Combine(details.UrlPath.TrimStart('/', '\\')).GetRelativePath(physicalPath).ToString()); @@ -41,7 +42,11 @@ namespace Orchard.Specs.Hosting { .SelectMany(kv => kv.Value.Select(v => new { k = kv.Key, v })) .Select((kv, n) => new { p = HttpUtility.UrlEncode(kv.k) + "=" + HttpUtility.UrlEncode(kv.v), n }) .Aggregate("", (a, x) => a + (x.n == 0 ? "" : "&") + x.p); - details.PostData = Encoding.Default.GetBytes(requestBodyText); + + if (requestMethod == "POST") + details.PostData = Encoding.Default.GetBytes(requestBodyText); + else + details.Query = requestBodyText; } webHost.Execute(() => { diff --git a/src/Orchard.Specs/Orchard.Specs.csproj b/src/Orchard.Specs/Orchard.Specs.csproj index fd8af6fda..9a13e8d9e 100644 --- a/src/Orchard.Specs/Orchard.Specs.csproj +++ b/src/Orchard.Specs/Orchard.Specs.csproj @@ -131,6 +131,7 @@ + diff --git a/src/Orchard.Specs/Widgets.feature b/src/Orchard.Specs/Widgets.feature index 34fdb51af..913fcb804 100644 --- a/src/Orchard.Specs/Widgets.feature +++ b/src/Orchard.Specs/Widgets.feature @@ -41,3 +41,25 @@ Scenario: I can delete a layer And I am redirected Then I should see "Layer was successfully deleted" And I should not see "]*>Default" + +Scenario: I can add a widget to a specific zone in a specific layer + Given I have installed Orchard + When I go to "admin/widgets" + And I fill in + | name | value | + | layerId | Disabled | + And I hit "Show" + Then I should see "]*selected[^>]*>Disabled" + When I follow "Add" where href has "zone=Header" + Then I should see "]*>Choose A Widget" + When I follow "Html Widget" + Then I should see "]*>Add Widget" + When I fill in + | name | value | + | Title | Flashy HTML Widget | + | Body.Text | hi | + And I hit "Save" + And I am redirected + Then I should see "Your Html Widget has been added." + And I should see "]*selected[^>]*>Disabled" + And I should see "]*class="[^"]*widgets-this-layer[^"]*"[^>]*>\s*]*>\s*]*>\s*]*>Flashy HTML Widget\s*" diff --git a/src/Orchard.Specs/Widgets.feature.cs b/src/Orchard.Specs/Widgets.feature.cs index ad58a757a..b1bda5e0b 100644 --- a/src/Orchard.Specs/Widgets.feature.cs +++ b/src/Orchard.Specs/Widgets.feature.cs @@ -158,6 +158,65 @@ this.ScenarioSetup(scenarioInfo); testRunner.Then("I should see \"Layer was successfully deleted\""); #line 43 testRunner.And("I should not see \"]*>Default\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("I can add a widget to a specific zone in a specific layer")] + public virtual void ICanAddAWidgetToASpecificZoneInASpecificLayer() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("I can add a widget to a specific zone in a specific layer", ((string[])(null))); +#line 45 +this.ScenarioSetup(scenarioInfo); +#line 46 + testRunner.Given("I have installed Orchard"); +#line 47 + testRunner.When("I go to \"admin/widgets\""); +#line hidden + TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table3.AddRow(new string[] { + "layerId", + "Disabled"}); +#line 48 + testRunner.And("I fill in", ((string)(null)), table3); +#line 51 + testRunner.And("I hit \"Show\""); +#line 52 + testRunner.Then("I should see \"]*selected[^>]*>Disabled\""); +#line 53 + testRunner.When("I follow \"Add\" where href has \"zone=Header\""); +#line 54 + testRunner.Then("I should see \"]*>Choose A Widget\""); +#line 55 + testRunner.When("I follow \"Html Widget\""); +#line 56 + testRunner.Then("I should see \"]*>Add Widget\""); +#line hidden + TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table4.AddRow(new string[] { + "Title", + "Flashy HTML Widget"}); + table4.AddRow(new string[] { + "Body.Text", + "hi"}); +#line 57 + testRunner.When("I fill in", ((string)(null)), table4); +#line 61 + testRunner.And("I hit \"Save\""); +#line 62 + testRunner.And("I am redirected"); +#line 63 + testRunner.Then("I should see \"Your Html Widget has been added.\""); +#line 64 + testRunner.And("I should see \"]*selected[^>]*>Disabled\""); +#line 65 + testRunner.And("I should see \"]*class=\"[^\"]*widgets-this-layer[^\"]*\"[^>]*>\\s*]*>\\s*" + + "]*>\\s*]*>Flashy HTML Widget\\s*\""); #line hidden testRunner.CollectScenarioErrors(); } 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/Containers/Views/Parts.ContainerWidget.cshtml b/src/Orchard.Web/Core/Containers/Views/Parts.ContainerWidget.cshtml index 46e0c33a4..8623f85b8 100644 --- a/src/Orchard.Web/Core/Containers/Views/Parts.ContainerWidget.cshtml +++ b/src/Orchard.Web/Core/Containers/Views/Parts.ContainerWidget.cshtml @@ -1 +1,6 @@ -@Display(Model.ContentItems) \ No newline at end of file +@{ + IEnumerable items = Model.ContentItems; + Model.ContentItems.Classes.Add("content-items"); + Model.ContentItems.Classes.Add("list-items"); +} +@Display(items) \ No newline at end of file diff --git a/src/Orchard.Web/Core/Dashboard/Views/Admin/Index.cshtml b/src/Orchard.Web/Core/Dashboard/Views/Admin/Index.cshtml index 5f05fe235..da1561f46 100644 --- a/src/Orchard.Web/Core/Dashboard/Views/Admin/Index.cshtml +++ b/src/Orchard.Web/Core/Dashboard/Views/Admin/Index.cshtml @@ -24,9 +24,13 @@ Contribute back. Help grow Orchard. We encourage contributions of all sorts, including code submissions, documentation, translations, feature recommendations, and more.Here are some ways to give back to the project. - -@T("Advisory from {0}", "http://www.orchardproject.net/advisory") - + + @T("Stay up to date.") + @T("Your browser does not support iframes. You can't see advisory messages.") + + + + diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/AddPartsTo.cshtml b/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/AddPartsTo.cshtml index 4008075aa..033e57d9e 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/AddPartsTo.cshtml +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Views/Admin/AddPartsTo.cshtml @@ -18,6 +18,7 @@ ViewData.TemplateInfo.GetFullHtmlFieldId(fieldNameStart + "IsSelected"), partSelection.PartDisplayName, Html.Hidden(fieldNameStart + "PartName", partSelection.PartName))); + }, "available-parts") diff --git a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ShapeTracingFactory.cs b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ShapeTracingFactory.cs index 268459701..2f3b05b0f 100644 --- a/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ShapeTracingFactory.cs +++ b/src/Orchard.Web/Modules/Orchard.DesignerTools/Services/ShapeTracingFactory.cs @@ -192,7 +192,7 @@ namespace Orchard.DesignerTools.Services { private static string FormatJsonValue(string value) { // replace " by \" in json strings - return value.Replace(@"\", @"\\").Replace("\"", @"\""").Replace("\r\n", @"\n").Replace("\n", @"\n"); + return value.Replace(@"\", @"\\").Replace("\"", @"\""").Replace("\r\n", @"\n").Replace("\r", @"\n").Replace("\n", @"\n"); } private static string FormatShapeFilename(string shape, string shapeType, string displayType, string themePrefix, string extension) { diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Services/ExtensionReferenceRepository.cs b/src/Orchard.Web/Modules/Orchard.Packaging/Services/ExtensionReferenceRepository.cs index 1ea3a9f44..508150384 100644 --- a/src/Orchard.Web/Modules/Orchard.Packaging/Services/ExtensionReferenceRepository.cs +++ b/src/Orchard.Web/Modules/Orchard.Packaging/Services/ExtensionReferenceRepository.cs @@ -38,20 +38,19 @@ namespace Orchard.Packaging.Services { } public override IQueryable GetPackages() { + IEnumerable repositoryPackages = SourceRepository.GetPackages().ToList(); IEnumerable packages = from extension in _extensionManager.AvailableExtensions() let id = PackageBuilder.BuildPackageId(extension.Id, extension.ExtensionType) let version = Version.Parse(extension.Version) - let package = SourceRepository.FindPackage(id, version) + let package = repositoryPackages.FirstOrDefault(p => p.Id == id && p.Version == version) where package != null select package; return packages.AsQueryable(); } - public override void AddPackage(IPackage package) { - } + public override void AddPackage(IPackage package) {} - public override void RemovePackage(IPackage package) { - } + public override void RemovePackage(IPackage package) {} } } diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs index 2058367c1..ebf9aef0a 100644 --- a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs +++ b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs @@ -73,22 +73,15 @@ namespace Orchard.Packaging.Services { .SelectMany( source => { var galleryFeedContext = new GalleryFeedContext(new Uri(source.FeedUrl)); - IQueryable packages = galleryFeedContext.Packages; - + IQueryable packages = includeScreenshots + ? galleryFeedContext.Packages.Expand("Screenshots") + : galleryFeedContext.Packages; + if (query != null) { packages = query(packages); } - return packages.ToList().Select( - p => { - PublishedScreenshot firstScreenshot = includeScreenshots - ? galleryFeedContext.Screenshots - .Where(s => s.PublishedPackageId == p.Id && s.PublishedPackageVersion == p.Version) - .ToList() - .FirstOrDefault() - : null; - return CreatePackageEntry(p, firstScreenshot, packagingSource, galleryFeedContext.GetReadStreamUri(p)); - }); + return packages.ToList().Select(p => CreatePackageEntry(p, packagingSource, galleryFeedContext.GetReadStreamUri(p))); } ); } @@ -116,12 +109,14 @@ namespace Orchard.Packaging.Services { #endregion - private static PackagingEntry CreatePackageEntry(PublishedPackage package, PublishedScreenshot screenshot, PackagingSource source, Uri downloadUri) { + private static PackagingEntry CreatePackageEntry(PublishedPackage package, PackagingSource source, Uri downloadUri) { Uri baseUri = new Uri(string.Format("{0}://{1}:{2}/", downloadUri.Scheme, downloadUri.Host, downloadUri.Port)); + PublishedScreenshot screenshot = package.Screenshots != null ? package.Screenshots.FirstOrDefault() : null; + string iconUrl = GetAbsoluteUri(package.IconUrl, baseUri); string firstScreenshot = screenshot != null ? GetAbsoluteUri(screenshot.ScreenshotUri, baseUri) : string.Empty; diff --git a/src/Orchard.Web/Themes/TheAdmin/Styles/images/icon-advisory.png b/src/Orchard.Web/Themes/TheAdmin/Styles/images/icon-advisory.png new file mode 100644 index 000000000..6cd96fdd5 Binary files /dev/null and b/src/Orchard.Web/Themes/TheAdmin/Styles/images/icon-advisory.png differ diff --git a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css index 684920f9c..97334a548 100644 --- a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css +++ b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css @@ -119,7 +119,8 @@ body { padding:0; } -iframe {border:1px solid #eee;} +/*Hide shape tracing*/ +#shape-tracing-container {display:none;} /* Headings */ h1,h2,h3,h4,h5,h6 { font-weight: normal;} @@ -1085,14 +1086,9 @@ fieldset.delete-button { .dashboard .help-item h2 { - background-color: #dee0e1; padding: 6px; font-size: 16px; } -.dashboard h2.advisory -{ - font-size: 16px; -} .dashboard .help-item h2.gallery { background: #f1f1f2 url('images/icon-gallery.png') no-repeat 5px 50%; @@ -1123,6 +1119,11 @@ fieldset.delete-button { padding-left: 40px; } +.dashboard .help-item h2.advisory +{ + background: #f1f1f2 url('images/icon-advisory.png') no-repeat 5px 50%; + padding-left: 40px; +} diff --git a/src/Orchard.Web/Themes/Themes.csproj b/src/Orchard.Web/Themes/Themes.csproj index 910a581c9..387c6a0f7 100644 --- a/src/Orchard.Web/Themes/Themes.csproj +++ b/src/Orchard.Web/Themes/Themes.csproj @@ -55,6 +55,12 @@ + + + + + +
hi
Help grow Orchard. We encourage contributions of all sorts, including code submissions, documentation, translations, feature recommendations, and more.Here are some ways to give back to the project.
@T("Your browser does not support iframes. You can't see advisory messages.")