admin.py 8.9 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, link=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.link = link
  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_links(self):
  97. links = []
  98. first_action = True
  99. for action in self.actions:
  100. if first_action:
  101. links += patterns('', url('^', include(action.urlpatterns)))
  102. first_action = False
  103. else:
  104. links += patterns('', url(('^%s/' % action.id), include(action.urlpatterns)))
  105. return links
  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. links = []
  116. sections = []
  117. sections_index = {}
  118. def discover(self):
  119. """
  120. Build admin site structure
  121. """
  122. if self.links:
  123. return self.links
  124. # Found actions
  125. actions = []
  126. # Orphan actions that have no section yet
  127. late_actions = []
  128. # Load default admin site
  129. from misago.apps.admin.sections import ADMIN_SECTIONS
  130. for section in ADMIN_SECTIONS:
  131. self.sections.append(section)
  132. self.sections_index[section.id] = section
  133. # Loop section actions
  134. section_actions = import_module('misago.apps.admin.sections.%s' % section.id)
  135. for action in section_actions.ADMIN_ACTIONS:
  136. self.actions_index[action.id] = action
  137. if not action.after:
  138. action.after = self.sections_index[section.id].last
  139. actions.append(action)
  140. self.sections_index[section.id].last = action.after
  141. # Iterate over installed applications
  142. for app_name in settings.INSTALLED_APPS:
  143. try:
  144. app = import_module(app_name + '.admin')
  145. # Attempt to import sections
  146. try:
  147. for section in app.ADMIN_SECTIONS:
  148. self.sections.append(section)
  149. self.sections_index[section.id] = section
  150. except AttributeError:
  151. pass
  152. # Attempt to import actions
  153. try:
  154. for action in app.ADMIN_ACTIONS:
  155. self.actions_index[action.id] = action
  156. if action.section in self.sections_index:
  157. if not action.after:
  158. action.after = self.sections_index[action.section].last
  159. actions.append(action)
  160. self.sections_index[action.section].last = action.after
  161. else:
  162. late_actions.append(action)
  163. except AttributeError:
  164. pass
  165. except ImportError:
  166. pass
  167. # So actions and late actions
  168. actions += late_actions
  169. # Sorth sections and actions
  170. sort_sections = SortList(self.sections)
  171. sort_actions = SortList(actions)
  172. self.sections = sort_sections.sort()
  173. actions = sort_actions.sort()
  174. # Put actions in sections
  175. for action in actions:
  176. self.sections_index[action.section].actions.append(action)
  177. # Return ready admin routing
  178. first_section = True
  179. for section in self.sections:
  180. if first_section:
  181. self.links += patterns('', url('^', include(section.get_links())))
  182. first_section = False
  183. else:
  184. self.links += patterns('', url(('^%s/' % section.id), include(section.get_links())))
  185. return self.links
  186. def get_action(self, action):
  187. """
  188. Get admin action
  189. """
  190. return self.actions_index.get(action)
  191. def get_admin_index(self):
  192. """
  193. Return admin index link - first action of first section
  194. """
  195. return self.sections[0].actions[0].link
  196. def get_admin_navigation(self, request):
  197. """
  198. Find and return current admin navigation
  199. """
  200. sections = []
  201. actions = []
  202. active_section = False
  203. active_action = False
  204. # Loop sections, build list of sections and find active section
  205. for section in self.sections:
  206. is_active = section.is_active(request.path)
  207. sections.append({
  208. 'is_active': is_active,
  209. 'name': section.name,
  210. 'icon': section.icon,
  211. 'link': section.actions[0].link
  212. })
  213. if is_active:
  214. active_section = section
  215. # If no section was found to be active, default to first one
  216. if not active_section:
  217. active_section = self.sections[0]
  218. sections[0]['is_active'] = True
  219. # Loop active section actions
  220. for action in active_section.actions:
  221. is_active = action.is_active(request.path, active_section.id if active_section != self.sections[0] else None)
  222. actions.append({
  223. 'is_active': is_active,
  224. 'name': action.name,
  225. 'icon': action.icon,
  226. 'help': action.help,
  227. 'link': action.link
  228. })
  229. if is_active:
  230. active_action = action
  231. # If no action was found to be active, default to first one
  232. if not active_action:
  233. active_action = active_section.actions[0]
  234. actions[0]['is_active'] = True
  235. # Return admin navigation for this location
  236. return {
  237. 'sections': sections,
  238. 'actions': actions,
  239. 'admin_index': self.get_admin_index(),
  240. }
  241. site = AdminSite();