# -*- coding: utf-8 -*- import hashlib from django.http import JsonResponse from django.contrib.admin import site from django.contrib.admin.views.decorators import staff_member_required from django.utils.decorators import method_decorator from django.core.exceptions import ImproperlyConfigured from django.views import View from .config import get_config class GetAppListJsonView(View): @method_decorator(staff_member_required) def dispatch(self, *args, **kwargs): """ Only staff members can access this view """ return super(GetAppListJsonView, self).dispatch(*args, **kwargs) def get(self, request): """ Returns a json representing the menu voices in a format eaten by the js menu. Raised ImproperlyConfigured exceptions can be viewed in the browser console """ self.app_list = site.get_app_list(request) self.apps_dict = self.create_app_list_dict() # no menu provided items = self.get_menu(request) if not items: voices = self.get_default_voices() else: voices = [] for item in items: self.add_voice(voices, item) return JsonResponse(voices, safe=False) def get_menu(self, request): return get_config('MENU') def add_voice(self, voices, item): """ Adds a voice to the list """ voice = None if item.get('type') == 'title': voice = self.get_title_voice(item) elif item.get('type') == 'app': voice = self.get_app_voice(item) elif item.get('type') == 'model': voice = self.get_app_model_voice(item) elif item.get('type') == 'free': voice = self.get_free_voice(item) if voice: voices.append(voice) def get_title_voice(self, item): """ Title voice Returns the js menu compatible voice dict if the user can see it, None otherwise """ view = True if item.get('perms', None): view = self.check_user_permission(item.get('perms', [])) elif item.get('apps', None): view = self.check_apps_permission(item.get('apps', [])) if view: children_items = item.get('children', []) children = [] if len(children_items): for citem in children_items: self.add_voice(children, citem) return { 'type': 'title', 'label': item.get('label', ''), 'icon': item.get('icon', None), 'defaultOpen': item.get('default_open', False), 'children': children, } return None def get_free_voice(self, item): """ Free voice Returns the js menu compatible voice dict if the user can see it, None otherwise """ view = True if item.get('perms', None): view = self.check_user_permission(item.get('perms', [])) elif item.get('apps', None): view = self.check_apps_permission(item.get('apps', [])) if view: children_items = item.get('children', []) children = [] if len(children_items): for citem in children_items: self.add_voice(children, citem) return { 'type': 'free', 'label': item.get('label', ''), 'icon': item.get('icon', None), 'url': item.get('url', None), 're': item.get('re', None), 'defaultOpen': item.get('default_open', False), 'children': children, } return None def get_app_voice(self, item): """ App voice Returns the js menu compatible voice dict if the user can see it, None otherwise """ if item.get('name', None) is None: raise ImproperlyConfigured('App menu voices must have a name key') if self.check_apps_permission([item.get('name', None)]): children = [] if item.get('models', None) is None: for name, model in self.apps_dict[item.get('name')]['models'].items(): # noqa children.append({ 'type': 'model', 'label': model.get('name', ''), 'url': model.get('admin_url', '') }) else: for model_item in item.get('models', []): voice = self.get_model_voice(item.get('name'), model_item) if voice: children.append(voice) return { 'type': 'app', 'label': item.get('label', ''), 'icon': item.get('icon', None), 'defaultOpen': item.get('default_open', False), 'children': children } return None def get_app_model_voice(self, app_model_item): """ App Model voice Returns the js menu compatible voice dict if the user can see it, None otherwise """ if app_model_item.get('name', None) is None: raise ImproperlyConfigured('Model menu voices must have a name key') # noqa if app_model_item.get('app', None) is None: raise ImproperlyConfigured('Model menu voices must have an app key') # noqa return self.get_model_voice(app_model_item.get('app'), app_model_item) def get_model_voice(self, app, model_item): """ Model voice Returns the js menu compatible voice dict if the user can see it, None otherwise """ if model_item.get('name', None) is None: raise ImproperlyConfigured('Model menu voices must have a name key') # noqa if self.check_model_permission(app, model_item.get('name', None)): return { 'type': 'model', 'label': model_item.get('label', ''), 'icon': model_item.get('icon', None), 'url': self.apps_dict[app]['models'][model_item.get('name')]['admin_url'], # noqa } return None def create_app_list_dict(self): """ Creates a more efficient to check dictionary from the app_list list obtained from django admin """ d = {} for app in self.app_list: models = {} for model in app.get('models', []): models[model.get('object_name').lower()] = model d[app.get('app_label').lower()] = { 'app_url': app.get('app_url', ''), 'app_label': app.get('app_label'), 'models': models } return d def check_user_permission(self, perms): for perm in perms: if self.request.user.has_perm(perm): return True return False def check_apps_permission(self, apps): """ Checks if one of apps is listed in apps_dict Since apps_dict is derived from the app_list given by django admin, it lists only the apps the user can view """ for app in apps: if app in self.apps_dict: return True return False def check_model_permission(self, app, model): """ Checks if model is listed in apps_dict Since apps_dict is derived from the app_list given by django admin, it lists only the apps and models the user can view """ if self.apps_dict.get(app, False) and model in self.apps_dict[app]['models']: return True return False def get_default_voices(self): """ When no custom menu is defined in settings Retrieves a js menu ready dict from the django admin app list """ voices = [] for app in self.app_list: children = [] for model in app.get('models', []): child = { 'type': 'model', 'label': model.get('name', ''), 'url': model.get('admin_url', '') } children.append(child) voice = { 'type': 'app', 'label': app.get('name', ''), 'url': app.get('app_url', ''), 'children': children } voices.append(voice) return voices class GetGravatartUrlJsonView(View): def get(self, request): if not request.user.is_authenticated: return JsonResponse({}) try: email = request.user.email.lower().strip() hash = hashlib.md5(email.encode()) return JsonResponse({"hash": hash.hexdigest()}) except Exception: return JsonResponse({})