/// <summary>
        /// Check to see if we should return children of a container node
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        /// <remarks>
        /// This is required in case a user has custom start nodes that are children of a list view since in that case we'll need to render the tree node. In normal cases we don't render
        /// children of a list view.
        /// </remarks>
        protected bool ShouldRenderChildrenOfContainer(IEntitySlim e)
        {
            var isContainer = e.IsContainer;

            var renderChildren = e.HasChildren && (isContainer == false);

            //Here we need to figure out if the node is a container and if so check if the user has a custom start node, then check if that start node is a child
            // of this container node. If that is true, the HasChildren must be true so that the tree node still renders even though this current node is a container/list view.
            if (isContainer && UserStartNodes.Length > 0 && UserStartNodes.Contains(Constants.System.Root) == false)
            {
                var startNodes = _entityService.GetAll(UmbracoObjectType, UserStartNodes);
                //if any of these start nodes' parent is current, then we need to render children normally so we need to switch some logic and tell
                // the UI that this node does have children and that it isn't a container

                if (startNodes.Any(x =>
                {
                    var pathParts = x.Path.Split(Constants.CharArrays.Comma);
                    return(pathParts.Contains(e.Id.ToInvariantString()));
                }))
                {
                    renderChildren = true;
                }
            }

            return(renderChildren);
        }
        protected virtual IEnumerable <IEntitySlim> GetChildEntities(string id, FormDataCollection queryStrings)
        {
            // try to parse id as an integer else use GetEntityFromId
            // which will grok Guids, Udis, etc and let use obtain the id
            if (int.TryParse(id, out var entityId) == false)
            {
                var entity = GetEntityFromId(id);
                if (entity == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }

                entityId = entity.Id;
            }

            IEntitySlim[] result;

            // if a request is made for the root node but user has no access to
            // root node, return start nodes instead
            if (entityId == Constants.System.Root && UserStartNodes.Contains(Constants.System.Root) == false)
            {
                result = UserStartNodes.Length > 0
                    ? Services.EntityService.GetAll(UmbracoObjectType, UserStartNodes).ToArray()
                    : Array.Empty <IEntitySlim>();
            }
            else
            {
                result = GetChildrenFromEntityService(entityId).ToArray();
            }

            return(result);
        }
        protected virtual ActionResult <IEnumerable <IEntitySlim> > GetChildEntities(string id, FormCollection queryStrings)
        {
            // try to parse id as an integer else use GetEntityFromId
            // which will grok Guids, Udis, etc and let use obtain the id
            if (!int.TryParse(id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var entityId))
            {
                var entity = GetEntityFromId(id);
                if (entity == null)
                {
                    return(NotFound());
                }

                entityId = entity.Id;
            }

            var ignoreUserStartNodes = IgnoreUserStartNodes(queryStrings);

            IEntitySlim[] result;

            // if a request is made for the root node but user has no access to
            // root node, return start nodes instead
            if (!ignoreUserStartNodes && entityId == Constants.System.Root && UserStartNodes.Contains(Constants.System.Root) == false)
            {
                result = UserStartNodes.Length > 0
                    ? _entityService.GetAll(UmbracoObjectType, UserStartNodes).ToArray()
                    : Array.Empty <IEntitySlim>();
            }
            else
            {
                result = GetChildrenFromEntityService(entityId).ToArray();
            }

            return(result);
        }
        protected IEnumerable <IUmbracoEntity> GetChildEntities(string id)
        {
            // use helper method to ensure we support both integer and guid lookups
            int iid;

            // look up from GUID if it's not an integer
            if (int.TryParse(id, out iid) == false)
            {
                var idEntity = GetEntityFromId(id);
                if (idEntity == null)
                {
                    throw new HttpResponseException(HttpStatusCode.NotFound);
                }
                iid = idEntity.Id;
            }

            // if a request is made for the root node but user has no access to
            // root node, return start nodes instead
            if (iid == Constants.System.Root && UserStartNodes.Contains(Constants.System.Root) == false)
            {
                return(UserStartNodes.Length > 0
                    ? Services.EntityService.GetAll(UmbracoObjectType, UserStartNodes)
                    : Enumerable.Empty <IUmbracoEntity>());
            }

            return(Services.EntityService.GetChildren(iid, UmbracoObjectType).ToArray());
        }
        /// <summary>
        /// Ensure the noAccess metadata is applied for the root node if in dialog mode and the user doesn't have path access to it
        /// </summary>
        /// <param name="queryStrings"></param>
        /// <returns></returns>
        protected override TreeNode CreateRootNode(FormDataCollection queryStrings)
        {
            var node = base.CreateRootNode(queryStrings);

            if (IsDialog(queryStrings) && UserStartNodes.Contains(Constants.System.Root) == false)
            {
                node.AdditionalData["noAccess"] = true;
            }

            return(node);
        }
        public UserStartNodes GetById(int userId)
        {
            StartNodeCollection startNodes     = StartNodeRepository.GetCachedStartNodesByUserId(userId, ApplicationContext, DatabaseContext);
            UserStartNodes      userStartNodes = new UserStartNodes();

            userStartNodes.UserId  = userId;
            userStartNodes.Content = (startNodes.Content != null) ? string.Join(",", startNodes.Content) : "";
            userStartNodes.Media   = (startNodes.Media != null) ? string.Join(",", startNodes.Media) : "";

            return(userStartNodes);
        }
        /// <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>
        /// Ensure the noAccess metadata is applied for the root node if in dialog mode and the user doesn't have path access to it
        /// </summary>
        /// <param name="queryStrings"></param>
        /// <returns></returns>
        protected override ActionResult <TreeNode> CreateRootNode(FormCollection queryStrings)
        {
            var nodeResult = base.CreateRootNode(queryStrings);

            if ((nodeResult.Result is null))
            {
                return(nodeResult);
            }
            var node = nodeResult.Value;

            if (IsDialog(queryStrings) && UserStartNodes.Contains(Constants.System.Root) == false && IgnoreUserStartNodes(queryStrings) == false)
            {
                node.AdditionalData["noAccess"] = true;
            }

            return(node);
        }
        /// <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 TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
        {
            var ignoreUserStartNodes = queryStrings.GetValue <bool>(TreeQueryStringParameters.IgnoreUserStartNodes);

            //check if we're rendering the root
            if (id == Constants.System.RootString && UserStartNodes.Contains(Constants.System.Root) || ignoreUserStartNodes)
            {
                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 nodes = GetTreeNodesInternal(id, queryStrings);

                //only render the recycle bin if we are not in dialog and the start id id still the root
                if (IsDialog(queryStrings) == false && id == Constants.System.RootString)
                {
                    nodes.Add(CreateTreeNode(
                                  RecycleBinId.ToInvariantString(),
                                  id,
                                  queryStrings,
                                  Services.TextService.Localize("general/recycleBin"),
                                  "icon-trash",
                                  RecycleBinSmells,
                                  queryStrings.GetRequiredValue <string>("application") + TreeAlias.EnsureStartsWith('/') + "/recyclebin"));
                }

                return(nodes);
            }

            return(GetTreeNodesInternal(id, queryStrings));
        }
        public UserStartNodes Save(UserStartNodes userStartNodes)
        {
            StartNodeRepository.Save(userStartNodes, ApplicationContext, DatabaseContext);

            return(userStartNodes);
        }