utils.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. import datetime
  2. import json
  3. from django.template import Context
  4. from django.utils import translation
  5. from jet import settings
  6. from jet.models import PinnedApplication
  7. try:
  8. from django.apps.registry import apps
  9. except ImportError:
  10. try:
  11. from django.apps import apps # Fix Django 1.7 import issue
  12. except ImportError:
  13. pass
  14. from django.core.serializers.json import DjangoJSONEncoder
  15. from django.http import HttpResponse
  16. from django.core.urlresolvers import reverse, resolve, NoReverseMatch
  17. from django.contrib.admin import AdminSite
  18. from django.utils.encoding import smart_text
  19. from django.utils.text import capfirst
  20. from django.contrib import messages
  21. from django.utils.encoding import force_text
  22. from django.utils.functional import Promise
  23. from django.contrib.admin.options import IncorrectLookupParameters
  24. from django.core import urlresolvers
  25. from django.contrib import admin
  26. from django.utils.translation import ugettext_lazy as _
  27. from django.utils.text import slugify
  28. try:
  29. from collections import OrderedDict
  30. except ImportError:
  31. from ordereddict import OrderedDict # Python 2.6
  32. class JsonResponse(HttpResponse):
  33. """
  34. An HTTP response class that consumes data to be serialized to JSON.
  35. :param data: Data to be dumped into json. By default only ``dict`` objects
  36. are allowed to be passed due to a security flaw before EcmaScript 5. See
  37. the ``safe`` parameter for more information.
  38. :param encoder: Should be an json encoder class. Defaults to
  39. ``django.core.serializers.json.DjangoJSONEncoder``.
  40. :param safe: Controls if only ``dict`` objects may be serialized. Defaults
  41. to ``True``.
  42. """
  43. def __init__(self, data, encoder=DjangoJSONEncoder, safe=True, **kwargs):
  44. if safe and not isinstance(data, dict):
  45. raise TypeError('In order to allow non-dict objects to be '
  46. 'serialized set the safe parameter to False')
  47. kwargs.setdefault('content_type', 'application/json')
  48. data = json.dumps(data, cls=encoder)
  49. super(JsonResponse, self).__init__(content=data, **kwargs)
  50. def get_app_list(context, order=True):
  51. admin_site = get_admin_site(context)
  52. request = context['request']
  53. app_dict = {}
  54. for model, model_admin in admin_site._registry.items():
  55. app_label = model._meta.app_label
  56. try:
  57. has_module_perms = model_admin.has_module_permission(request)
  58. except AttributeError:
  59. has_module_perms = request.user.has_module_perms(app_label) # Fix Django < 1.8 issue
  60. if has_module_perms:
  61. perms = model_admin.get_model_perms(request)
  62. # Check whether user has any perm for this module.
  63. # If so, add the module to the model_list.
  64. if True in perms.values():
  65. info = (app_label, model._meta.model_name)
  66. model_dict = {
  67. 'name': capfirst(model._meta.verbose_name_plural),
  68. 'object_name': model._meta.object_name,
  69. 'perms': perms,
  70. 'model_name': model._meta.model_name
  71. }
  72. if perms.get('change', False):
  73. try:
  74. model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin_site.name)
  75. except NoReverseMatch:
  76. pass
  77. if perms.get('add', False):
  78. try:
  79. model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=admin_site.name)
  80. except NoReverseMatch:
  81. pass
  82. if app_label in app_dict:
  83. app_dict[app_label]['models'].append(model_dict)
  84. else:
  85. try:
  86. name = apps.get_app_config(app_label).verbose_name
  87. except NameError:
  88. name = app_label.title()
  89. app_dict[app_label] = {
  90. 'name': name,
  91. 'app_label': app_label,
  92. 'app_url': reverse(
  93. 'admin:app_list',
  94. kwargs={'app_label': app_label},
  95. current_app=admin_site.name,
  96. ),
  97. 'has_module_perms': has_module_perms,
  98. 'models': [model_dict],
  99. }
  100. # Sort the apps alphabetically.
  101. app_list = list(app_dict.values())
  102. if order:
  103. app_list.sort(key=lambda x: x['name'].lower())
  104. # Sort the models alphabetically within each app.
  105. for app in app_list:
  106. app['models'].sort(key=lambda x: x['name'])
  107. return app_list
  108. def get_admin_site(context):
  109. try:
  110. current_resolver = resolve(context.get('request').path)
  111. index_resolver = resolve(reverse('%s:index' % current_resolver.namespaces[0]))
  112. if hasattr(index_resolver.func, 'admin_site'):
  113. return index_resolver.func.admin_site
  114. for func_closure in index_resolver.func.__closure__:
  115. if isinstance(func_closure.cell_contents, AdminSite):
  116. return func_closure.cell_contents
  117. except:
  118. pass
  119. return admin.site
  120. def get_admin_site_name(context):
  121. return get_admin_site(context).name
  122. class LazyDateTimeEncoder(json.JSONEncoder):
  123. def default(self, obj):
  124. if isinstance(obj, datetime.datetime) or isinstance(obj, datetime.date):
  125. return obj.isoformat()
  126. elif isinstance(obj, Promise):
  127. return force_text(obj)
  128. return self.encode(obj)
  129. def get_model_instance_label(instance):
  130. if getattr(instance, "related_label", None):
  131. return instance.related_label()
  132. return smart_text(instance)
  133. class SuccessMessageMixin(object):
  134. """
  135. Adds a success message on successful form submission.
  136. """
  137. success_message = ''
  138. def form_valid(self, form):
  139. response = super(SuccessMessageMixin, self).form_valid(form)
  140. success_message = self.get_success_message(form.cleaned_data)
  141. if success_message:
  142. messages.success(self.request, success_message)
  143. return response
  144. def get_success_message(self, cleaned_data):
  145. return self.success_message % cleaned_data
  146. def get_model_queryset(admin_site, model, request, preserved_filters=None):
  147. model_admin = admin_site._registry.get(model)
  148. try:
  149. changelist_url = urlresolvers.reverse('%s:%s_%s_changelist' % (
  150. admin_site.name,
  151. model._meta.app_label,
  152. model._meta.model_name
  153. ))
  154. except NoReverseMatch:
  155. return
  156. changelist_filters = None
  157. if preserved_filters:
  158. changelist_filters = preserved_filters.get('_changelist_filters')
  159. if changelist_filters:
  160. changelist_url += '?' + changelist_filters
  161. if model_admin:
  162. queryset = model_admin.get_queryset(request)
  163. else:
  164. queryset = model.objects
  165. list_display = model_admin.get_list_display(request)
  166. list_display_links = model_admin.get_list_display_links(request, list_display)
  167. list_filter = model_admin.get_list_filter(request)
  168. search_fields = model_admin.get_search_fields(request) \
  169. if hasattr(model_admin, 'get_search_fields') else model_admin.search_fields
  170. list_select_related = model_admin.get_list_select_related(request) \
  171. if hasattr(model_admin, 'get_list_select_related') else model_admin.list_select_related
  172. actions = model_admin.get_actions(request)
  173. if actions:
  174. list_display = ['action_checkbox'] + list(list_display)
  175. ChangeList = model_admin.get_changelist(request)
  176. try:
  177. cl = ChangeList(
  178. request, model, list_display, list_display_links, list_filter, model_admin.date_hierarchy, search_fields,
  179. list_select_related, model_admin.list_per_page, model_admin.list_max_show_all, model_admin.list_editable,
  180. model_admin)
  181. queryset = cl.get_queryset(request)
  182. except IncorrectLookupParameters:
  183. pass
  184. return queryset
  185. def get_possible_language_codes():
  186. language_code = translation.get_language()
  187. language_code = language_code.replace('_', '-').lower()
  188. language_codes = []
  189. # making dialect part uppercase
  190. split = language_code.split('-', 2)
  191. if len(split) == 2:
  192. language_code = '%s-%s' % (split[0].lower(), split[1].upper()) if split[0] != split[1] else split[0]
  193. language_codes.append(language_code)
  194. # adding language code without dialect part
  195. if len(split) == 2:
  196. language_codes.append(split[0].lower())
  197. return language_codes
  198. def get_original_menu_items(context):
  199. if context.get('user') and context['user'].is_authenticated():
  200. pinned_apps = PinnedApplication.objects.filter(user=context['user'].pk).values_list('app_label', flat=True)
  201. else:
  202. pinned_apps = []
  203. original_app_list = get_app_list(context)
  204. return map(lambda app: {
  205. 'app_label': app['app_label'],
  206. 'url': app['app_url'],
  207. 'url_blank': False,
  208. 'label': app.get('name', capfirst(_(app['app_label']))),
  209. 'has_perms': app.get('has_module_perms', False),
  210. 'models': list(map(lambda model: {
  211. 'url': model.get('admin_url'),
  212. 'url_blank': False,
  213. 'name': model['model_name'],
  214. 'object_name': model['object_name'],
  215. 'label': model.get('name', model['object_name']),
  216. 'has_perms': any(model.get('perms', {}).values()),
  217. }, app['models'])),
  218. 'pinned': app['app_label'] in pinned_apps,
  219. 'custom': False
  220. }, original_app_list)
  221. def get_menu_item_url(url, original_app_list):
  222. if isinstance(url, dict):
  223. url_type = url.get('type')
  224. if url_type == 'app':
  225. return original_app_list[url['app_label']]['url']
  226. elif url_type == 'model':
  227. models = dict(map(
  228. lambda x: (x['name'], x['url']),
  229. original_app_list[url['app_label']]['models']
  230. ))
  231. return models[url['model']]
  232. elif url_type == 'reverse':
  233. return reverse(url['name'], args=url.get('args'), kwargs=url.get('kwargs'))
  234. elif isinstance(url, str):
  235. return url
  236. def get_menu_items(context):
  237. pinned_apps = PinnedApplication.objects.filter(user=context['user'].pk).values_list('app_label', flat=True)
  238. original_app_list = OrderedDict(map(lambda app: (app['app_label'], app), get_original_menu_items(context)))
  239. custom_app_list = settings.JET_SIDE_MENU_ITEMS
  240. custom_app_list_deprecated = settings.JET_SIDE_MENU_CUSTOM_APPS
  241. if custom_app_list not in (None, False):
  242. if isinstance(custom_app_list, dict):
  243. admin_site = get_admin_site(context)
  244. custom_app_list = custom_app_list.get(admin_site.name, [])
  245. app_list = []
  246. def get_menu_item_app_model(app_label, data):
  247. item = {'has_perms': True}
  248. if 'name' in data:
  249. parts = data['name'].split('.', 2)
  250. if len(parts) > 1:
  251. app_label, name = parts
  252. else:
  253. name = data['name']
  254. if app_label in original_app_list:
  255. models = dict(map(
  256. lambda x: (x['name'], x),
  257. original_app_list[app_label]['models']
  258. ))
  259. if name in models:
  260. item = models[name].copy()
  261. if 'label' in data:
  262. item['label'] = data['label']
  263. if 'url' in data:
  264. item['url'] = get_menu_item_url(data['url'], original_app_list)
  265. if 'url_blank' in data:
  266. item['url_blank'] = data['url_blank']
  267. if 'permissions' in data:
  268. item['has_perms'] = item.get('has_perms', True) and context['user'].has_perms(data['permissions'])
  269. return item
  270. def get_menu_item_app(data):
  271. app_label = data.get('app_label')
  272. if not app_label:
  273. if 'label' not in data:
  274. raise Exception('Custom menu items should at least have \'label\' or \'app_label\' key')
  275. app_label = 'custom_%s' % slugify(data['label'], allow_unicode=True)
  276. if app_label in original_app_list:
  277. item = original_app_list[app_label].copy()
  278. else:
  279. item = {'app_label': app_label, 'has_perms': True}
  280. if 'label' in data:
  281. item['label'] = data['label']
  282. if 'models' in data:
  283. item['models'] = list(map(lambda x: get_menu_item_app_model(app_label, x), data['models']))
  284. if 'url' in data:
  285. item['url'] = get_menu_item_url(data['url'], original_app_list)
  286. if 'url_blank' in data:
  287. item['url_blank'] = data['url_blank']
  288. if 'permissions' in data:
  289. item['has_perms'] = item.get('has_perms', True) and context['user'].has_perms(data['permissions'])
  290. item['pinned'] = item['app_label'] in pinned_apps
  291. return item
  292. for data in custom_app_list:
  293. item = get_menu_item_app(data)
  294. app_list.append(item)
  295. elif custom_app_list_deprecated not in (None, False):
  296. app_dict = {}
  297. models_dict = {}
  298. for app in original_app_list.values():
  299. app_label = app['app_label']
  300. app_dict[app_label] = app
  301. for model in app['models']:
  302. if app_label not in models_dict:
  303. models_dict[app_label] = {}
  304. models_dict[app_label][model['object_name']] = model
  305. app['models'] = []
  306. app_list = []
  307. if isinstance(custom_app_list_deprecated, dict):
  308. admin_site = get_admin_site(context)
  309. custom_app_list_deprecated = custom_app_list_deprecated.get(admin_site.name, [])
  310. for item in custom_app_list_deprecated:
  311. app_label, models = item
  312. if app_label in app_dict:
  313. app = app_dict[app_label]
  314. for model_label in models:
  315. if model_label == '__all__':
  316. app['models'] = models_dict[app_label].values()
  317. break
  318. elif model_label in models_dict[app_label]:
  319. model = models_dict[app_label][model_label]
  320. app['models'].append(model)
  321. app_list.append(app)
  322. else:
  323. app_list = original_app_list.values()
  324. current_found = False
  325. for app in app_list:
  326. if not current_found:
  327. for model in app['models']:
  328. if not current_found and model.get('url') and context['request'].path.startswith(model['url']):
  329. model['current'] = True
  330. current_found = True
  331. else:
  332. model['current'] = False
  333. if not current_found and app.get('url') and context['request'].path.startswith(app['url']):
  334. app['current'] = True
  335. current_found = True
  336. else:
  337. app['current'] = False
  338. return app_list
  339. def context_to_dict(context):
  340. if isinstance(context, Context):
  341. flat = {}
  342. for d in context.dicts:
  343. flat.update(d)
  344. context = flat
  345. return context