admin.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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.core.admin.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.core.admin.sections.%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. print section.get_routes()
  182. if first_section:
  183. self.routes += patterns('', url('^', include(section.get_routes())))
  184. first_section = False
  185. else:
  186. self.routes += patterns('', url(('^%s/' % section.id), include(section.get_routes())))
  187. return self.routes
  188. def get_action(self, action):
  189. """
  190. Get admin action
  191. """
  192. return self.actions_index.get(action)
  193. def get_admin_index(self):
  194. """
  195. Return admin index route - first action of first section
  196. """
  197. return self.sections[0].actions[0].route
  198. def get_admin_navigation(self, request):
  199. """
  200. Find and return current admin navigation
  201. """
  202. sections = []
  203. actions = []
  204. active_section = False
  205. active_action = False
  206. # Loop sections, build list of sections and find active section
  207. for section in self.sections:
  208. is_active = section.is_active(request.path)
  209. sections.append({
  210. 'is_active': is_active,
  211. 'name': section.name,
  212. 'icon': section.icon,
  213. 'route': section.actions[0].route
  214. })
  215. if is_active:
  216. active_section = section
  217. # If no section was found to be active, default to first one
  218. if not active_section:
  219. active_section = self.sections[0]
  220. sections[0]['is_active'] = True
  221. # Loop active section actions
  222. for action in active_section.actions:
  223. is_active = action.is_active(request.path, active_section.id if active_section != self.sections[0] else None)
  224. actions.append({
  225. 'is_active': is_active,
  226. 'name': action.name,
  227. 'icon': action.icon,
  228. 'help': action.help,
  229. 'route': action.route
  230. })
  231. if is_active:
  232. active_action = action
  233. # If no action was found to be active, default to first one
  234. if not active_action:
  235. active_action = active_section.actions[0]
  236. actions[0]['is_active'] = True
  237. # Return admin navigation for this location
  238. return {
  239. 'sections': sections,
  240. 'actions': actions,
  241. 'admin_index': self.get_admin_index(),
  242. }
  243. site = AdminSite();