Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
namespace Dnn.PersonaBar.Library.Containers
Expand Down Expand Up @@ -32,6 +32,9 @@ namespace Dnn.PersonaBar.Library.Containers

public class PersonaBarContainer : IPersonaBarContainer
{
/// <summary>The query string key that names a Persona Bar item to open when the page loads.</summary>
private const string RequestedPanelQueryKey = "personabar";

private static IPersonaBarContainer instance;
private readonly IPersonaBarController personaBarController;
private readonly IHostSettings hostSettings;
Expand Down Expand Up @@ -104,6 +107,46 @@ public virtual void FilterMenu(PersonaBarMenu menu)
{
}

/// <summary>
/// Gets the identifier of the Persona Bar item requested in the query string (e.g. <c>?personabar=Dnn.Pages</c>),
/// only if it is a panel in the current user's menu, so the URL can never open anything the user cannot already open.
/// </summary>
/// <param name="menu">The current user's menu.</param>
/// <returns>The matching menu item identifier, or <see langword="null"/>.</returns>
private static string GetRequestedPanel(PersonaBarMenu menu)
{
var requested = HttpContextSource.Current?.Request.QueryString[RequestedPanelQueryKey];
if (string.IsNullOrWhiteSpace(requested))
{
return null;
}

return FindPanelIdentifier(menu.MenuItems, requested.Trim());
}

private static string FindPanelIdentifier(IEnumerable<MenuItem> menuItems, string identifier)
{
foreach (var menuItem in menuItems)
{
// Only items that open a panel: root menus (Content, Manage, ...) have no Path, and items with a Link navigate away instead.
if (string.Equals(menuItem.Identifier, identifier, StringComparison.OrdinalIgnoreCase)
&& !string.IsNullOrEmpty(menuItem.ModuleName)
&& !string.IsNullOrEmpty(menuItem.Path)
&& string.IsNullOrEmpty(menuItem.Link))
{
return menuItem.Identifier;
}

var childIdentifier = FindPanelIdentifier(menuItem.Children, identifier);
if (childIdentifier != null)
{
return childIdentifier;
}
}

return null;
}

private Dictionary<string, object> GetConfiguration(PortalSettings portalSettings)
{
var settings = new Dictionary<string, object>();
Expand All @@ -123,6 +166,13 @@ private Dictionary<string, object> GetConfiguration(PortalSettings portalSetting
settings.Add("userMode", Personalization.GetUserMode().ToString());
settings.Add("userSettings", PersonaBarUserSettingsController.Instance.GetPersonaBarUserSettings());
settings.Add("menuStructure", JObject.FromObject(menuStructure));

var requestedPanel = GetRequestedPanel(menuStructure);
if (requestedPanel != null)
{
settings.Add("requestedPanel", requestedPanel);
}

settings.Add("sku", DotNetNukeContext.Current.Application.SKU);
settings.Add("debugMode", HttpContextSource.Current != null && HttpContextSource.Current.IsDebuggingEnabled);
settings.Add("portalId", portalId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,16 @@ span.dnnFormError:after {
margin-top: 0;
}

/* Menu item links (for opening items in a new tab) look exactly like the item text. */
.hovermenu > ul > li > a.hovermenu-link,
.hovermenu > ul > li > a.hovermenu-link:hover,
.hovermenu > ul > li > a.hovermenu-link:focus,
.hovermenu > ul > li > a.hovermenu-link:visited {
color: inherit;
cursor: inherit;
text-decoration: none;
}

.hovermenu > ul > li.disabled {
cursor: default;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>PersonaBar</title>
Expand Down Expand Up @@ -56,7 +56,6 @@
<!-- ko foreach: { data: menuItem.menuItems, as: 'columnItem' } -->
<ul data-bind="foreach: { data: columnItem, as: 'subMenuItem' }">
<li data-bind="
html: subMenuItem.displayName,
attr: {
'id': subMenuItem.id,
'data-path': subMenuItem.path,
Expand All @@ -67,7 +66,12 @@
'class': (subMenuItem.css || ''),
'href': subMenuItem.link,
'target': subMenuItem.link ? '_blank' : ''
}"></li>
}"><a class="hovermenu-link" data-bind="
html: subMenuItem.displayName,
attr: {
'href': subMenuItem.href,
'target': subMenuItem.link ? '_blank' : '_top'
}"></a></li>
</ul>
<!-- /ko -->
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,19 @@ require(['jquery', 'knockout', 'dayjs', '../util', '../sf', '../config', './../e
return false;
}

function removeRequestedPanelFromUrl() {
// Keep the address bar clean once the requested panel is open; later reloads restore panels as usual.
try {
var url = new URL(window.top.location.href);
if (url.searchParams.has('personabar')) {
url.searchParams.delete('personabar');
window.top.history.replaceState(window.top.history.state, '', url.toString());
}
} catch (e) {
// Leave the URL as it is.
}
}

function calculateHoverMenuPosition($menu) {
if ($menu.length === 0) {
return;
Expand Down Expand Up @@ -868,6 +881,12 @@ require(['jquery', 'knockout', 'dayjs', '../util', '../sf', '../config', './../e
});

$(".btn_panel, .hovermenu > ul > li").click(function handleClickOnHoverMenuItem(evt) {
// Ctrl/Cmd/Shift-click on an item's link: let the browser open it in a new tab or window.
if ((evt.ctrlKey || evt.metaKey || evt.shiftKey) && $(evt.target).closest('a.hovermenu-link[href]').length) {
evt.stopPropagation();
return;
}

evt.preventDefault();
evt.stopPropagation();

Expand Down Expand Up @@ -1069,6 +1088,9 @@ require(['jquery', 'knockout', 'dayjs', '../util', '../sf', '../config', './../e
})();
} else {
$(".personabarnav > li.btn_panel, .hovermenu > ul > li").addClass("disabled");
$(".hovermenu a.hovermenu-link").click(function (e) {
e.preventDefault();
});
}

(function setupEditButton() {
Expand Down Expand Up @@ -1179,6 +1201,14 @@ require(['jquery', 'knockout', 'dayjs', '../util', '../sf', '../config', './../e
return;
}

// A page opened from a Persona Bar link (?personabar=<identifier>) opens that item.
// The server only passes identifiers of panels in the current user's menu.
if (config.visible && config.requestedPanel) {
util.loadPanel(config.requestedPanel, {});
removeRequestedPanelFromUrl();
return;
}

var settings = util.persistent.load();
if (settings.expandPersonaBar && settings.activeIdentifier) {
var identifier = settings.activeIdentifier;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,41 @@ define(['jquery', 'purify.min'], function ($, DOMPurify) {
getPanelIdFromPath: function getPanelIdFromPath(path) {
return path + '-panel';
},

/**
* Builds a URL of the current page that opens the given Persona Bar item when loaded,
* so the item can be opened in a new browser tab.
*
* @method getPanelUrl
* @param {String} identifier the menu item identifier, e.g. Dnn.Pages
* @return {String} the URL, or null if it cannot be built
*/
getPanelUrl: function getPanelUrl(identifier) {
try {
var url = new URL(window.top.location.href);
url.searchParams.set('personabar', identifier);
url.hash = '';
return url.toString();
} catch (e) {
return null;
}
},

/**
* Gets the URL a menu item's link points to: its own Link (resolved like checkMenuLink in main.js),
* or a URL of the current page that opens its panel.
*
* @method getMenuItemUrl
* @param {Object} menuItem the menu item from the menu structure
* @return {String} the URL, or null if the item has neither a link nor a panel
*/
getMenuItemUrl: function getMenuItemUrl(menuItem) {
if (menuItem.Link) {
return menuItem.Link.indexOf('://') > -1 ? menuItem.Link : config.siteRoot + menuItem.Link;
}

return menuItem.ModuleName && menuItem.Path ? this.getPanelUrl(menuItem.Identifier) : null;
},
parseQueryParameter: function (item) {
item.Query = '';
var pathInfo;
Expand Down Expand Up @@ -378,7 +413,8 @@ define(['jquery', 'purify.min'], function ($, DOMPurify) {
css: menuItem.CssClass,
icon: menuItem.IconFile,
displayName: menuItem.DisplayName,
settings: menuItem.Settings
settings: menuItem.Settings,
href: util.getMenuItemUrl(menuItem)
}
topMenuItem.menuItems.push(subMenuItem);
});
Expand Down
Loading