private IList <NavBE> Nav_GetInternal(DataCommand queryCommand) { List <NavBE> navPages = new List <NavBE>(); queryCommand.Execute(delegate(IDataReader dr) { while (dr.Read()) { NavBE np = Nav_Populate(dr); navPages.Add(np); } }); return(navPages); }
//--- Constructors --- public ExpandableNavPage(NavBE navPage) { this.NavPage = navPage; }
public Yield GetNavigationFull(DreamContext context, DreamMessage request, Result <DreamMessage> response) { CheckResponseCache(context, false); PageBE page = PageBL_GetPageFromUrl(context, false); if (page.Title.IsTalk) { page = PageBL.GetPageByTitle(page.Title.AsFront()); } // check if requestest page exists, otherwise find nearest parent uint new_page_id = NavBL.NEW_PAGE_ID; ulong homepageId = DekiContext.Current.Instance.HomePageId; List <NavBE> list; bool expandableNav = context.GetParam("type", "compact").EqualsInvariantIgnoreCase("expandable"); // check if a page was found if (page.ID == 0) { // find nearest ancestor and authorize access PageBE ancestor = page; while (!ancestor.Title.IsHomepage) { // fetch ancestor page based on title ulong id = DbUtils.CurrentSession.Nav_GetNearestParent(ancestor.Title); ancestor = PageBL.GetPageById(id); if (PermissionsBL.IsUserAllowed(DekiContext.Current.User, ancestor, Permissions.BROWSE)) { break; } // determine parent page title Title title = ancestor.Title.GetParent(); if (title == null) { // current ancestor was the homepage break; } ancestor = new PageBE { Title = title }; } if (ancestor.ID == 0) { ancestor = PageBL.GetHomePage(); } list = NavBL.QueryNavTreeData(ancestor, context.Culture, expandableNav).ToList(); // find the nearest parent node and increase its child count foreach (NavBE nearestAncestors in list) { if (nearestAncestors.Id == ancestor.ID) { nearestAncestors.ChildCount = nearestAncestors.ChildCount + 1; break; } } // for each missing node, generate a dummy page and insert it into result set ulong ancestor_id = ancestor.ID; string[] ancestor_segments = ancestor.Title.AsUnprefixedDbSegments(); string[] new_page_segments = page.Title.AsUnprefixedDbSegments(); List <NavBE> newNodes = new List <NavBE>(32); for (int i = ancestor_segments.Length; i < new_page_segments.Length; ++i) { string title = string.Join("/", new_page_segments, 0, i + 1); // create dummy node with <page><page_id /><page_namespace /><page_title ><page_parent /><page_children /></page> NavBE newPage = new NavBE { Id = new_page_id, NameSpace = (ushort)page.Title.Namespace, Title = title, ParentId = (ancestor_id == homepageId) ? 0 : ancestor_id, ChildCount = (i == new_page_segments.Length - 1) ? 0 : 1 }; newNodes.Add(newPage); // update page information page.ID = new_page_id; page.ParentID = ancestor_id; ancestor_id = new_page_id++; } // check if we need to remove the children nodes of the ancestor ancestor_id = (ancestor.ID == homepageId) ? 0 : (uint)ancestor.ID; if (!expandableNav && (new_page_segments.Length - ancestor_segments.Length) > 1) { // remove ancestor children and add new dummy nodes for (int start = 0; start < list.Count; ++start) { // check if we found a matching child if ((list[start].ParentId == ancestor_id) && (list[start].Id != homepageId)) { // look for last child to remove so we can remove an entire range at once int end = start + 1; for (; (end < list.Count) && (list[end].ParentId == ancestor_id) && (list[end].Id != homepageId); ++end) { } list.RemoveRange(start, end - start); --start; } } } else { // find where among the ancestor children we need to insert the dummy node for (int index = 0; index < list.Count; ++index) { NavBE current = list[index]; if ((current.ParentId == ancestor_id) && (current.Id != homepageId)) { string[] parts = Title.FromDbPath(NS.UNKNOWN, current.Title, current.DisplayName).AsUnprefixedDbSegments(); if ((parts.Length > 0) && (new_page_segments.Length > 0) && (string.Compare(parts[parts.Length - 1], new_page_segments[parts.Length - 1], true, context.Culture) > 0)) { // found the spot list.InsertRange(index, newNodes); newNodes = null; break; } } } } // check if we didn't find the spot if (newNodes != null) { // add new nodes to the end list.AddRange(newNodes); } } else { list = NavBL.QueryNavTreeData(page, context.Culture, expandableNav).ToList(); } // find first parent ulong parent_id = homepageId; int parent_index = -1; for (int i = 0; i < list.Count; ++i) { if (list[i].Id == parent_id) { parent_index = i; break; } } if (parent_index == -1) { throw new Exception("unexpected [homepage not found]"); } // add any missing ancestor nodes (might have been removed by permission check or might simply not exist) string[] page_segments = page.Title.AsUnprefixedDbSegments(); ushort ns = (ushort)page.Title.Namespace; for (int i = 0; i <= page_segments.Length; ++i) { string title = string.Join("/", page_segments, 0, i); // loop over all nodes bool found = false; for (int j = 0; j < list.Count; ++j) { NavBE node = list[j]; // NOTE (steveb): we walk the path one parent at a time; however, there are a few special cases, because of namespaces // for instance, the parent page of User:Bob is the homepage (ditto for Template:Page), but the parent page of Admin:Config is Admin: // check if we found a node matching the current title if ((string.Compare(node.Title, title, true, context.Culture) == 0) && (((i == 0) && (ns != (ushort)NS.ADMIN)) ? (node.NameSpace == (ushort)NS.MAIN) : (node.NameSpace == ns))) { found = true; // let's make sure node is pointing to right parent node.ParentId = (parent_id == homepageId) ? 0 : parent_id; parent_id = node.Id; parent_index = j; break; } } if (!found) { // node is missing, let's create a new one NavBE newPage = new NavBE { Id = new_page_id, NameSpace = (ushort)page.Title.Namespace, Title = title, ParentId = (parent_id == homepageId) ? 0 : parent_id, ChildCount = 1 }; // add new page after parent list.Insert(parent_index + 1, newPage); parent_id = new_page_id++; parent_index = parent_index + 1; } } // build response if (ShowDebug(context)) { response.Return(DreamMessage.Ok(NavBL.ConvertNavPageListToDoc(list))); } else { XDoc result = expandableNav ? NavBL.ComputeExpandableNavigationDocument(list, page, 0, 0, false) : NavBL.ComputeNavigationDocument(list, page, 0, 0, false, context.GetParam("width", int.MaxValue)); if (ShowXml(context)) { response.Return(DreamMessage.Ok(result)); } else { response.Return(DreamMessage.Ok(new XDoc("tree").Value(result.Contents))); } } yield break; }
public static XDoc ComputeNavigationDocument(IList <NavBE> list, PageBE page, uint splitSibling, uint splitChildren, bool hidden, int max_width) { XDoc result = new XDoc("tree"); List <string> css = new List <string>(); List <string> fetchedChildren = new List <string>(); List <NavBE> childrenNodes = new List <NavBE>(); ulong homepageId = DekiContext.Current.Instance.HomePageId; NavDocStage stage = NavDocStage.None; if (splitSibling > 0) { stage = NavDocStage.SiblingPre; result.Start("siblings-pre"); } else if (splitChildren > 0) { stage = NavDocStage.ChildrenPre; result.Start("siblings-pre").End(); result.Start("children-pre"); } int splitSiblingIndex = 0; int splitChildrenIndex = 0; // fix page_parent_id: it's stored as zero for home and children of home ulong page_parent_id = page.ParentID; if ((page_parent_id == 0) && (page.ID != homepageId)) { page_parent_id = homepageId; } int page_index = 0; // iterate over result set int siblingIndex = 0; int childIndex = 0; foreach (NavBE node in list) { // retrieve page values uint node_id = node.Id; ulong node_parent_id = node.ParentId; bool virtual_node = (node_id >= NEW_PAGE_ID); // fix parent_id: it's stored as zero for home and children of home if ((node_parent_id == 0) && (node_id != homepageId)) { node_parent_id = homepageId; } // set node index (if possible) if (node_id == page.ID) { page_index = siblingIndex; } // check if we need to split the output if (node_id == splitSibling) { splitSiblingIndex = siblingIndex; stage = NavDocStage.ChildrenPre; result.End().Start("children-pre"); if (splitChildren == 0) { stage = NavDocStage.ChildrenPost; result.End().Start("children-post"); } continue; } if (node_id == splitChildren) { splitChildrenIndex = childIndex; stage = NavDocStage.ChildrenPost; result.End().Start("children-post"); continue; } if (((stage == NavDocStage.ChildrenPre) || (stage == NavDocStage.ChildrenPost)) && (splitSibling > 0) && (node_parent_id != splitSibling)) { if (stage == NavDocStage.ChildrenPre) { result.End().Start("children-post"); } stage = NavDocStage.SiblingPost; result.End().Start("siblings-post"); } // check if this node is part of the result set (only include ancestors, siblings, and children of selected node) bool ancestor = false; Title nodeTitle = Title.FromDbPath((NS)node.NameSpace, node.Title, node.DisplayName); if ((node_id != page.ID /* selected */) && (node_parent_id != page_parent_id /* sibling */) && (node_parent_id != page.ID /* child */)) { ancestor = (node_id == page_parent_id /* immediate parent */) || (node_id == homepageId) || nodeTitle.IsParentOf(page.Title); if (!ancestor) { continue; } } // don't include siblings root user pages if (((page.Title.IsUser) || (page.Title.IsSpecial) || (page.Title.IsTemplate)) && (page_parent_id == homepageId) && (node_parent_id == homepageId) && (node_id != page.ID)) { continue; } // 'div' element result.Start("div"); // 'class' attribute css.Clear(); css.Add("node"); if (hidden) { css.Add("closedNode"); } // 'c' (children) attribute fetchedChildren.Clear(); childrenNodes.Clear(); uint parentId = (node_id == homepageId) ? 0 : node_id; for (int i = 0; i < list.Count; ++i) { NavBE child = list[i]; if ((child.ParentId == parentId) & (child.Id != homepageId)) { Title childTitle = Title.FromDbPath((NS)child.NameSpace, child.Title, child.DisplayName); // skip children if they are siblings of the top User: or Template: page if (((page.Title.IsUser) || (page.Title.IsSpecial) || (page.Title.IsTemplate)) && (node_id == homepageId) && !childTitle.IsParentOf(page.Title)) { continue; } childrenNodes.Add(child); fetchedChildren.Add("n" + child.Id); } } int totalChildrenCount = node.ChildCount ?? fetchedChildren.Count; // 'p' (parent) attribute string p = null; if (node_id != homepageId) { p = "n" + node_parent_id; } // 'cd' (child-data) and 'sd' (sibling-data) attribute string cd = null; string sd; if (node_id == page.ID) { // active node if (node_id == homepageId) { css.Add("dockedNode"); css.Add("homeNode"); css.Add("parentClosed"); css.Add("homeSelected"); } else { css.Add("childNode"); if (!hidden) { css.Add("sibling"); } if (totalChildrenCount > 0) { css.Add("parentOpen"); } } css.Add("selected"); // we have all the child data cd = "1"; sd = "1"; } else if (node_parent_id == page_parent_id) { // sibling of active node css.Add("childNode"); if (!hidden) { css.Add("sibling"); } if (totalChildrenCount > 0) { css.Add("parentClosed"); } // if no children exist, then we have all the child data cd = ((totalChildrenCount > 0) ? "0" : "1"); sd = "1"; } else if (node_parent_id == page.ID) { // child of active node css.Add("childNode"); if ((node_parent_id == homepageId) && !hidden) { css.Add("sibling"); } if ((page.ID != homepageId) && !hidden) { css.Add("selectedChild"); } if (totalChildrenCount > 0) { css.Add("parentClosed"); } // if no children exist, then we have all the child data cd = ((totalChildrenCount > 0) ? "0" : "1"); sd = "1"; } else if (ancestor) { // ancestor of active node (parent and above) css.Add("dockedNode"); if (node_id == homepageId) { css.Add("homeNode"); } if (node_id == page_parent_id) { css.Add("lastDocked"); } css.Add("parentClosed"); // check if we are the last docked node or have more than one child if ((node_id == page_parent_id) || (totalChildrenCount == 1)) { cd = "1"; } else { // find the child node that is actually included in the tree foreach (NavBE child in childrenNodes) { Title childTitle = Title.FromDbPath((NS)child.NameSpace, child.Title, child.DisplayName); if (childTitle.IsParentOf(page.Title)) { cd = "n" + child.Id; break; } } if (cd == null) { #if DEBUG throw new Exception("unexpected [expected to find child nodes]"); #else cd = "1"; #endif } } // check if parent of this node has more than one child if ((node_id == homepageId) || (result[string.Format("div[@id='{0}']/@cd", "n" + node_parent_id)].Contents == "1")) { sd = "1"; } else { sd = "0"; } } else { throw new Exception("unexpected"); } // attributes result.Attr("class", string.Join(" ", css.ToArray())); result.Attr("id", "n" + node_id.ToString()); if (fetchedChildren.Count > 0) { result.Attr("c", string.Join(",", fetchedChildren.ToArray())); } if (p != null) { result.Attr("p", p); } // NOTE (steveb): this is used by the JS in the browser to correlate nodes in the pane (it's never used by anything else; hence the different format) string safe_path = nodeTitle.AsPrefixedDbPath().Replace("//", "%2f"); result.Attr("path", (safe_path.Length == 0) ? string.Empty : (safe_path + "/")); // root page always has all children if they belong to User:, Template:, or Special: namespace if ((cd != "1") && !virtual_node && ((page.Title.IsMain) || (((page.Title.IsTemplate) || (page.Title.IsUser) || (page.Title.IsSpecial)) && (node_id != homepageId)))) { result.Attr("cd", cd); } // children of root page always have all siblings if they belong to User:, Template:, or Special: namespace if ((sd != "1") && !virtual_node && ((page.Title.IsMain) || (((page.Title.IsTemplate) || (page.Title.IsUser) || (page.Title.IsSpecial)) && (node_parent_id != homepageId)))) { result.Attr("sd", "0"); } if (virtual_node || ((node_id == homepageId) && (!page.Title.IsMain))) { result.Attr("reload", "1"); } // div contents result.Start("a"); // set page title string name = nodeTitle.AsUserFriendlyName(); result.Attr("href", Utils.AsPublicUiUri(nodeTitle)); result.Attr("title", name); result.Elem("span", DekiWikiService.ScreenFont.Truncate(name, max_width)); result.End(); result.End(); if (node_parent_id == page_parent_id) { ++siblingIndex; } else if (node_parent_id == page.ID) { ++childIndex; } } // post-process created list if ((splitSibling > 0) || (splitChildren > 0)) { if (stage == NavDocStage.SiblingPre) { result.End().Start("siblings-post"); result.End().Start("children-pre"); result.End().Start("children-post"); } else if (stage == NavDocStage.ChildrenPre) { result.End().Start("children-post"); result.End().Start("siblings-post"); } else if (stage == NavDocStage.ChildrenPost) { result.End().Start("siblings-post"); } result.End(); // truncate siblings and children TruncateList(result["siblings-pre/div | siblings-post/div"], ~splitSiblingIndex, hidden); TruncateList(result["children-pre/div | children-post/div"], ~splitChildrenIndex, hidden); } else if (hidden) { // truncate full list TruncateList(result["div"], 0, hidden); } else { // truncate children of selected node TruncateList(result[string.Format("div[@p='n{0}']", page.ID)], 0, hidden); // truncate siblings of selected node TruncateList(result[string.Format("div[@p='n{0}']", page_parent_id)], page_index, hidden); } return(result); }