text: import copy
  import functools
  import datetime
  import decimal
  from functools import update_wrapper
  from inspect import getargspec
  
  from django import forms
  from django.apps import apps
  from django.conf import settings
  from django.contrib import messages
  from django.contrib.auth import get_permission_codename
  from django.core.exceptions import ValidationError
  from django.core.serializers.json import DjangoJSONEncoder
  from django.core.urlresolvers import reverse
  from django.http import HttpResponse
  from django.template import Context, Template
  from django.template.response import TemplateResponse
  from django.utils import six
  from django.utils.decorators import method_decorator, classonlymethod
  from django.utils.encoding import force_text, smart_text, smart_str
  from django.utils.functional import Promise
  from django.utils.http import urlencode
  from django.utils.itercompat import is_iterable
  from django.utils.safestring import mark_safe
  from django.utils.text import capfirst
  from django.utils.translation import ugettext as _
  from django.views.decorators.csrf import csrf_protect
  from django.views.generic import View
  from collections import OrderedDict
  from xadmin.util import static, json, vendor, sortkeypicker
  
  from xadmin.models import Log
  
  csrf_protect_m = method_decorator(csrf_protect)
  
  
  class IncorrectPluginArg(Exception):
      pass
  
  def get_content_type_for_model(obj):
      from django.contrib.contenttypes.models import ContentType
      return ContentType.objects.get_for_model(obj, for_concrete_model=False)
  
  def filter_chain(filters, token, func, *args, **kwargs):
      if token == -1:
          return func()
      else:
          def _inner_method():
              fm = filters[token]
              fargs = getargspec(fm)[0]
              if len(fargs) == 1:
                  # Only self arg
                  result = func()
                  if result is None:
                      return fm()
                  else:
                      raise IncorrectPluginArg(u'Plugin filter method need a arg to receive parent method result.')
              else:
                  return fm(func if fargs[1] == '__' else func(), *args, **kwargs)
          return filter_chain(filters, token - 1, _inner_method, *args, **kwargs)
  
  
  def filter_hook(func):
      tag = func.__name__
      func.__doc__ = "``filter_hook``\n\n" + (func.__doc__ or "")
  
      @functools.wraps(func)
      def method(self, *args, **kwargs):
  
          def _inner_method():
              return func(self, *args, **kwargs)
  
          if self.plugins:
              filters = [(getattr(getattr(p, tag), 'priority', 10), getattr(p, tag))
                         for p in self.plugins if callable(getattr(p, tag, None))]
              filters = [f for p, f in sorted(filters, key=lambda x:x[0])]
              return filter_chain(filters, len(filters) - 1, _inner_method, *args, **kwargs)
          else:
              return _inner_method()
      return method
  
  
  def inclusion_tag(file_name, context_class=Context, takes_context=False):
      def wrap(func):
          @functools.wraps(func)
          def method(self, context, nodes, *arg, **kwargs):
              _dict = func(self, context, nodes, *arg, **kwargs)
              from django.template.loader import get_template, select_template
              cls_str = str if six.PY3 else basestring
              if isinstance(file_name, Template):
                  t = file_name
              elif not isinstance(file_name, cls_str) and is_iterable(file_name):
                  t = select_template(file_name)
              else:
                  t = get_template(file_name)
  
              _dict['autoescape'] = context.autoescape
              _dict['use_l10n'] = context.use_l10n
              _dict['use_tz'] = context.use_tz
              _dict['admin_view'] = context['admin_view']
  
              csrf_token = context.get('csrf_token', None)
              if csrf_token is not None:
                  _dict['csrf_token'] = csrf_token
              nodes.append(t.render(_dict))
  
          return method
      return wrap
  
  
  class JSONEncoder(DjangoJSONEncoder):
      def default(self, o):
          if isinstance(o, datetime.datetime):
              return o.strftime('%Y-%m-%d %H:%M:%S')
          elif isinstance(o, datetime.date):
              return o.strftime('%Y-%m-%d')
          elif isinstance(o, decimal.Decimal):
              return str(o)
          elif isinstance(o, Promise):
              return force_text(o)
          else:
              try:
                  return super(JSONEncoder, self).default(o)
              except Exception:
                  return smart_text(o)
  
  
  class BaseAdminObject(object):
  
      def get_view(self, view_class, option_class=None, *args, **kwargs):
          opts = kwargs.pop('opts', {})
          return self.admin_site.get_view_class(view_class, option_class, **opts)(self.request, *args, **kwargs)
  
      def get_model_view(self, view_class, model, *args, **kwargs):
          return self.get_view(view_class, self.admin_site._registry.get(model), *args, **kwargs)
  
      def get_admin_url(self, name, *args, **kwargs):
          return reverse('%s:%s' % (self.admin_site.app_name, name), args=args, kwargs=kwargs)
  
      def get_model_url(self, model, name, *args, **kwargs):
          return reverse(
              '%s:%s_%s_%s' % (self.admin_site.app_name, model._meta.app_label,
                               model._meta.model_name, name),
              args=args, kwargs=kwargs, current_app=self.admin_site.name)
  
      def get_model_perm(self, model, name):
          return '%s.%s_%s' % (model._meta.app_label, name, model._meta.model_name)
  
      def has_model_perm(self, model, name, user=None):
          user = user or self.user
          return user.has_perm(self.get_model_perm(model, name)) or (name == 'view' and
                                                                     self.has_model_perm(model, 'change', user))
  
      def get_query_string(self, new_params=None, remove=None):
          if new_params is None:
              new_params = {}
          if remove is None:
              remove = []
          p = dict(self.request.GET.items()).copy()
          arr_keys = list(p.keys())
          for r in remove:
              for k in arr_keys:
                  if k.startswith(r):
                      del p[k]
          for k, v in new_params.items():
              if v is None:
                  if k in p:
                      del p[k]
              else:
                  p[k] = v
          return '?%s' % urlencode(p)
  
      def get_form_params(self, new_params=None, remove=None):
          if new_params is None:
              new_params = {}
          if remove is None:
              remove = []
          p = dict(self.request.GET.items()).copy()
          arr_keys = list(p.keys())
          for r in remove:
              for k in arr_keys:
                  if k.startswith(r):
                      del p[k]
          for k, v in new_params.items():
              if v is None:
                  if k in p:
                      del p[k]
              else:
                  p[k] = v
          return mark_safe(''.join(
              '<input type="hidden" name="%s" value="%s"/>' % (k, v) for k, v in p.items() if v))
  
      def render_response(self, content, response_type='json'):
          if response_type == 'json':
              response = HttpResponse(content_type="application/json; charset=UTF-8")
              response.write(
                  json.dumps(content, cls=JSONEncoder, ensure_ascii=False))
              return response
          return HttpResponse(content)
  
      def template_response(self, template, context):
          return TemplateResponse(self.request, template, context)
  
      def message_user(self, message, level='info'):
          """
          Send a message to the user. The default implementation
          posts a message using the django.contrib.messages backend.
          """
          if hasattr(messages, level) and callable(getattr(messages, level)):
              getattr(messages, level)(self.request, message)
  
      def static(self, path):
          return static(path)
  
      def vendor(self, *tags):
          return vendor(*tags)
  
      def log(self, flag, message, obj=None):
          log = Log(
              user=self.user, 
              ip_addr=self.request.META['REMOTE_ADDR'],
              action_flag=flag,
              message=message
          )
          if obj:
              log.content_type = get_content_type_for_model(obj)
              log.object_id = obj.pk
              log.object_repr = force_text(obj)
          log.save()
  
  
  class BaseAdminPlugin(BaseAdminObject):
  
      def __init__(self, admin_view):
          self.admin_view = admin_view
          self.admin_site = admin_view.admin_site
  
          if hasattr(admin_view, 'model'):
              self.model = admin_view.model
              self.opts = admin_view.model._meta
  
      def init_request(self, *args, **kwargs):
          pass
  
  
  class BaseAdminView(BaseAdminObject, View):
      """ Base Admin view, support some comm attrs."""
  
      base_template = 'xadmin/base.html'
      need_site_permission = True
  
      def __init__(self, request, *args, **kwargs):
          self.request = request
          self.request_method = request.method.lower()
          self.user = request.user
          
          self.base_plugins = [p(self) for p in getattr(self,
                                                        "plugin_classes", [])]
  
          self.args = args
          self.kwargs = kwargs
          self.init_plugin(*args, **kwargs)
          self.init_request(*args, **kwargs)
  
      @classonlymethod
      def as_view(cls):
          def view(request, *args, **kwargs):
              self = cls(request, *args, **kwargs)
  
              if hasattr(self, 'get') and not hasattr(self, 'head'):
                  self.head = self.get
  
              if self.request_method in self.http_method_names:
                  handler = getattr(
                      self, self.request_method, self.http_method_not_allowed)
              else:
                  handler = self.http_method_not_allowed
  
              return handler(request, *args, **kwargs)
  
          # take name and docstring from class
          update_wrapper(view, cls, updated=())
          view.need_site_permission = cls.need_site_permission
  
          return view
  
      def init_request(self, *args, **kwargs):
          pass
  
      def init_plugin(self, *args, **kwargs):
          plugins = []
          for p in self.base_plugins:
              p.request = self.request
              p.user = self.user
              p.args = self.args
              p.kwargs = self.kwargs
              result = p.init_request(*args, **kwargs)
              if result is not False:
                  plugins.append(p)
          self.plugins = plugins
  
      @filter_hook
      def get_context(self):
          return {'admin_view': self, 'media': self.media, 'base_template': self.base_template}
  
      @property
      def media(self):
          return self.get_media()
  
      @filter_hook
      def get_media(self):
          return forms.Media()
  
  
  class CommAdminView(BaseAdminView):
  
      base_template = 'xadmin/base_site.html'
      menu_template = 'xadmin/includes/sitemenu_default.html'
  
      site_title = getattr(settings,"XADMIN_TITLE",_(u"Django Xadmin"))
      site_footer = getattr(settings,"XADMIN_FOOTER_TITLE",_(u"my-company.inc"))
  
      global_models_icon = {}
      default_model_icon = None
      apps_label_title = {}
      apps_icons = {}
  
      def get_site_menu(self):
          return None
  
      @filter_hook
      def get_nav_menu(self):
          site_menu = list(self.get_site_menu() or [])
          had_urls = []
  
          def get_url(menu, had_urls):
              if 'url' in menu:
                  had_urls.append(menu['url'])
              if 'menus' in menu:
                  for m in menu['menus']:
                      get_url(m, had_urls)
          get_url({'menus': site_menu}, had_urls)
  
          nav_menu = OrderedDict()
  
          for model, model_admin in self.admin_site._registry.items():
              if getattr(model_admin, 'hidden_menu', False):
                  continue
              app_label = model._meta.app_label
              app_icon = None
              model_dict = {
                  'title': smart_text(capfirst(model._meta.verbose_name_plural)),
                  'url': self.get_model_url(model, "changelist"),
                  'icon': self.get_model_icon(model),
                  'perm': self.get_model_perm(model, 'view'),
                  'order': model_admin.order,
              }
              if model_dict['url'] in had_urls:
                  continue
  
              app_key = "app:%s" % app_label
              if app_key in nav_menu:
                  nav_menu[app_key]['menus'].append(model_dict)
              else:
                  # Find app title
                  app_title = smart_text(app_label.title())
                  if app_label.lower() in self.apps_label_title:
                      app_title = self.apps_label_title[app_label.lower()]
                  else:
                      app_title = smart_text(apps.get_app_config(app_label).verbose_name)
                  # find app icon
                  if app_label.lower() in self.apps_icons:
                      app_icon = self.apps_icons[app_label.lower()]
  
                  nav_menu[app_key] = {
                      'title': app_title,
                      'menus': [model_dict],
                  }
  
              app_menu = nav_menu[app_key]
              if app_icon:
                  app_menu['first_icon'] = app_icon
              elif ('first_icon' not in app_menu or
                      app_menu['first_icon'] == self.default_model_icon) and model_dict.get('icon'):
                  app_menu['first_icon'] = model_dict['icon']
  
              if 'first_url' not in app_menu and model_dict.get('url'):
                  app_menu['first_url'] = model_dict['url']
  
          for menu in nav_menu.values():
              menu['menus'].sort(key=sortkeypicker(['order', 'title']))
  
          nav_menu = list(nav_menu.values())
          nav_menu.sort(key=lambda x: x['title'])
  
          site_menu.extend(nav_menu)
  
          return site_menu
  
      @filter_hook
      def get_context(self):
          context = super(CommAdminView, self).get_context()
  
          if not settings.DEBUG and 'nav_menu' in self.request.session:
              nav_menu = json.loads(self.request.session['nav_menu'])
          else:
              menus = copy.copy(self.get_nav_menu())
  
              def check_menu_permission(item):
                  need_perm = item.pop('perm', None)
                  if need_perm is None:
                      return True
                  elif callable(need_perm):
                      return need_perm(self.user)
                  elif need_perm == 'super':
                      return self.user.is_superuser
                  else:
                      return self.user.has_perm(need_perm)
  
              def filter_item(item):
                  if 'menus' in item:
                      before_filter_length = len(item['menus'])
                      item['menus'] = [filter_item(
                          i) for i in item['menus'] if check_menu_permission(i)]
                      after_filter_length = len(item['menus'])
                      if after_filter_length == 0 and before_filter_length > 0:
                          return None
                  return item
  
              nav_menu = [filter_item(item) for item in menus if check_menu_permission(item)]
              nav_menu = list(filter(lambda x:x, nav_menu))
  
              if not settings.DEBUG:
                  self.request.session['nav_menu'] = json.dumps(nav_menu, cls=JSONEncoder, ensure_ascii=False)
                  self.request.session.modified = True
  
          def check_selected(menu, path):
              selected = False
              if 'url' in menu:
                  chop_index = menu['url'].find('?')
                  if chop_index == -1:
                      selected = path.startswith(menu['url'])
                  else:
                      selected = path.startswith(menu['url'][:chop_index])
              if 'menus' in menu:
                  for m in menu['menus']:
                      _s = check_selected(m, path)
                      if _s:
                          selected = True
              if selected:
                  menu['selected'] = True
              return selected
          for menu in nav_menu:
              check_selected(menu, self.request.path)
  
          context.update({
              'menu_template': self.menu_template,
              'nav_menu': nav_menu,
              'site_title': self.site_title,
              'site_footer': self.site_footer,
              'breadcrumbs': self.get_breadcrumb()
          })
  
          return context
  
      @filter_hook
      def get_model_icon(self, model):
          icon = self.global_models_icon.get(model)
          if icon is None and model in self.admin_site._registry:
              icon = getattr(self.admin_site._registry[model], 'model_icon', self.default_model_icon)
          return icon
  
      @filter_hook
      def get_breadcrumb(self):
          return [{'url': self.get_admin_url('index'), 'title': _('Home')}]
  
  
  class ModelAdminView(CommAdminView):
  
      fields = None
      exclude = None
      ordering = None
      model = None
      remove_permissions = []
  
      def __init__(self, request, *args, **kwargs):
          self.opts = self.model._meta
          self.app_label = self.model._meta.app_label
          self.model_name = self.model._meta.model_name
          self.model_info = (self.app_label, self.model_name)
  
          super(ModelAdminView, self).__init__(request, *args, **kwargs)
  
      @filter_hook
      def get_context(self):
          new_context = {
              "opts": self.opts,
              "app_label": self.app_label,
              "model_name": self.model_name,
              "verbose_name": force_text(self.opts.verbose_name),
              'model_icon': self.get_model_icon(self.model),
          }
          context = super(ModelAdminView, self).get_context()
          context.update(new_context)
          return context
  
      @filter_hook
      def get_breadcrumb(self):
          bcs = super(ModelAdminView, self).get_breadcrumb()
          item = {'title': self.opts.verbose_name_plural}
          if self.has_view_permission():
              item['url'] = self.model_admin_url('changelist')
          bcs.append(item)
          return bcs
  
      @filter_hook
      def get_object(self, object_id):
          """
          Get model object instance by object_id, used for change admin view
          """
          # first get base admin view property queryset, return default model queryset
          queryset = self.queryset()
          model = queryset.model
          try:
              object_id = model._meta.pk.to_python(object_id)
              return queryset.get(pk=object_id)
          except (model.DoesNotExist, ValidationError):
              return None
  
      @filter_hook
      def get_object_url(self, obj):
          if self.has_change_permission(obj):
              return self.model_admin_url("change", getattr(obj, self.opts.pk.attname))
          elif self.has_view_permission(obj):
              return self.model_admin_url("detail", getattr(obj, self.opts.pk.attname))
          else:
              return None
  
      def model_admin_url(self, name, *args, **kwargs):
          return reverse(
              "%s:%s_%s_%s" % (self.admin_site.app_name, self.opts.app_label,
              self.model_name, name), args=args, kwargs=kwargs)
  
      def get_model_perms(self):
          """
          Returns a dict of all perms for this model. This dict has the keys
          ``add``, ``change``, and ``delete`` mapping to the True/False for each
          of those actions.
          """
          return {
              'view': self.has_view_permission(),
              'add': self.has_add_permission(),
              'change': self.has_change_permission(),
              'delete': self.has_delete_permission(),
          }
  
      def get_template_list(self, template_name):
          opts = self.opts
          return (
              "xadmin/%s/%s/%s" % (
                  opts.app_label, opts.object_name.lower(), template_name),
              "xadmin/%s/%s" % (opts.app_label, template_name),
              "xadmin/%s" % template_name,
          )
  
      def get_ordering(self):
          """
          Hook for specifying field ordering.
          """
          return self.ordering or ()  # otherwise we might try to *None, which is bad ;)
  
      @filter_hook
      def queryset(self):
          """
          Returns a QuerySet of all model instances that can be edited by the
          admin site. This is used by changelist_view.
          """
          return self.model._default_manager.get_queryset()
  
      def has_view_permission(self, obj=None):
          view_codename = get_permission_codename('view', self.opts)
          change_codename = get_permission_codename('change', self.opts)
  
          return ('view' not in self.remove_permissions) and (self.user.has_perm('%s.%s' % (self.app_label, view_codename)) or \
              self.user.has_perm('%s.%s' % (self.app_label, change_codename)))
  
      def has_add_permission(self):
          codename = get_permission_codename('add', self.opts)
          return ('add' not in self.remove_permissions) and self.user.has_perm('%s.%s' % (self.app_label, codename))
  
      def has_change_permission(self, obj=None):
          codename = get_permission_codename('change', self.opts)
          return ('change' not in self.remove_permissions) and self.user.has_perm('%s.%s' % (self.app_label, codename))
  
      def has_delete_permission(self, obj=None):
          codename = get_permission_codename('delete', self.opts)
          return ('delete' not in self.remove_permissions) and self.user.has_perm('%s.%s' % (self.app_label, codename))
  