/// <summary> /// Helper method to create a root model for a tree /// </summary> /// <returns></returns> protected virtual TreeNode CreateRootNode(FormCollection queryStrings) { var jsonUrl = Url.GetTreeUrl(GetType(), RootNodeId, queryStrings); var isDialog = queryStrings.GetValue <bool>(TreeQueryStringParameters.DialogMode); var node = new TreeNode(RootNodeId, BackOfficeRequestContext.RegisteredComponents.MenuItems, jsonUrl) { HasChildren = true, EditorUrl = queryStrings.HasKey(TreeQueryStringParameters.OnNodeClick) //has a node click handler? ? queryStrings.Get(TreeQueryStringParameters.OnNodeClick) //return node click handler : isDialog //is in dialog mode without a click handler ? ? "#" //return empty string, otherwise, return an editor URL: : Url.GetCurrentDashboardUrl(), Title = NodeDisplayName }; //add the tree id to the root node.AdditionalData.Add("treeId", TreeId.ToString("N")); //add the tree-root css class node.Style.AddCustom("tree-root"); //node.AdditionalData.Add("id", node.HiveId.ToString()); //node.AdditionalData.Add("title", node.Title); AddQueryStringsToAdditionalData(node, queryStrings); //check if the tree is searchable and add that to the meta data as well if (this is ISearchableTree) { node.AdditionalData.Add("searchable", "true"); } return(node); }
/// <summary> /// Returns the value of a mandatory item in the FormCollection /// </summary> /// <param name="items"></param> /// <param name="key"></param> /// <returns></returns> public static string GetRequiredString(this FormCollection items, string key) { if (items.HasKey(key) == false) { throw new ArgumentNullException("The " + key + " query string parameter was not found but is required"); } return(items.Single(x => x.Key.InvariantEquals(key)).Value); }
/// <summary> /// Return the editor URL for the currrent node depending on the data found in the query strings /// </summary> /// <param name="id"></param> /// <param name="queryStrings"></param> /// <returns></returns> protected virtual string GetEditorUrl(HiveId id, FormCollection queryStrings) { var isDialog = queryStrings.GetValue <bool>(TreeQueryStringParameters.DialogMode); return(queryStrings.HasKey(TreeQueryStringParameters.OnNodeClick) //has a node click handler? ? queryStrings.Get(TreeQueryStringParameters.OnNodeClick) //return node click handler : isDialog //is in dialog mode without a click handler ? ? string.Empty //return empty string, otherwise, return an editor URL: : Url.GetEditorUrl(id, EditorControllerId, BackOfficeRequestContext.RegisteredComponents, BackOfficeRequestContext.Application.Settings)); }
/// <summary> /// Ensures the recycle bin is appended when required (i.e. user has access to the root and it's not in dialog mode) /// </summary> /// <param name="id"></param> /// <param name="queryStrings"></param> /// <returns></returns> /// <remarks> /// This method is overwritten strictly to render the recycle bin, it should serve no other purpose /// </remarks> protected sealed override ActionResult <TreeNodeCollection> GetTreeNodes(string id, FormCollection queryStrings) { //check if we're rendering the root if (id == Constants.System.RootString && UserStartNodes.Contains(Constants.System.Root)) { var altStartId = string.Empty; if (queryStrings.HasKey(TreeQueryStringParameters.StartNodeId)) { altStartId = queryStrings.GetValue <string>(TreeQueryStringParameters.StartNodeId); } //check if a request has been made to render from a specific start node if (string.IsNullOrEmpty(altStartId) == false && altStartId != "undefined" && altStartId != Constants.System.RootString) { id = altStartId; } var nodesResult = GetTreeNodesInternal(id, queryStrings); if (!(nodesResult.Result is null)) { return(nodesResult.Result); } var nodes = nodesResult.Value; //only render the recycle bin if we are not in dialog and the start id is still the root //we need to check for the "application" key in the queryString because its value is required here, //and for some reason when there are no dashboards, this parameter is missing if (IsDialog(queryStrings) == false && id == Constants.System.RootString && queryStrings.HasKey("application")) { nodes.Add(CreateTreeNode( RecycleBinId.ToInvariantString(), id, queryStrings, LocalizedTextService.Localize("general", "recycleBin"), "icon-trash", RecycleBinSmells, queryStrings.GetRequiredValue <string>("application") + TreeAlias.EnsureStartsWith('/') + "/recyclebin")); } return(nodes); } return(GetTreeNodesInternal(id, queryStrings)); }
/// <summary> /// Return the editor URL for the currrent node depending on the data found in the query strings /// </summary> /// <param name="id"></param> /// <param name="queryStrings"></param> /// <returns></returns> /// <remarks> /// This checks if the tree there is a OnNodeClick handler assigned, if so, it assigns it, /// otherwise it checks if the tree is in DialogMode, if it is then it returns an empty handler, otherwise /// it sets the Url to the editor's url. /// </remarks> public string GetEditorUrl(HiveId id, FormCollection queryStrings) { Mandate.ParameterNotEmpty(id, "id"); Mandate.ParameterNotNull(queryStrings, "queryStrings"); Mandate.That <NullReferenceException>(Url != null); var isDialog = queryStrings.GetValue <bool>(TreeQueryStringParameters.DialogMode); return(queryStrings.HasKey(TreeQueryStringParameters.OnNodeClick) //has a node click handler? ? queryStrings.Get(TreeQueryStringParameters.OnNodeClick) //return node click handler : isDialog //is in dialog mode without a click handler ? ? "#" //return empty string, otherwise, return an editor URL: : Url.GetEditorUrl( id, EditorControllerId, BackOfficeRequestContext.RegisteredComponents, BackOfficeRequestContext.Application.Settings)); }
protected virtual ActionResult <TreeNodeCollection> PerformGetTreeNodes(string id, FormCollection queryStrings) { var nodes = new TreeNodeCollection(); var rootIdString = Constants.System.RootString; var hasAccessToRoot = UserStartNodes.Contains(Constants.System.Root); var startNodeId = queryStrings.HasKey(TreeQueryStringParameters.StartNodeId) ? queryStrings.GetValue <string>(TreeQueryStringParameters.StartNodeId) : string.Empty; var ignoreUserStartNodes = IgnoreUserStartNodes(queryStrings); if (string.IsNullOrEmpty(startNodeId) == false && startNodeId != "undefined" && startNodeId != rootIdString) { // request has been made to render from a specific, non-root, start node id = startNodeId; // ensure that the user has access to that node, otherwise return the empty tree nodes collection // TODO: in the future we could return a validation statement so we can have some UI to notify the user they don't have access if (ignoreUserStartNodes == false && HasPathAccess(id, queryStrings) == false) { _logger.LogWarning("User {Username} does not have access to node with id {Id}", _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Username, id); return(nodes); } // if the tree is rendered... // - in a dialog: render only the children of the specific start node, nothing to do // - in a section: if the current user's start nodes do not contain the root node, we need // to include these start nodes in the tree too, to provide some context - i.e. change // start node back to root node, and then GetChildEntities method will take care of the rest. if (IsDialog(queryStrings) == false && hasAccessToRoot == false) { id = rootIdString; } } // get child entities - if id is root, but user's start nodes do not contain the // root node, this returns the start nodes instead of root's children var entitiesResult = GetChildEntities(id, queryStrings); if (!(entitiesResult.Result is null)) { return(entitiesResult.Result); } var entities = entitiesResult.Value.ToList(); //get the current user start node/paths GetUserStartNodes(out var userStartNodes, out var userStartNodePaths); // if the user does not have access to the root node, what we have is the start nodes, // but to provide some context we need to add their topmost nodes when they are not // topmost nodes themselves (level > 1). if (id == rootIdString && hasAccessToRoot == false) { // first add the entities that are topmost to the nodes collection var topMostEntities = entities.Where(x => x.Level == 1).ToArray(); nodes.AddRange(topMostEntities.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths)).Where(x => x != null)); // now add the topmost nodes of the entities that aren't topmost to the nodes collection as well // - these will appear as "no-access" nodes in the tree, but will allow the editors to drill down through the tree // until they reach their start nodes var topNodeIds = entities.Except(topMostEntities).Select(GetTopNodeId).Where(x => x != 0).Distinct().ToArray(); if (topNodeIds.Length > 0) { var topNodes = _entityService.GetAll(UmbracoObjectType, topNodeIds.ToArray()); nodes.AddRange(topNodes.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths)).Where(x => x != null)); } } else { // the user has access to the root, just add the entities nodes.AddRange(entities.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths)).Where(x => x != null)); } return(nodes); }