diff --git a/src/Skybrud.Umbraco.Redirects/Config/RedirectsContentAppSettings.cs b/src/Skybrud.Umbraco.Redirects/Config/RedirectsContentAppSettings.cs index 5a4797b..e8b6cf6 100644 --- a/src/Skybrud.Umbraco.Redirects/Config/RedirectsContentAppSettings.cs +++ b/src/Skybrud.Umbraco.Redirects/Config/RedirectsContentAppSettings.cs @@ -13,6 +13,10 @@ public class RedirectsContentAppSettings { public bool Enabled { get; set; } = true; /// + /// Gets or sets whether the user's start nodes should filter which redirects they have access to. Default is . + /// + public bool UseStartNodes { get; set; } = false; + /// Gets or sets a list of content types and media types where the content app should or should not be shown. /// The format follows Umbraco's show option - eg. +content/* enables the content app for all /// content. diff --git a/src/Skybrud.Umbraco.Redirects/Controllers/Api/RedirectsController.cs b/src/Skybrud.Umbraco.Redirects/Controllers/Api/RedirectsController.cs index 808048d..bff63bb 100644 --- a/src/Skybrud.Umbraco.Redirects/Controllers/Api/RedirectsController.cs +++ b/src/Skybrud.Umbraco.Redirects/Controllers/Api/RedirectsController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Net; using Microsoft.AspNetCore.Mvc; @@ -12,12 +13,17 @@ using Skybrud.Umbraco.Redirects.Models.Api; using Skybrud.Umbraco.Redirects.Models.Options; using Skybrud.Umbraco.Redirects.Services; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Web.BackOffice.Controllers; using Umbraco.Cms.Web.Common.Attributes; +using Umbraco.Cms.Web.Common.UmbracoContext; +using Umbraco.Extensions; #pragma warning disable 1591 @@ -61,13 +67,15 @@ public RedirectsController(ILogger logger, IRedirectsServic [HttpGet] public ActionResult GetRootNodes() { - RedirectRootNode[] rootNodes = _redirects.GetRootNodes(); - - return new JsonResult(new { - total = rootNodes.Length, - items = rootNodes.Select(x => new RedirectRootNodeModel(x, _backOffice)) - }); + var rootNodes = _backOffice.Settings.ContentApp.UseStartNodes + ? _redirects.GetRootNodes(_backOffice.CurrentUser) + : _redirects.GetRootNodes(); + return new JsonResult(new + { + total = rootNodes.Length, + items = rootNodes.Select(x => new RedirectRootNodeModel(x, _backOffice)) + }); } [HttpPost] @@ -255,12 +263,15 @@ public ActionResult DeleteRedirect(Guid redirectId) { /// The maximum amount of redirects to be returned per page. /// The type of redirects that should be returned. /// The text that the returned redirects should match. - /// The root node key that the returned redirects should match. null means all redirects. means all global redirects. + /// An array of root node keys that the returned redirects should match. null means all redirects. means all global redirects. /// A list of redirects. [HttpGet] - public ActionResult GetRedirects(int page = 1, int limit = 20, string? type = null, string? text = null, Guid? rootNodeKey = null) { + public ActionResult GetRedirects(int page = 1, int limit = 20, string? type = null, string? text = null, [FromQuery] Guid[]? rootNodeKeys = null) { try { + var rootKeys = _backOffice.Settings.ContentApp.UseStartNodes + ? _redirects.GetRootNodes(_backOffice.CurrentUser!).Select(p => p.Key) + : _redirects.GetRootNodes().Select(p => p.Key); // Initialize the search options RedirectsSearchOptions options = new() { @@ -268,7 +279,7 @@ public ActionResult GetRedirects(int page = 1, int limit = 20, string? type = nu Limit = limit, Type = EnumUtils.ParseEnum(type, RedirectTypeFilter.All), Text = text, - RootNodeKey = rootNodeKey + RootNodeKeys = (rootNodeKeys != null && rootNodeKeys.Any()) ? rootNodeKeys : rootKeys.ToArray() }; // Make the search for redirects via the redirects service @@ -369,7 +380,6 @@ private ActionResult Error(RedirectsException ex) { }; } - } } \ No newline at end of file diff --git a/src/Skybrud.Umbraco.Redirects/Models/RedirectRootNode.cs b/src/Skybrud.Umbraco.Redirects/Models/RedirectRootNode.cs index ab84fce..dc677e6 100644 --- a/src/Skybrud.Umbraco.Redirects/Models/RedirectRootNode.cs +++ b/src/Skybrud.Umbraco.Redirects/Models/RedirectRootNode.cs @@ -36,6 +36,12 @@ public class RedirectRootNode { [JsonProperty("icon")] public string Icon { get; } + /// + /// Gets the path of the root node. + /// + [JsonProperty("path")] + public IEnumerable Path { get; set; } + /// /// Gets the domains asscoiated with the root node. /// @@ -45,6 +51,7 @@ private RedirectRootNode(IContent content, IEnumerable? domains) Id = content.Id; Key = content.Key; Name = content.Name!; + Path = content.Path.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); Icon = content.ContentType.Icon!; Domains = domains?.Select(x => x.Name).ToArray() ?? Array.Empty(); } diff --git a/src/Skybrud.Umbraco.Redirects/Services/IRedirectsService.cs b/src/Skybrud.Umbraco.Redirects/Services/IRedirectsService.cs index 7dc67a2..ca625dd 100644 --- a/src/Skybrud.Umbraco.Redirects/Services/IRedirectsService.cs +++ b/src/Skybrud.Umbraco.Redirects/Services/IRedirectsService.cs @@ -1,9 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; + using Microsoft.AspNetCore.Http; using Skybrud.Umbraco.Redirects.Models; using Skybrud.Umbraco.Redirects.Models.Options; +using Umbraco.Cms.Core.Models.Membership; + namespace Skybrud.Umbraco.Redirects.Services { /// @@ -116,6 +120,35 @@ public interface IRedirectsService { /// An array of representing the root nodes. RedirectRootNode[] GetRootNodes(); + /// + /// Returns an array of all rode nodes configured in Umbraco. + /// + /// An with potential root node access restrictions. + /// An array of representing the root nodes the user has access to. + RedirectRootNode[] GetRootNodes(IUser user) + { + var rootNodes = GetRootNodes(); + HashSet rootNodeIds = new(); + + if (user.StartContentIds != null) + { + foreach (var rootNodeId in user.StartContentIds) + { + rootNodeIds.Add(rootNodeId); + } + } + + foreach (var group in user.Groups) + { + if (group.StartContentId != null) + { + rootNodeIds.Add(group.StartContentId.Value); + } + } + + return rootNodes.Where(rootNode => rootNodeIds.Any(x => rootNode.Path.Contains(x))).ToArray(); + } + /// /// Returns an array of redirects where the destination matches the specified and . /// diff --git a/src/Skybrud.Umbraco.Redirects/Services/RedirectsSearchOptions.cs b/src/Skybrud.Umbraco.Redirects/Services/RedirectsSearchOptions.cs index 68872df..54129b3 100644 --- a/src/Skybrud.Umbraco.Redirects/Services/RedirectsSearchOptions.cs +++ b/src/Skybrud.Umbraco.Redirects/Services/RedirectsSearchOptions.cs @@ -33,12 +33,19 @@ public class RedirectsSearchOptions { /// public string? Text { get; set; } - /// - /// Gets or sets the key the returned redirects should match. indicates all global - /// redirects. Default is null, in which case this filter is disabled. - /// + /// + /// Gets or sets the key the returned redirects should match. indicates all global + /// redirects. Default is null, in which case this filter is disabled. + /// + [Obsolete("Obsoleted in favour of RootNodeKeys, as a user may have access to more than one root node.")] public Guid? RootNodeKey { get; set; } + /// + /// Gets or sets the key the returned redirects should match. indicates all global + /// redirects. Default is null, in which case this filter is disabled. + /// + public Guid[] RootNodeKeys { get; set; } + #endregion #region Constructors diff --git a/src/Skybrud.Umbraco.Redirects/Services/RedirectsService.cs b/src/Skybrud.Umbraco.Redirects/Services/RedirectsService.cs index 33ba602..2514eaf 100644 --- a/src/Skybrud.Umbraco.Redirects/Services/RedirectsService.cs +++ b/src/Skybrud.Umbraco.Redirects/Services/RedirectsService.cs @@ -13,6 +13,7 @@ using Skybrud.Umbraco.Redirects.Models; using Skybrud.Umbraco.Redirects.Models.Dtos; using Skybrud.Umbraco.Redirects.Models.Options; +using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -368,7 +369,11 @@ public RedirectsSearchResult GetRedirects(RedirectsSearchOptions options) { var sql = scope.SqlContext.Sql().Select().From(); // Search by the rootNodeId - if (options.RootNodeKey != null) sql = sql.Where(x => x.RootKey == options.RootNodeKey.Value); + if (options.RootNodeKeys != null && options.RootNodeKeys.Any()) { + sql = sql.Where(x => options.RootNodeKeys.Contains(x.RootKey)); + } else if (options.RootNodeKey != null) { + sql = sql.Where(x => x.RootKey == options.RootNodeKey); + } // Search by the type if (options.Type != RedirectTypeFilter.All) { @@ -397,6 +402,7 @@ public RedirectsSearchResult GetRedirects(RedirectsSearchOptions options) { || (x.Path.Contains(url) && x.QueryString.Contains(query)) )); + } } @@ -460,19 +466,17 @@ public IEnumerable GetAllRedirects() { /// Returns an array of all rode nodes configured in Umbraco. /// /// An array of representing the root nodes. - public RedirectRootNode[] GetRootNodes() { - - // Multiple domains may be configured for a single node, so we need to group the domains before proceeding - var domainsByRootNodeId = GetDomains().GroupBy(x => x.RootNodeId); - - return ( - from domainGroup in domainsByRootNodeId - let content = _contentService.GetById(domainGroup.First().RootNodeId) - where content is { Trashed: false } - orderby content.Id - select RedirectRootNode.GetFromContent(content, domainGroup) - ).ToArray(); - + public RedirectRootNode[] GetRootNodes() + { + var domainsByRootNodeId = GetDomains().GroupBy(x => x.RootNodeId); + + return ( + from domainGroup in domainsByRootNodeId + let content = _contentService.GetById(domainGroup.First().RootNodeId) + where content is { Trashed: false } + orderby content.Id + select RedirectRootNode.GetFromContent(content, domainGroup) + ).ToArray(); } /// diff --git a/src/Skybrud.Umbraco.Redirects/wwwroot/Scripts/Controllers/Dashboards/Default.js b/src/Skybrud.Umbraco.Redirects/wwwroot/Scripts/Controllers/Dashboards/Default.js index 604a4f5..7cbf000 100644 --- a/src/Skybrud.Umbraco.Redirects/wwwroot/Scripts/Controllers/Dashboards/Default.js +++ b/src/Skybrud.Umbraco.Redirects/wwwroot/Scripts/Controllers/Dashboards/Default.js @@ -113,7 +113,7 @@ // Any filters? if (vm.filters.rootNode && vm.filters.rootNode.key) { - args.rootNodeKey = vm.filters.rootNode.key; + args.rootNodeKeys = vm.filters.rootNode.key; vm.activeFilters++; }