Fix for typeclass app_label, and admin fix. Resolves #2112.

This commit is contained in:
Griatch 2020-06-26 21:17:07 +02:00
parent 2e4f7e5744
commit 1e692d1bf5
2 changed files with 108 additions and 13 deletions

View file

@ -4,13 +4,26 @@
#
from django import forms
from django.conf import settings
from django.contrib import admin
from django.contrib import admin, messages
from django.contrib.admin.options import IS_POPUP_VAR
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.contrib.admin.utils import unquote
from django.template.response import TemplateResponse
from django.http import Http404, HttpResponseRedirect
from django.core.exceptions import PermissionDenied
from django.views.decorators.debug import sensitive_post_parameters
from django.utils.decorators import method_decorator
from django.utils.html import escape
from django.urls import path, reverse
from django.contrib.auth import update_session_auth_hash
from evennia.accounts.models import AccountDB
from evennia.typeclasses.admin import AttributeInline, TagInline
from evennia.utils import create
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
# handle the custom User editor
class AccountDBChangeForm(UserChangeForm):
@ -260,6 +273,71 @@ class AccountDBAdmin(BaseUserAdmin):
),
)
@sensitive_post_parameters_m
def user_change_password(self, request, id, form_url=''):
user = self.get_object(request, unquote(id))
if not self.has_change_permission(request, user):
raise PermissionDenied
if user is None:
raise Http404('%(name)s object with primary key %(key)r does not exist.') % {
'name': self.model._meta.verbose_name,
'key': escape(id),
}
if request.method == 'POST':
form = self.change_password_form(user, request.POST)
if form.is_valid():
form.save()
change_message = self.construct_change_message(request, form, None)
self.log_change(request, user, change_message)
msg = 'Password changed successfully.'
messages.success(request, msg)
update_session_auth_hash(request, form.user)
return HttpResponseRedirect(
reverse(
'%s:%s_%s_change' % (
self.admin_site.name,
user._meta.app_label,
# the model_name is something we need to hardcode
# since our accountdb is a proxy:
"accountdb",
),
args=(user.pk,),
)
)
else:
form = self.change_password_form(user)
fieldsets = [(None, {'fields': list(form.base_fields)})]
adminForm = admin.helpers.AdminForm(form, fieldsets, {})
context = {
'title': 'Change password: %s' % escape(user.get_username()),
'adminForm': adminForm,
'form_url': form_url,
'form': form,
'is_popup': (IS_POPUP_VAR in request.POST or
IS_POPUP_VAR in request.GET),
'add': True,
'change': False,
'has_delete_permission': False,
'has_change_permission': True,
'has_absolute_url': False,
'opts': self.model._meta,
'original': user,
'save_as': False,
'show_save': True,
**self.admin_site.each_context(request),
}
request.current_app = self.admin_site.name
return TemplateResponse(
request,
self.change_user_password_template or
'admin/auth/user/change_password.html',
context,
)
def save_model(self, request, obj, form, change):
"""
Custom save actions.

View file

@ -104,18 +104,24 @@ class TypeclassBase(SharedMemoryModelBase):
attrs["typename"] = name
attrs["path"] = "%s.%s" % (attrs["__module__"], name)
# typeclass proxy setup
app_label = None
# first check explicit __applabel__ on the typeclass
if "__applabel__" not in attrs:
# find the app-label in one of the bases, usually the dbmodel
def _get_dbmodel(bases):
"""Recursively get the dbmodel"""
if not hasattr(bases, "__iter__"):
bases = [bases]
for base in bases:
try:
attrs["__applabel__"] = base.__applabel__
except AttributeError:
pass
else:
break
if base._meta.proxy or base._meta.abstract:
for kls in base._meta.parents:
return _get_dbmodel(kls)
return base
dbmodel = _get_dbmodel(bases)
# typeclass proxy setup
# first check explicit __applabel__ on the typeclass, then figure
# it out from the dbmodel
if dbmodel and "__applabel__" not in attrs:
# find the app-label in one of the bases, usually the dbmodel
attrs["__applabel__"] = dbmodel._meta.app_label
if "Meta" not in attrs:
class Meta:
@ -127,9 +133,20 @@ class TypeclassBase(SharedMemoryModelBase):
new_class = ModelBase.__new__(cls, name, bases, attrs)
# django doesn't support inheriting proxy models so we hack support for
# it here by injecting `proxy_for_model` to the actual dbmodel.
# Unfortunately we cannot also set the correct model_name, because this
# would block multiple-inheritance of typeclasses (Django doesn't allow
# multiple bases of the same model).
if dbmodel:
new_class._meta.proxy_for_model = dbmodel
# Maybe Django will eventually handle this in the future:
# new_class._meta.model_name = dbmodel._meta.model_name
# attach signals
signals.post_save.connect(call_at_first_save, sender=new_class)
signals.pre_delete.connect(remove_attributes_on_delete, sender=new_class)
signals.pre_delete.connect(
remove_attributes_on_delete, sender=new_class)
return new_class