diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeDriver.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeDriver.cs index 780cdce97e4..c374bab5fe0 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeDriver.cs @@ -33,6 +33,7 @@ public override IDisplayResult Edit(LinkAdminNode treeNode) model.LinkText = treeNode.LinkText; model.LinkUrl = treeNode.LinkUrl; model.IconClass = treeNode.IconClass; + model.Target = treeNode.Target; var permissions = await _adminMenuPermissionService.GetPermissionsAsync(); @@ -56,10 +57,11 @@ public override IDisplayResult Edit(LinkAdminNode treeNode) public override async Task UpdateAsync(LinkAdminNode treeNode, IUpdateModel updater) { var model = new LinkAdminNodeViewModel(); - await updater.TryUpdateModelAsync(model, Prefix, x => x.LinkUrl, x => x.LinkText, x => x.IconClass, x => x.SelectedPermissionNames); + await updater.TryUpdateModelAsync(model, Prefix, x => x.LinkUrl, x => x.LinkText, x => x.Target, x => x.IconClass, x => x.SelectedPermissionNames); treeNode.LinkText = model.LinkText; treeNode.LinkUrl = model.LinkUrl; + treeNode.Target = model.Target; treeNode.IconClass = model.IconClass; var selectedPermissions = (model.SelectedPermissionNames == null ? [] : model.SelectedPermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries)); diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeNavigationBuilder.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeNavigationBuilder.cs index 8bd05d0460f..b8f8d3035e6 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeNavigationBuilder.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeNavigationBuilder.cs @@ -59,6 +59,7 @@ public Task BuildNavigationAsync(MenuItem menuItem, NavigationBuilder builder, I // Add the actual link. itemBuilder.Url(nodeLinkUrl); + itemBuilder.Target(node.Target); itemBuilder.Priority(node.Priority); itemBuilder.Position(node.Position); diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeViewModel.cs b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeViewModel.cs index 21564732a86..b2335ee58e7 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/AdminNodes/LinkAdminNodeViewModel.cs @@ -12,6 +12,8 @@ public class LinkAdminNodeViewModel [Required] public string LinkUrl { get; set; } + public string Target { get; set; } + public string IconClass { get; set; } public string SelectedPermissionNames { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Views/Items/LinkAdminNode.Fields.TreeEdit.cshtml b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Views/Items/LinkAdminNode.Fields.TreeEdit.cshtml index 1b12979744b..dd5c12bac83 100644 --- a/src/OrchardCore.Modules/OrchardCore.AdminMenu/Views/Items/LinkAdminNode.Fields.TreeEdit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.AdminMenu/Views/Items/LinkAdminNode.Fields.TreeEdit.cshtml @@ -24,6 +24,18 @@ @T["The url of the link. A link will be shown only if it or one of their children have a url. The url will be relative to the root of the admin site"] +
+ + + + + + + + + @T["The target attribute of the A tag, see more:"] target +
+
diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs index 9d52962dbf4..56710193123 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/HtmlMenuItemPartDisplayDriver.cs @@ -67,6 +67,7 @@ public override IDisplayResult Edit(HtmlMenuItemPart part) { model.Name = part.ContentItem.DisplayText; model.Url = part.Url; + model.Target = part.Target; model.Html = part.Html; model.MenuItemPart = part; }); @@ -81,6 +82,7 @@ public override async Task UpdateAsync(HtmlMenuItemPart part, IU part.ContentItem.DisplayText = model.Name; part.Html = settings.SanitizeHtml ? _htmlSanitizerService.Sanitize(model.Html) : model.Html; part.Url = model.Url; + part.Target = model.Target; var urlToValidate = part.Url; diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs index 7902719c617..b23aadc5cc3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Drivers/LinkMenuItemPartDisplayDriver.cs @@ -59,6 +59,7 @@ public override IDisplayResult Edit(LinkMenuItemPart part) { model.Name = part.ContentItem.DisplayText; model.Url = part.Url; + model.Target = part.Target; model.MenuItemPart = part; }); } @@ -70,6 +71,7 @@ public override async Task UpdateAsync(LinkMenuItemPart part, IU await updater.TryUpdateModelAsync(model, Prefix); part.Url = model.Url; + part.Target = model.Target; part.ContentItem.DisplayText = model.Name; var urlToValidate = part.Url; diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/HtmlMenuItemQueryObjectType.cs b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/HtmlMenuItemQueryObjectType.cs index 84f06d541ac..bd57ae30466 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/HtmlMenuItemQueryObjectType.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/GraphQL/HtmlMenuItemQueryObjectType.cs @@ -10,6 +10,7 @@ public HtmlMenuItemQueryObjectType() Name = "HtmlMenuItemPart"; Field(x => x.Url, nullable: true); + Field(x => x.Target, nullable: true); Field(x => x.Html, nullable: true); } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Models/HtmlMenuItemPart.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Models/HtmlMenuItemPart.cs index 3065e60b50b..cabd9772546 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Models/HtmlMenuItemPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Models/HtmlMenuItemPart.cs @@ -9,6 +9,11 @@ public class HtmlMenuItemPart : ContentPart /// public string Url { get; set; } + /// + /// The target of the link to create. + /// + public string Target { get; set; } + /// /// The raw html to display for this link. /// diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Models/LinkMenuItemPart.cs b/src/OrchardCore.Modules/OrchardCore.Menu/Models/LinkMenuItemPart.cs index 90d042abf62..ad255bc864b 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Models/LinkMenuItemPart.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Models/LinkMenuItemPart.cs @@ -8,5 +8,10 @@ public class LinkMenuItemPart : ContentPart /// The url of the link to create. /// public string Url { get; set; } + + /// + /// The target of the link to create. + /// + public string Target { get; set; } } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartEditViewModel.cs index 37f22c337b7..3a24a2c42f3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/HtmlMenuItemPartEditViewModel.cs @@ -9,6 +9,8 @@ public class HtmlMenuItemPartEditViewModel public string Url { get; set; } + public string Target { get; set; } + public string Html { get; set; } [BindNever] diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/LinkMenuItemPartEditViewModel.cs b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/LinkMenuItemPartEditViewModel.cs index 1848b526e77..301701c5b93 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/LinkMenuItemPartEditViewModel.cs +++ b/src/OrchardCore.Modules/OrchardCore.Menu/ViewModels/LinkMenuItemPartEditViewModel.cs @@ -9,6 +9,8 @@ public class LinkMenuItemPartEditViewModel public string Url { get; set; } + public string Target { get; set; } + [BindNever] public LinkMenuItemPart MenuItemPart { get; set; } } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Views/HtmlMenuItemPart.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Menu/Views/HtmlMenuItemPart.Edit.cshtml index 500faad52db..6c897b4c8b3 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Views/HtmlMenuItemPart.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Views/HtmlMenuItemPart.Edit.cshtml @@ -15,6 +15,20 @@
+
+ +
+ + + + + + + + @T["The target attribute of the A tag, see more:"] target +
+
+
diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Views/LinkMenuItemPart.Edit.cshtml b/src/OrchardCore.Modules/OrchardCore.Menu/Views/LinkMenuItemPart.Edit.cshtml index 473a1c549bd..92f6ed71005 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Views/LinkMenuItemPart.Edit.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Views/LinkMenuItemPart.Edit.cshtml @@ -14,3 +14,17 @@
+ +
+ +
+ + + + + + + + @T["The target attribute of the A tag, see more:"] target +
+
diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink-HtmlMenuItem.cshtml b/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink-HtmlMenuItem.cshtml index 0efbecc2b4b..17ff3aa36b7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink-HtmlMenuItem.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink-HtmlMenuItem.cshtml @@ -17,6 +17,12 @@ } tag.Attributes["href"] = url; + + if (!string.IsNullOrEmpty(htmlMenuItemPart.Target)) + { + tag.Attributes["target"] = htmlMenuItemPart.Target; + } + tag.InnerHtml.AppendHtml(Html.Raw(htmlMenuItemPart.Html)); } @tag diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink-LinkMenuItem.cshtml b/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink-LinkMenuItem.cshtml index b46b647cf0c..fe75f0004e6 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink-LinkMenuItem.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink-LinkMenuItem.cshtml @@ -16,6 +16,7 @@ url = Url.Content(linkMenuItemPart.Url); } + tag.Attributes["target"] = linkMenuItemPart.Target; tag.Attributes["href"] = url; tag.InnerHtml.Append(contentItem.DisplayText); } diff --git a/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink.cshtml b/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink.cshtml index b6ad29baba5..924cc3c7294 100644 --- a/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Menu/Views/MenuItemLink.cshtml @@ -1 +1 @@ -@Model.Text +@Model.Text diff --git a/src/OrchardCore.Modules/OrchardCore.Navigation/Views/NavigationItemLink.cshtml b/src/OrchardCore.Modules/OrchardCore.Navigation/Views/NavigationItemLink.cshtml index 7eb3e808daa..651d04e24b2 100644 --- a/src/OrchardCore.Modules/OrchardCore.Navigation/Views/NavigationItemLink.cshtml +++ b/src/OrchardCore.Modules/OrchardCore.Navigation/Views/NavigationItemLink.cshtml @@ -5,4 +5,4 @@ Model.Metadata.Alternates.Add("NavigationItemText_Id__" + Model.Id); } -@await DisplayAsync(Model) +@await DisplayAsync(Model) diff --git a/src/OrchardCore.Themes/TheAdmin/Views/NavigationItemLink-admin.cshtml b/src/OrchardCore.Themes/TheAdmin/Views/NavigationItemLink-admin.cshtml index 090ef8c99d9..08ac661787d 100644 --- a/src/OrchardCore.Themes/TheAdmin/Views/NavigationItemLink-admin.cshtml +++ b/src/OrchardCore.Themes/TheAdmin/Views/NavigationItemLink-admin.cshtml @@ -22,6 +22,11 @@ tag.Attributes["href"] = Model.Href; } + if (!string.IsNullOrEmpty(Model.Target)) + { + tag.Attributes["target"] = Model.Target; + } + // Extract classes that are not icons from 'Model.Classes'. var notIconClasses = ((IEnumerable)Model.Classes) .Where(c => !c.StartsWith(NavigationConstants.CssClassPrefix, StringComparison.OrdinalIgnoreCase)) diff --git a/src/OrchardCore.Themes/TheAgencyTheme/Views/MenuItemLink-HtmlMenuItem.liquid b/src/OrchardCore.Themes/TheAgencyTheme/Views/MenuItemLink-HtmlMenuItem.liquid index b9f0c3672f4..a05e932748e 100644 --- a/src/OrchardCore.Themes/TheAgencyTheme/Views/MenuItemLink-HtmlMenuItem.liquid +++ b/src/OrchardCore.Themes/TheAgencyTheme/Views/MenuItemLink-HtmlMenuItem.liquid @@ -1,2 +1,2 @@ {% assign link = Model.ContentItem.Content.HtmlMenuItemPart %} -{{ link.Html | raw }} \ No newline at end of file +{{ link.Html | raw }} diff --git a/src/OrchardCore.Themes/TheAgencyTheme/Views/MenuItemLink-LinkMenuItem.liquid b/src/OrchardCore.Themes/TheAgencyTheme/Views/MenuItemLink-LinkMenuItem.liquid index f1ed48b2c57..19f41cfa9d6 100644 --- a/src/OrchardCore.Themes/TheAgencyTheme/Views/MenuItemLink-LinkMenuItem.liquid +++ b/src/OrchardCore.Themes/TheAgencyTheme/Views/MenuItemLink-LinkMenuItem.liquid @@ -1,5 +1,5 @@ {% assign link = Model.ContentItem.Content.LinkMenuItemPart %} - diff --git a/src/OrchardCore.Themes/TheBlogTheme/Views/MenuItemLink-HtmlMenuItem.liquid b/src/OrchardCore.Themes/TheBlogTheme/Views/MenuItemLink-HtmlMenuItem.liquid index 296c2e1b9cb..e69aaf13cdb 100644 --- a/src/OrchardCore.Themes/TheBlogTheme/Views/MenuItemLink-HtmlMenuItem.liquid +++ b/src/OrchardCore.Themes/TheBlogTheme/Views/MenuItemLink-HtmlMenuItem.liquid @@ -3,5 +3,5 @@ {% if Model.HasItems %} {{ link.Html | raw }} {% else %} - {{ link.Html | raw }} -{% endif %} \ No newline at end of file + {{ link.Html | raw }} +{% endif %} diff --git a/src/OrchardCore.Themes/TheBlogTheme/Views/MenuItemLink-LinkMenuItem.liquid b/src/OrchardCore.Themes/TheBlogTheme/Views/MenuItemLink-LinkMenuItem.liquid index c52a9fc051a..53bad68c116 100644 --- a/src/OrchardCore.Themes/TheBlogTheme/Views/MenuItemLink-LinkMenuItem.liquid +++ b/src/OrchardCore.Themes/TheBlogTheme/Views/MenuItemLink-LinkMenuItem.liquid @@ -3,5 +3,5 @@ {% if Model.HasItems %} {{ Model.ContentItem.DisplayText }} {% else %} - {{ Model.ContentItem.DisplayText }} + {{ Model.ContentItem.DisplayText }} {% endif %} diff --git a/src/OrchardCore.Themes/TheTheme/Views/MenuItemLink-HtmlMenuItem.cshtml b/src/OrchardCore.Themes/TheTheme/Views/MenuItemLink-HtmlMenuItem.cshtml index 89499bd7631..c9db308828c 100644 --- a/src/OrchardCore.Themes/TheTheme/Views/MenuItemLink-HtmlMenuItem.cshtml +++ b/src/OrchardCore.Themes/TheTheme/Views/MenuItemLink-HtmlMenuItem.cshtml @@ -18,6 +18,12 @@ } tag.Attributes["href"] = url; + + if (!string.IsNullOrEmpty(htmlMenuItemPart.Target)) + { + tag.Attributes["target"] = htmlMenuItemPart.Target; + } + tag.InnerHtml.AppendHtml(Html.Raw(htmlMenuItemPart.Html)); if (Model.Level == 0 && Model.HasItems) diff --git a/src/OrchardCore/OrchardCore.Navigation.Core/MenuItem.cs b/src/OrchardCore/OrchardCore.Navigation.Core/MenuItem.cs index d5d28575d32..28e1f39d783 100644 --- a/src/OrchardCore/OrchardCore.Navigation.Core/MenuItem.cs +++ b/src/OrchardCore/OrchardCore.Navigation.Core/MenuItem.cs @@ -36,6 +36,11 @@ public MenuItem() /// public string Href { get; set; } + /// + /// The html target of the menu item. + /// + public string Target { get; set; } + /// /// The optional url the menu item should link to. /// diff --git a/src/OrchardCore/OrchardCore.Navigation.Core/NavigationHelper.cs b/src/OrchardCore/OrchardCore.Navigation.Core/NavigationHelper.cs index 598341f1726..b28f1e468c7 100644 --- a/src/OrchardCore/OrchardCore.Navigation.Core/NavigationHelper.cs +++ b/src/OrchardCore/OrchardCore.Navigation.Core/NavigationHelper.cs @@ -68,6 +68,7 @@ private static async Task BuildMenuItemShapeAsync(dynamic shapeFactory, var menuItemShape = (await shapeFactory.NavigationItem()) .Text(menuItem.Text) .Href(menuItem.Href) + .Target(menuItem.Target) .Url(menuItem.Url) .LinkToFirstChild(menuItem.LinkToFirstChild) .RouteValues(menuItem.RouteValues) diff --git a/src/OrchardCore/OrchardCore.Navigation.Core/NavigationItemBuilder.cs b/src/OrchardCore/OrchardCore.Navigation.Core/NavigationItemBuilder.cs index 22b47ca9ce2..25c2b0f959a 100644 --- a/src/OrchardCore/OrchardCore.Navigation.Core/NavigationItemBuilder.cs +++ b/src/OrchardCore/OrchardCore.Navigation.Core/NavigationItemBuilder.cs @@ -46,6 +46,13 @@ public NavigationItemBuilder Url(string url) return this; } + public NavigationItemBuilder Target(string target) + { + _item.Target = target; + + return this; + } + public NavigationItemBuilder Culture(string culture) { _item.Culture = culture; diff --git a/src/OrchardCore/OrchardCore.Navigation.Core/NavigationManager.cs b/src/OrchardCore/OrchardCore.Navigation.Core/NavigationManager.cs index a810d5767cb..d1ebefa9d7e 100644 --- a/src/OrchardCore/OrchardCore.Navigation.Core/NavigationManager.cs +++ b/src/OrchardCore/OrchardCore.Navigation.Core/NavigationManager.cs @@ -110,6 +110,7 @@ private static void Merge(List items) source.RouteValues = cursor.RouteValues; source.Text = cursor.Text; source.Url = cursor.Url; + source.Target = cursor.Target; source.Permissions.Clear(); source.Permissions.AddRange(cursor.Permissions); @@ -133,6 +134,7 @@ private static void Merge(List items) source.RouteValues = cursor.RouteValues; source.Text = cursor.Text; source.Url = cursor.Url; + source.Target = cursor.Target; source.Permissions.Clear(); source.Permissions.AddRange(cursor.Permissions); diff --git a/src/docs/releases/2.0.0.md b/src/docs/releases/2.0.0.md index 2ae05224a01..2894c957ca5 100644 --- a/src/docs/releases/2.0.0.md +++ b/src/docs/releases/2.0.0.md @@ -310,6 +310,10 @@ The `OrchardCore.Email` module has undergone a refactoring process with no break - If you were using the `OrchardCore_Email` configuration key to set up the SMTP provider for all tenants, please update the configuration key to `OrchardCore_Email_Smtp`. The `OrchardCore_Email` key continues to work but will be deprecated in a future release. - A new email provider was added to allow you to send email using Azure Communication Services Email. Click [here](../reference/modules/Email.Azure/README.md) to read more about the ACS module. +### Menu + +`Menus` and `AdminMenus` now support specifying the target property. + ### Admin Menu The admin menu has undergone performance enhancements, and new helpers have been added. When incorporating `INavigationProvider` in your project, you can now utilize `NavigationHelper.IsAdminMenu(name)` instead of the previous approach using `string.Equals(name, "admin", StringComparison.OrdinalIgnoreCase)`. Moreover, when passing route values to an action, it is advised to store them in a constant variable. An illustrative example is provided below.