import json import traceback from django import forms from django.contrib import admin from django.contrib import messages from django.contrib.admin import widgets from django.core.files.storage import default_storage from django.urls import path, reverse from django.template.response import TemplateResponse from django.utils import timezone from django.utils.encoding import force_str from django.utils.translation import gettext_lazy as _ from django.utils.html import format_html from django.conf import settings from django.http.response import HttpResponse, HttpResponseForbidden, \ HttpResponseBadRequest from adminlteui.widgets import AdminlteSelect from treebeard.admin import TreeAdmin from treebeard.forms import movenodeform_factory from .models import Options, Menu, ContentType def get_option(option_name): try: if Options.objects.filter(option_name=option_name): return Options.objects.get(option_name=option_name).option_value except Exception: return None def handle_uploaded_file(f, file_name): # save site_logo return default_storage.save(file_name, f) class ImageBox: # for ClearableFileInput initial def __init__(self, name): self.url = '/' + name self.value = name def __str__(self): return '{}'.format(self.value) def get_image_box(): return ImageBox(get_option(option_name='site_logo')) if get_option( option_name='site_logo') else '' class ModelAdmin(admin.ModelAdmin): select2_list_filter = () search_field_placeholder = '' class Media: css = { "all": ("admin/components/select2/dist/css/select2.min.css",) } js = ( "admin/components/select2/dist/js/select2.min.js", ) def changelist_view(self, request, extra_context=None): view = super().changelist_view(request, extra_context) if hasattr(view, 'context_data'): cl = view.context_data.get('cl', None) if cl: cl.search_field_placeholder = self.search_field_placeholder filter_specs = cl.filter_specs for index, filter_spec in enumerate(filter_specs): if filter_spec.field_path in self.select2_list_filter: # flag to use select2 filter_spec.display_select2 = True cl.filter_specs[index] = filter_spec view.context_data['cl'] = cl return view class GeneralOptionForm(forms.Form): site_title = forms.CharField(label=_('Site Title'), widget=widgets.AdminTextInputWidget(), help_text=_( "Text to put at the end of each page's tag title.")) site_header = forms.CharField(label=_('Site Header'), widget=widgets.AdminTextInputWidget(), help_text=_( "Text to put in base page's tag b.")) # index_title = forms.CharField(label=_('Index Title'), # widget=widgets.AdminTextInputWidget()) site_logo = forms.ImageField(label=_('Site Logo'), widget=forms.ClearableFileInput(), required=False, help_text=_( "Transparent background picture is a good choice.")) welcome_sign = forms.CharField( label=_('Welcome Sign'), widget=widgets.AdminTextInputWidget(), help_text=_("Login page welcome sign.") ) avatar_field = forms.CharField(label=_('Avatar Field'), widget=widgets.AdminTextInputWidget(), required=False, help_text=_( "which field is avatar.")) show_avatar = forms.BooleanField( label=_('Show Avatar'), required=False) def save(self): try: # clear site-logo if self.data.get('site_logo-clear'): obj = Options.objects.get(option_name='site_logo') obj.delete() self.changed_data.remove('site_logo') if not self.data.get('show_avatar'): try: obj = Options.objects.get(option_name='show_avatar') obj.option_value = 'off' obj.save() except Exception: obj = Options.objects.create(option_name='show_avatar', option_value='off') obj.save() for data_item in self.changed_data: try: obj = Options.objects.get(option_name=data_item) if data_item == 'site_logo': if not settings.MEDIA_ROOT or not settings.MEDIA_URL: self.errors[data_item] = _( 'site_logo depends on setting.MEDIA_URL and setting.MEDIA_ROOT.') return False if not self.files.get(data_item) or self.data.get( data_item) == '': continue filename = handle_uploaded_file(self.files.get(data_item), self.files.get(data_item).name) obj.option_value = settings.MEDIA_URL + filename else: if obj.option_value == self.data.get(data_item): continue obj.option_value = self.data.get(data_item) except Options.DoesNotExist: if data_item == 'site_logo': if not settings.MEDIA_ROOT or not settings.MEDIA_URL: self.errors[data_item] = _( 'site_logo depends on setting.MEDIA_URL and setting.MEDIA_ROOT.') return False filename = handle_uploaded_file(self.files.get(data_item), self.files.get(data_item).name) obj = Options.objects.create( option_name=data_item, option_value=settings.MEDIA_URL + filename, create_time=timezone.now()) else: obj = Options.objects.create( option_name=data_item, option_value=self.data.get(data_item), create_time=timezone.now()) obj.save() return True except Exception as e: traceback.print_exc() # self.errors = e return False @admin.register(Options) class OptionsAdmin(admin.ModelAdmin): list_display = ('option_name', 'valid', 'update_time', 'create_time') search_fields = ('option_name', 'option_value') list_filter = ('valid',) list_editable = ('valid',) def get_urls(self): base_urls = super().get_urls() urls = [ path('general_option/', self.admin_site.admin_view( self.general_option_view, cacheable=True), name='general_option'), ] return urls + base_urls def general_option_view(self, request): if request.user.has_perm('django_admin_settings.add_options') is False \ and request.user.has_perm( 'django_admin_settings.change_options') is False: return HttpResponseForbidden(format_html('<h1>403 Forbidden</h1>')) context = dict( self.admin_site.each_context(request), ) if request.method == 'GET': initial_value = { 'site_title': get_option( option_name='site_title') or admin.AdminSite.site_title, 'site_header': get_option( option_name='site_header') or admin.AdminSite.site_header, # 'index_title': get_option( # option_name='index_title') or admin.AdminSite.index_title, 'welcome_sign': get_option(option_name='welcome_sign'), 'site_logo': ImageBox( get_option(option_name='site_logo')) if get_option( option_name='site_logo') else '', 'show_avatar': True if get_option( option_name='show_avatar') == 'on' else False, 'avatar_field': get_option( option_name='avatar_field') or 'request.user.head_avatar', } form = GeneralOptionForm(initial=initial_value) else: form = GeneralOptionForm( request.POST, request.FILES ) if form.save(): initial_value = { 'site_title': get_option( option_name='site_title') or admin.AdminSite.site_title, 'site_header': get_option( option_name='site_header') or admin.AdminSite.site_header, # 'index_title': get_option( # option_name='index_title') or admin.AdminSite.index_title, 'welcome_sign': get_option(option_name='welcome_sign'), 'site_logo': ImageBox( get_option(option_name='site_logo')) if get_option( option_name='site_logo') else '', 'show_avatar': True if get_option( option_name='show_avatar') == 'on' else False, 'avatar_field': get_option( option_name='avatar_field') or 'request.user.head_avatar', } form = GeneralOptionForm(initial=initial_value) messages.add_message(request, messages.SUCCESS, _('General Option Saved.')) else: messages.add_message(request, messages.ERROR, _('General Option Save Failed.')) context['line'] = form return TemplateResponse(request, 'adminlte/general_option.html', context) @admin.register(Menu) class MenuAdmin(TreeAdmin): list_display = ('name', 'position', 'link_type', 'display_link', 'display_content_type', 'priority_level', 'display_icon', 'valid') list_filter = ('position', 'link_type', 'valid') list_editable = ('valid', 'priority_level') form = movenodeform_factory(Menu) change_list_template = 'adminlte/menu_change_list.html' change_form_template = 'adminlte/menu_change_form.html' class ContentTypeModelChoiceField(forms.ModelChoiceField): def label_from_instance(self, obj): return "%s:%s" % (obj.app_label, obj.model) def formfield_for_foreignkey(self, db_field, request, **kwargs): """ reset content_type display text """ if db_field.name == 'content_type': return self.ContentTypeModelChoiceField( queryset=ContentType.objects.all(), required=False, label=_('ContentType'), widget=AdminlteSelect) return super().formfield_for_foreignkey(db_field, request, **kwargs) def changeform_view(self, request, object_id=None, form_url='', extra_context=None): try: return super().changeform_view(request, object_id, form_url, extra_context) except Exception as e: messages.error(request, _('Exception raised while add node: %s') % _( force_str(e))) return HttpResponseBadRequest( _('Exception raised while add node: %s') % _(force_str(e))) def get_urls(self): base_urls = super().get_urls() urls = [ path('exchange_menu/', self.admin_site.admin_view( self.exchange_menu_view, cacheable=True), name='exchange_menu'), ] return urls + base_urls def exchange_menu_view(self, request): if request.user.has_perm('django_admin_settings.view_menu') is False: return HttpResponseForbidden(format_html('<h1>403 Forbidden</h1>')) if request.is_ajax(): response_data = dict() response_data['message'] = 'success' try: use_custom_menu = Options.objects.get( option_name='USE_CUSTOM_MENU') except Options.DoesNotExist: use_custom_menu = Options.objects.create( option_name='USE_CUSTOM_MENU', option_value='0' ) if not use_custom_menu or use_custom_menu.option_value == '0': use_custom_menu.option_value = '1' use_custom_menu.save() messages.add_message(request, messages.SUCCESS, _( 'Menu exchanged, current is `custom menu`.')) else: use_custom_menu.option_value = '0' use_custom_menu.save() messages.add_message(request, messages.SUCCESS, _( 'Menu exchanged, current is `system menu`.')) return HttpResponse(json.dumps(response_data), content_type="application/json,charset=utf-8") return HttpResponse('method not allowed.') def display_link(self, obj): if obj.link: if '/' in obj.link: return format_html( '<i class="fa fa-check text-green"></i> {}'.format( obj.link)) try: reverse(obj.link) return format_html( '<i class="fa fa-check text-green"></i> {}'.format( obj.link)) except Exception as e: return format_html( '<i class="fa fa-close text-red"></i> {}'.format(obj.link)) return '-' display_link.short_description = _('Link') def display_icon(self, obj): if obj.icon: return format_html( '<i class="fa {}"></i> {}'.format(obj.icon, obj.icon)) return obj.icon display_icon.short_description = _('Icon') def display_content_type(self, obj): if obj.content_type: return '{}:{}'.format(obj.content_type.app_label, obj.content_type.model) return obj.content_type_id display_content_type.short_description = _('ContentType')