Added new feature : display the file/folder and link of a SharingLink object in the permissions reports.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using SharepointToolbox.Core.Helpers;
|
||||
using SharepointToolbox.Core.Models;
|
||||
using SharepointToolbox.Localization;
|
||||
|
||||
@@ -137,7 +138,10 @@ document.addEventListener('click', function(ev) {
|
||||
string? principalType,
|
||||
IReadOnlyDictionary<string, IReadOnlyList<ResolvedMember>>? groupMembers,
|
||||
int colSpan,
|
||||
ref int grpMemIdx)
|
||||
ref int grpMemIdx,
|
||||
string? targetLabel = null,
|
||||
string? sharingLinkType = null,
|
||||
bool hideSystemGroupRaw = false)
|
||||
{
|
||||
var T = TranslationSource.Instance;
|
||||
var logins = userLogins.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
||||
@@ -151,14 +155,40 @@ document.addEventListener('click', function(ev) {
|
||||
var name = i < names.Length ? names[i].Trim() : login;
|
||||
var isExt = login.Contains("#EXT#", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
bool isExpandable = principalType == "SharePointGroup"
|
||||
// When the principal is a resolved system group and the user wants the raw
|
||||
// name hidden, replace the pill's visible text with the link-type badge
|
||||
// (sharing links) and/or the target label. Falls back to the raw name when
|
||||
// resolution failed (no targetLabel).
|
||||
var classification = principalType == "SharePointGroup"
|
||||
? PermissionEntryHelper.Classify(name)
|
||||
: new SystemGroupClassification(SystemGroupKind.None, null, null, null, null, null);
|
||||
bool isResolvedSystemGroup = hideSystemGroupRaw
|
||||
&& classification.Kind != SystemGroupKind.None
|
||||
&& classification.Kind != SystemGroupKind.LimitedAccessBare
|
||||
&& !string.IsNullOrEmpty(targetLabel);
|
||||
|
||||
bool hasResolvedMembers = principalType == "SharePointGroup"
|
||||
&& groupMembers != null
|
||||
&& groupMembers.TryGetValue(name, out _);
|
||||
|
||||
if (isExpandable && groupMembers != null && groupMembers.TryGetValue(name, out var resolved))
|
||||
if (hasResolvedMembers && groupMembers!.TryGetValue(name, out var resolved))
|
||||
{
|
||||
var grpId = $"grpmem{grpMemIdx}";
|
||||
pills.Append($"<span class=\"user-pill group-expandable\" data-group-target=\"{HtmlEncode(grpId)}\" data-email=\"{HtmlEncode(login)}\">{HtmlEncode(name)} ▼</span>");
|
||||
pills.Append("<span class=\"user-pill group-expandable\"");
|
||||
if (isResolvedSystemGroup)
|
||||
pills.Append(" data-system-group=\"1\"");
|
||||
pills.Append($" data-group-target=\"{HtmlEncode(grpId)}\" data-email=\"{HtmlEncode(login)}\">");
|
||||
if (isResolvedSystemGroup)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sharingLinkType))
|
||||
pills.Append(BuildSharingLinkBadge(sharingLinkType!));
|
||||
pills.Append(HtmlEncode(targetLabel!));
|
||||
}
|
||||
else
|
||||
{
|
||||
pills.Append(HtmlEncode(name));
|
||||
}
|
||||
pills.Append(" ▼</span>");
|
||||
|
||||
string memberContent;
|
||||
if (resolved.Count > 0)
|
||||
@@ -173,6 +203,14 @@ document.addEventListener('click', function(ev) {
|
||||
subRows.AppendLine($"<tr data-group=\"{HtmlEncode(grpId)}\" style=\"display:none\"><td colspan=\"{colSpan}\" style=\"padding-left:2em;font-size:.8rem;color:#555\">{memberContent}</td></tr>");
|
||||
grpMemIdx++;
|
||||
}
|
||||
else if (isResolvedSystemGroup)
|
||||
{
|
||||
pills.Append("<span class=\"user-pill\" data-system-group=\"1\">");
|
||||
if (!string.IsNullOrEmpty(sharingLinkType))
|
||||
pills.Append(BuildSharingLinkBadge(sharingLinkType!));
|
||||
pills.Append(HtmlEncode(targetLabel!));
|
||||
pills.Append("</span>");
|
||||
}
|
||||
else
|
||||
{
|
||||
var cls = isExt ? "user-pill external-user" : "user-pill";
|
||||
@@ -183,6 +221,80 @@ document.addEventListener('click', function(ev) {
|
||||
return (pills.ToString(), subRows.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the Granted Through cell. When the entry carries a resolved system-group
|
||||
/// target (Limited Access For Web/List or SharingLinks), a clickable link to the
|
||||
/// targeted resource is appended on a second line. For sharing links the link type
|
||||
/// (OrganizationEdit / AnonymousView / …) is surfaced alongside the target.
|
||||
///
|
||||
/// When <paramref name="hideSystemGroupRaw"/> is true and a target was resolved, the
|
||||
/// raw "SharePoint Group: SharingLinks.{guid}…" / "Limited Access System Group For
|
||||
/// Web|List {guid}" prefix is suppressed and only the link-type badge + clickable
|
||||
/// target are shown — keeps the report readable without losing information.
|
||||
/// </summary>
|
||||
internal static string BuildGrantedThroughCell(
|
||||
string grantedThrough,
|
||||
string? targetUrl,
|
||||
string? targetLabel,
|
||||
string? sharingLinkType,
|
||||
bool hideSystemGroupRaw = false)
|
||||
{
|
||||
var hasTarget = !string.IsNullOrEmpty(targetUrl) && !string.IsNullOrEmpty(targetLabel);
|
||||
var hasLinkType = !string.IsNullOrEmpty(sharingLinkType);
|
||||
var suppressRaw = hideSystemGroupRaw && hasTarget;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
if (!suppressRaw)
|
||||
sb.Append(HtmlEncode(grantedThrough));
|
||||
|
||||
if (!hasTarget && !hasLinkType)
|
||||
return sb.ToString();
|
||||
|
||||
if (suppressRaw)
|
||||
{
|
||||
// Inline layout — no leading raw text to wrap under.
|
||||
if (hasLinkType)
|
||||
sb.Append(BuildSharingLinkBadge(sharingLinkType!));
|
||||
if (hasTarget)
|
||||
{
|
||||
sb.Append("<a href=\"");
|
||||
sb.Append(HtmlEncode(targetUrl!));
|
||||
sb.Append("\" target=\"_blank\">");
|
||||
sb.Append(HtmlEncode(targetLabel!));
|
||||
sb.Append("</a>");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
sb.Append("<div style=\"margin-top:4px;font-size:.75rem;color:#555\">");
|
||||
if (hasLinkType)
|
||||
sb.Append(BuildSharingLinkBadge(sharingLinkType!));
|
||||
if (hasTarget)
|
||||
{
|
||||
sb.Append("→ <a href=\"");
|
||||
sb.Append(HtmlEncode(targetUrl!));
|
||||
sb.Append("\" target=\"_blank\">");
|
||||
sb.Append(HtmlEncode(targetLabel!));
|
||||
sb.Append("</a>");
|
||||
}
|
||||
sb.Append("</div>");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the colored badge for a SharePoint sharing-link type. Translates the
|
||||
/// raw <c>linkType</c> code (e.g. <c>OrganizationEdit</c>) into a human label
|
||||
/// (e.g. <c>Org link · Edit</c>) and tints by risk tier; raw code surfaces as a
|
||||
/// <c>title</c> tooltip so operators can still trace it back to the source.
|
||||
/// </summary>
|
||||
internal static string BuildSharingLinkBadge(string rawLinkType)
|
||||
{
|
||||
var (label, risk) = SharingLinkLabels.Describe(rawLinkType);
|
||||
var (bg, fg) = SharingLinkLabels.Colors(risk);
|
||||
return $"<span class=\"badge\" style=\"background:{bg};color:{fg};margin-right:6px\" " +
|
||||
$"title=\"{HtmlEncode(rawLinkType)}\">{HtmlEncode(label)}</span>";
|
||||
}
|
||||
|
||||
/// <summary>Returns the CSS class for the object-type badge.</summary>
|
||||
internal static string ObjectTypeCss(string t) => t switch
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user