__init__.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. from django.conf import settings
  2. from django.conf.urls import patterns, include, url
  3. from django.core.urlresolvers import resolve
  4. from django.utils.importlib import import_module
  5. """
  6. Clean admin path if it was defined, or leave variable empty if ACP is turned off.
  7. """
  8. ADMIN_PATH = ''
  9. if settings.ADMIN_PATH:
  10. ADMIN_PATH = settings.ADMIN_PATH
  11. while ADMIN_PATH[:1] == '/':
  12. ADMIN_PATH = ADMIN_PATH[1:]
  13. while ADMIN_PATH[-1:] == '/':
  14. ADMIN_PATH = ADMIN_PATH[:-1]
  15. ADMIN_PATH += '/'
  16. """
  17. Admin lists sorter for admin sections and actions
  18. """
  19. class SortList(object):
  20. def __init__(self, unsorted):
  21. self.unsorted = unsorted
  22. def sort(self):
  23. # Sort and return sorted list
  24. order = []
  25. cache = {}
  26. for item in self.unsorted:
  27. if item.after:
  28. try:
  29. cache[item.after].append(item.id)
  30. except KeyError:
  31. cache[item.after] = []
  32. cache[item.after].append(item.id)
  33. else:
  34. order.append(item.id)
  35. while cache:
  36. for item in cache.keys():
  37. try:
  38. target_index = order.index(item)
  39. for new_item in cache[item]:
  40. target_index += 1
  41. order.insert(target_index, new_item)
  42. del cache[item]
  43. except ValueError:
  44. pass
  45. sorted = []
  46. for item in order:
  47. for object in self.unsorted:
  48. if item == object.id:
  49. sorted.append(object)
  50. break
  51. return sorted
  52. """
  53. Admin site section
  54. """
  55. class AdminSiteItem(object):
  56. def __init__(self, id, name, icon, target=None, route=None, help=None, after=None):
  57. self.id = id
  58. self.name = name
  59. self.help = help
  60. self.after = after
  61. self.icon = icon
  62. self.target = target
  63. self.route = route
  64. self.sorted = False
  65. """
  66. Admin site action
  67. """
  68. class AdminAction(AdminSiteItem):
  69. def __init__(self, section=None, actions=[], model=None, messages={}, urlpatterns=None, **kwargs):
  70. self.actions = actions
  71. self.section = section
  72. self.model = model
  73. self.messages = messages
  74. self.urlpatterns = urlpatterns
  75. super(AdminAction, self).__init__(**kwargs)
  76. def get_action_attr(self, id, attr):
  77. for action in self.actions:
  78. if action['id'] == id:
  79. return action[attr]
  80. return None
  81. def is_active(self, full_path, section=None):
  82. if section:
  83. action_path = '/%s%s/%s/' % (ADMIN_PATH, section, self.id)
  84. else:
  85. action_path = '/%s%s/' % (ADMIN_PATH, self.id)
  86. # Paths overlap = active action
  87. return len(action_path) <= full_path and full_path[:len(action_path)] == action_path
  88. """
  89. Admin site section
  90. """
  91. class AdminSection(AdminSiteItem):
  92. def __init__(self, section=None, **kwargs):
  93. self.actions = []
  94. self.last = None
  95. super(AdminSection, self).__init__(**kwargs)
  96. def get_routes(self):
  97. routes = []
  98. first_action = True
  99. for action in self.actions:
  100. if first_action:
  101. routes += patterns('', url('^', include(action.urlpatterns)))
  102. first_action = False
  103. else:
  104. routes += patterns('', url(('^%s/' % action.id), include(action.urlpatterns)))
  105. return routes
  106. def is_active(self, full_path):
  107. action_path = '/%s%s/' % (ADMIN_PATH, self.id)
  108. # Paths overlap = active action
  109. return len(action_path) <= full_path and full_path[:len(action_path)] == action_path
  110. """
  111. Admin site class that knows ACP structure
  112. """
  113. class AdminSite(object):
  114. actions_index = {}
  115. routes = []
  116. sections = []
  117. sections_index = {}
  118. def discover(self):
  119. """
  120. Build admin site structure
  121. """
  122. # Return discovered admin routes, so we dont repeat ourself
  123. if self.routes:
  124. return self.routes
  125. # Found actions
  126. actions = []
  127. # Orphan actions that have no section yet
  128. late_actions = []
  129. # Load default admin site
  130. from misago.admin.layout.sections import ADMIN_SECTIONS
  131. for section in ADMIN_SECTIONS:
  132. self.sections.append(section)
  133. self.sections_index[section.id] = section
  134. # Loop section actions
  135. section_actions = import_module('misago.admin.layout.%s' % section.id)
  136. for action in section_actions.ADMIN_ACTIONS:
  137. self.actions_index[action.id] = action
  138. if not action.after:
  139. action.after = self.sections_index[section.id].last
  140. actions.append(action)
  141. self.sections_index[section.id].last = action.after
  142. # Iterate over installed applications
  143. for app_name in settings.INSTALLED_APPS:
  144. try:
  145. app = import_module(app_name + '.admin')
  146. # Attempt to import sections
  147. try:
  148. for section in app.ADMIN_SECTIONS:
  149. self.sections.append(section)
  150. self.sections_index[section.id] = section
  151. except AttributeError:
  152. pass
  153. # Attempt to import actions
  154. try:
  155. for action in app.ADMIN_ACTIONS:
  156. self.actions_index[action.id] = action
  157. if action.section in self.sections_index:
  158. if not action.after:
  159. action.after = self.sections_index[action.section].last
  160. actions.append(action)
  161. self.sections_index[action.section].last = action.after
  162. else:
  163. late_actions.append(action)
  164. except AttributeError:
  165. pass
  166. except ImportError:
  167. pass
  168. # So actions and late actions
  169. actions += late_actions
  170. # Sorth sections and actions
  171. sort_sections = SortList(self.sections)
  172. sort_actions = SortList(actions)
  173. self.sections = sort_sections.sort()
  174. actions = sort_actions.sort()
  175. # Put actions in sections
  176. for action in actions:
  177. self.sections_index[action.section].actions.append(action)
  178. # Return ready admin routing
  179. first_section = True
  180. for section in self.sections:
  181. if first_section:
  182. self.routes += patterns('', url('^', include(section.get_routes())))
  183. first_section = False
  184. else:
  185. self.routes += patterns('', url(('^%s/' % section.id), include(section.get_routes())))
  186. return self.routes
  187. def get_action(self, action):
  188. """
  189. Get admin action
  190. """
  191. return self.actions_index.get(action)
  192. def get_admin_index(self):
  193. """
  194. Return admin index route - first action of first section
  195. """
  196. return self.sections[0].actions[0].route
  197. def get_admin_navigation(self, request):
  198. """
  199. Find and return current admin navigation
  200. """
  201. sections = []
  202. actions = []
  203. active_section = False
  204. active_action = False
  205. # Loop sections, build list of sections and find active section
  206. for section in self.sections:
  207. is_active = section.is_active(request.path)
  208. sections.append({
  209. 'is_active': is_active,
  210. 'name': section.name,
  211. 'icon': section.icon,
  212. 'route': section.actions[0].route
  213. })
  214. if is_active:
  215. active_section = section
  216. # If no section was found to be active, default to first one
  217. if not active_section:
  218. active_section = self.sections[0]
  219. sections[0]['is_active'] = True
  220. # Loop active section actions
  221. for action in active_section.actions:
  222. is_active = action.is_active(request.path, active_section.id if active_section != self.sections[0] else None)
  223. actions.append({
  224. 'is_active': is_active,
  225. 'name': action.name,
  226. 'icon': action.icon,
  227. 'help': action.help,
  228. 'route': action.route
  229. })
  230. if is_active:
  231. active_action = action
  232. # If no action was found to be active, default to first one
  233. if not active_action:
  234. active_action = active_section.actions[0]
  235. actions[0]['is_active'] = True
  236. # Return admin navigation for this location
  237. return {
  238. 'sections': sections,
  239. 'actions': actions,
  240. 'admin_index': self.get_admin_index(),
  241. }
  242. site = AdminSite();