hierarchy.py 5.9 KB

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