hierarchy.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. from django.urls import reverse
  2. class Node:
  3. def __init__(self, name=None, icon=None, link=None):
  4. self.parent = None
  5. self.name = name
  6. self.icon = icon
  7. self.link = link
  8. self._children = []
  9. self._children_dict = {}
  10. self._resolved_namespace = None
  11. @property
  12. def namespace(self):
  13. if self._resolved_namespace:
  14. return self._resolved_namespace
  15. bits = self.link.split(":")
  16. self._resolved_namespace = ":".join(bits[:-1])
  17. return self._resolved_namespace
  18. def children(self):
  19. return self._children
  20. def children_as_dicts(self):
  21. childrens = []
  22. for children in self._children:
  23. childrens.append(
  24. {
  25. "name": children.name,
  26. "icon": children.icon,
  27. "link": reverse(children.link),
  28. "namespace": children.namespace,
  29. }
  30. )
  31. return childrens
  32. def add_node(self, node, after=None, before=None):
  33. if after:
  34. return self.add_node_after(node, after)
  35. if before:
  36. return self.add_node_before(node, before)
  37. node.parent = self
  38. self._children.append(node)
  39. self._children_dict[node.link] = node
  40. return True
  41. def add_node_after(self, node, after):
  42. success = False
  43. new_children_list = []
  44. for children in self._children:
  45. new_children_list.append(children)
  46. if children.link == after:
  47. new_children_list.append(node)
  48. success = True
  49. if success:
  50. node.parent = self
  51. self._children_dict[node.link] = node
  52. self._children = new_children_list
  53. return success
  54. def add_node_before(self, node, before):
  55. success = False
  56. new_children_list = []
  57. for children in self._children:
  58. if children.link == before:
  59. new_children_list.append(node)
  60. success = True
  61. new_children_list.append(children)
  62. if success:
  63. node.parent = self
  64. self._children_dict[node.link] = node
  65. self._children = new_children_list
  66. return success
  67. def child(self, namespace):
  68. try:
  69. return self._children_dict[namespace]
  70. except KeyError:
  71. raise ValueError(
  72. "Node %s is not a child of node %s" % (namespace, self.name)
  73. )
  74. def is_root(self):
  75. return False
  76. class AdminHierarchyBuilder:
  77. def __init__(self):
  78. self.nodes_record = []
  79. self.nodes_dict = {}
  80. def build_nodes_dict(self):
  81. nodes_dict = {"misago:admin": Node(link="misago:admin:index")}
  82. iterations = 0
  83. while self.nodes_record:
  84. iterations += 1
  85. if iterations > 512:
  86. message = (
  87. "Misago Admin hierarchy is invalid or too complex to resolve. "
  88. "Nodes left: %s"
  89. )
  90. raise ValueError(message % self.nodes_record)
  91. for index, node in enumerate(self.nodes_record):
  92. if node["parent"] in nodes_dict:
  93. node_obj = Node(
  94. name=node["name"], icon=node["icon"], link=node["link"]
  95. )
  96. parent = nodes_dict[node["parent"]]
  97. if node["after"]:
  98. node_added = parent.add_node(node_obj, after=node["after"])
  99. elif node["before"]:
  100. node_added = parent.add_node(node_obj, before=node["before"])
  101. else:
  102. node_added = parent.add_node(node_obj)
  103. if node_added:
  104. namespace = node.get("namespace") or node_obj.namespace
  105. if namespace not in nodes_dict:
  106. nodes_dict[namespace] = node_obj
  107. del self.nodes_record[index]
  108. break
  109. return nodes_dict
  110. def add_node(
  111. self,
  112. name=None,
  113. icon=None,
  114. parent="misago:admin",
  115. after=None,
  116. before=None,
  117. namespace=None,
  118. link=None,
  119. ):
  120. if self.nodes_dict:
  121. raise RuntimeError(
  122. "Misago admin site has already been initialized. "
  123. "You can't add new nodes to it."
  124. )
  125. if after and before:
  126. raise ValueError("after and before arguments are exclusive")
  127. self.nodes_record.append(
  128. {
  129. "name": name,
  130. "icon": icon,
  131. "parent": parent,
  132. "namespace": namespace,
  133. "after": after,
  134. "before": before,
  135. "link": link,
  136. }
  137. )
  138. def visible_branches(self, request):
  139. if not self.nodes_dict:
  140. self.nodes_dict = self.build_nodes_dict()
  141. branches = []
  142. try:
  143. namespace = request.resolver_match.namespace
  144. except AttributeError:
  145. namespace = "misago:admin"
  146. if namespace in self.nodes_dict:
  147. node = self.nodes_dict[namespace]
  148. while node:
  149. children = node.children_as_dicts()
  150. if children:
  151. branches.append(children)
  152. node = node.parent
  153. try:
  154. namespaces = request.resolver_match.namespaces
  155. except AttributeError:
  156. namespaces = ["misago", "admin"]
  157. branches.reverse()
  158. for depth, branch in enumerate(branches):
  159. depth_namespace = namespaces[2 : 3 + depth]
  160. for node in branch:
  161. node_namespace = node["namespace"].split(":")[2 : 3 + depth]
  162. if request.resolver_match:
  163. node["is_active"] = depth_namespace == node_namespace
  164. else:
  165. node["is_active"] = False
  166. return branches
  167. site = AdminHierarchyBuilder()