/// <summary>
        /// Render the treeview with the first two levels of albums that are viewable to the logged on user.
        /// </summary>
        private TreeView Generate()
        {
            _tv = new TreeView
            {
              EnableCheckBoxPlugin = _tvOptions.EnableCheckboxPlugin
            };

              foreach (IAlbum rootAlbum in GetRootAlbums())
              {
            // Add root node.
            TreeNode rootNode = new TreeNode();

            string albumTitle = GetRootAlbumTitle(rootAlbum);
            rootNode.Text = albumTitle;
            rootNode.ToolTip = albumTitle;
            rootNode.Id = String.Concat("tv_", rootAlbum.Id.ToString(CultureInfo.InvariantCulture));
            rootNode.DataId = rootAlbum.Id.ToString(CultureInfo.InvariantCulture);
            rootNode.Expanded = true;
            rootNode.AddCssClass("jstree-root-node");

            if (!String.IsNullOrEmpty(_tvOptions.NavigateUrl))
            {
              var url = rootAlbum.IsVirtualAlbum ? _tvOptions.NavigateUrl : Utils.AddQueryStringParameter(_tvOptions.NavigateUrl, String.Concat("aid=", rootAlbum.Id.ToString(CultureInfo.InvariantCulture)));
              rootNode.NavigateUrl = url;
            }

            if (_tvOptions.EnableCheckboxPlugin)
            {
              rootNode.ShowCheckBox = !rootAlbum.IsVirtualAlbum && Utils.IsUserAuthorized(_tvOptions.RequiredSecurityPermissions, RoleController.GetGalleryServerRolesForUser(), rootAlbum.Id, rootAlbum.GalleryId, rootAlbum.IsPrivate, SecurityActionsOption.RequireOne, rootAlbum.IsVirtualAlbum);
              rootNode.Selectable = rootNode.ShowCheckBox;
            }
            else
            {
              rootNode.Selectable = true;
            }
            //if (!rootNode.Selectable) rootNode.HoverCssClass = String.Empty;

            // Select and check this node if needed.
            if (_tvOptions.SelectedAlbumIds.Contains(rootAlbum.Id))
            {
              rootNode.Selected = true;
            }

            _tv.Nodes.Add(rootNode);

            // Add the first level of albums below the root album.
            BindAlbumToTreeview(rootAlbum.GetChildGalleryObjects(GalleryObjectType.Album, !Utils.IsAuthenticated).ToSortedList(), rootNode, false);

            // Only display the root node if it is selectable or we added any children to it; otherwise, remove it.
            if (!rootNode.Selectable && rootNode.Nodes.Count == 0)
            {
              _tv.Nodes.Remove(rootNode);
            }
              }

              // Make sure all specified albums are visible and checked.
              foreach (int albumId in _tvOptions.SelectedAlbumIds)
              {
            IAlbum album = AlbumController.LoadAlbumInstance(albumId, false);
            if (Utils.IsUserAuthorized(_tvOptions.RequiredSecurityPermissions, RoleController.GetGalleryServerRolesForUser(), album.Id, album.GalleryId, album.IsPrivate, SecurityActionsOption.RequireOne, album.IsVirtualAlbum))
            {
              BindSpecificAlbumToTreeview(album);
            }
              }

              return _tv;
        }
        /// <summary>
        /// Render the treeview with the first two levels of albums that are viewable to the logged on user.
        /// </summary>
        /// <param name="tvOptions">The treeview options.</param>
        /// <returns>Task&lt;TreeView&gt;.</returns>
        private async Task <TreeView> Generate(TreeViewOptions tvOptions)
        {
            Options = tvOptions;
            Tree.EnableCheckBoxPlugin = Options.EnableCheckboxPlugin;

            foreach (IAlbum rootAlbum in await GetTopAlbums())
            {
                if (!Options.UserController.IsUserAuthorized(SecurityActions.ViewAlbumOrMediaObject, await Options.UserController.GetGalleryServerRolesForUser(), rootAlbum.Id, rootAlbum.GalleryId, rootAlbum.IsPrivate, rootAlbum.IsVirtualAlbum))
                {
                    continue;
                }

                // Add root node.
                TreeNode rootNode = new TreeNode();

                string albumTitle = GetTopAlbumTitle(rootAlbum);
                rootNode.Text     = albumTitle;
                rootNode.ToolTip  = albumTitle;
                rootNode.Id       = String.Concat("tv_", rootAlbum.Id.ToString(CultureInfo.InvariantCulture));
                rootNode.DataId   = rootAlbum.Id.ToString(CultureInfo.InvariantCulture);
                rootNode.Expanded = (Options.NumberOfLevels > 1);

                if (rootAlbum.Parent is NullGalleryObject)
                {
                    rootNode.AddCssClass("jstree-root-node");
                }

                if (!String.IsNullOrEmpty(Options.NavigateUrl))
                {
                    //var url = rootAlbum.IsVirtualAlbum ? Options.NavigateUrl : Utils.AddQueryStringParameter(Options.NavigateUrl, String.Concat("aid=", rootAlbum.Id.ToString(CultureInfo.InvariantCulture)));
                    var url = rootAlbum.IsVirtualAlbum ? Options.NavigateUrl : string.Concat(Options.NavigateUrl, "?aid=", rootAlbum.Id.ToString(CultureInfo.InvariantCulture));
                    rootNode.NavigateUrl = url;
                }

                // If it has a nav URL, it's always selectable & won't have a checkbox. If not, then it's selectable if the user has permission
                if (string.IsNullOrEmpty(rootNode.NavigateUrl))
                {
                    rootNode.Selectable = !rootAlbum.IsVirtualAlbum && Options.UserController.IsUserAuthorized(Options.RequiredSecurityPermissions, await GetRoles(), rootAlbum.Id, rootAlbum.GalleryId, rootAlbum.IsPrivate, SecurityActionsOption.RequireOne, rootAlbum.IsVirtualAlbum);

                    if (Options.EnableCheckboxPlugin)
                    {
                        rootNode.ShowCheckBox = rootNode.Selectable;
                    }
                }
                else
                {
                    rootNode.Selectable = true;
                }

                // Select and check this node if needed.
                if (Options.SelectedAlbumIds.Contains(rootAlbum.Id))
                {
                    rootNode.Selected = true;
                }

                Tree.Nodes.Add(rootNode);

                // Add the first level of albums below the root album.
                var childAlbums = rootAlbum.GetChildGalleryObjects(GalleryObjectType.Album, !Options.UserController.IsAuthenticated);

                if (Options.NumberOfLevels == 1)
                {
                    rootNode.HasChildren = childAlbums.Any();
                }
                else
                {
                    BindAlbumToTreeview(childAlbums.ToSortedList(), rootNode, false);
                }

                // Only display the root node if it is selectable or we added any children to it; otherwise, remove it.
                //if (!rootNode.Selectable && rootNode.Nodes.Count == 0)
                //{
                //  Tree.Nodes.Remove(rootNode);
                //}
            }

            // Make sure all specified albums are visible and checked.
            try
            {
                foreach (var albumId in Options.SelectedAlbumIds.Where(id => id > int.MinValue))
                {
                    var album = Factory.LoadAlbumInstance(new AlbumLoadOptions(albumId));

                    if (Options.UserController.IsUserAuthorized(Options.RequiredSecurityPermissions, await GetRoles(), album.Id, album.GalleryId, album.IsPrivate, SecurityActionsOption.RequireOne, album.IsVirtualAlbum))
                    {
                        BindSpecificAlbumToTreeview(album);
                    }
                }
            }
            catch (InvalidAlbumException ex)
            {
                // One of the albums we want to select doesn't exist. Log the event but otherwise continue on gracefully.
                if (!ex.Data.Contains("Tree_SelectedAlbum_Info"))
                {
                    ex.Data.Add("Tree_SelectedAlbum_Info", $"Album {ex.AlbumId} was one of the SelectedAlbumIds of the TreeViewOptions object. It may have been deleted by another user just before this code ran.");
                }

                AppEventController.LogError(ex);
            }

            return(Tree);
        }
        /// <summary>
        /// Render the treeview with the first two levels of albums that are viewable to the logged on user.
        /// </summary>
        private TreeView Generate()
        {
            _tv = new TreeView
            {
                EnableCheckBoxPlugin = _tvOptions.EnableCheckboxPlugin
            };

            foreach (IAlbum rootAlbum in GetRootAlbums())
            {
                // Add root node.
                TreeNode rootNode = new TreeNode();

                string albumTitle = GetRootAlbumTitle(rootAlbum);
                rootNode.Text     = albumTitle;
                rootNode.ToolTip  = albumTitle;
                rootNode.Id       = String.Concat("tv_", rootAlbum.Id.ToString(CultureInfo.InvariantCulture));
                rootNode.DataId   = rootAlbum.Id.ToString(CultureInfo.InvariantCulture);
                rootNode.Expanded = true;
                rootNode.AddCssClass("jstree-root-node");

                if (!String.IsNullOrEmpty(_tvOptions.NavigateUrl))
                {
                    var url = rootAlbum.IsVirtualAlbum ? _tvOptions.NavigateUrl : Utils.AddQueryStringParameter(_tvOptions.NavigateUrl, String.Concat("aid=", rootAlbum.Id.ToString(CultureInfo.InvariantCulture)));
                    rootNode.NavigateUrl = url;
                }

                if (_tvOptions.EnableCheckboxPlugin)
                {
                    rootNode.ShowCheckBox = !rootAlbum.IsVirtualAlbum && Utils.IsUserAuthorized(_tvOptions.RequiredSecurityPermissions, RoleController.GetGalleryServerRolesForUser(), rootAlbum.Id, rootAlbum.GalleryId, rootAlbum.IsPrivate, SecurityActionsOption.RequireOne, rootAlbum.IsVirtualAlbum);
                    rootNode.Selectable   = rootNode.ShowCheckBox;
                }
                else
                {
                    rootNode.Selectable = true;
                }
                //if (!rootNode.Selectable) rootNode.HoverCssClass = String.Empty;

                // Select and check this node if needed.
                if (_tvOptions.SelectedAlbumIds.Contains(rootAlbum.Id))
                {
                    rootNode.Selected = true;
                }

                _tv.Nodes.Add(rootNode);

                // Add the first level of albums below the root album.
                BindAlbumToTreeview(rootAlbum.GetChildGalleryObjects(GalleryObjectType.Album, !Utils.IsAuthenticated).ToSortedList(), rootNode, false);

                // Only display the root node if it is selectable or we added any children to it; otherwise, remove it.
                if (!rootNode.Selectable && rootNode.Nodes.Count == 0)
                {
                    _tv.Nodes.Remove(rootNode);
                }
            }

            // Make sure all specified albums are visible and checked.
            foreach (int albumId in _tvOptions.SelectedAlbumIds)
            {
                IAlbum album = AlbumController.LoadAlbumInstance(albumId, false);
                if (Utils.IsUserAuthorized(_tvOptions.RequiredSecurityPermissions, RoleController.GetGalleryServerRolesForUser(), album.Id, album.GalleryId, album.IsPrivate, SecurityActionsOption.RequireOne, album.IsVirtualAlbum))
                {
                    BindSpecificAlbumToTreeview(album);
                }
            }

            return(_tv);
        }