Fix for typeclass app_label, and admin fix. Resolves #2112.
This commit is contained in:
parent
e8b99175ad
commit
5e6c52e2e6
2 changed files with 108 additions and 13 deletions
|
|
@ -4,13 +4,26 @@
|
||||||
#
|
#
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
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.admin import UserAdmin as BaseUserAdmin
|
||||||
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
|
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.accounts.models import AccountDB
|
||||||
from evennia.typeclasses.admin import AttributeInline, TagInline
|
from evennia.typeclasses.admin import AttributeInline, TagInline
|
||||||
from evennia.utils import create
|
from evennia.utils import create
|
||||||
|
|
||||||
|
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
|
||||||
|
|
||||||
|
|
||||||
# handle the custom User editor
|
# handle the custom User editor
|
||||||
class AccountDBChangeForm(UserChangeForm):
|
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):
|
def save_model(self, request, obj, form, change):
|
||||||
"""
|
"""
|
||||||
Custom save actions.
|
Custom save actions.
|
||||||
|
|
|
||||||
|
|
@ -104,18 +104,24 @@ class TypeclassBase(SharedMemoryModelBase):
|
||||||
attrs["typename"] = name
|
attrs["typename"] = name
|
||||||
attrs["path"] = "%s.%s" % (attrs["__module__"], name)
|
attrs["path"] = "%s.%s" % (attrs["__module__"], name)
|
||||||
|
|
||||||
# typeclass proxy setup
|
def _get_dbmodel(bases):
|
||||||
app_label = None
|
"""Recursively get the dbmodel"""
|
||||||
# first check explicit __applabel__ on the typeclass
|
if not hasattr(bases, "__iter__"):
|
||||||
if "__applabel__" not in attrs:
|
bases = [bases]
|
||||||
# find the app-label in one of the bases, usually the dbmodel
|
|
||||||
for base in bases:
|
for base in bases:
|
||||||
try:
|
if base._meta.proxy or base._meta.abstract:
|
||||||
attrs["__applabel__"] = base.__applabel__
|
for kls in base._meta.parents:
|
||||||
except AttributeError:
|
return _get_dbmodel(kls)
|
||||||
pass
|
return base
|
||||||
else:
|
|
||||||
break
|
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:
|
if "Meta" not in attrs:
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -127,9 +133,20 @@ class TypeclassBase(SharedMemoryModelBase):
|
||||||
|
|
||||||
new_class = ModelBase.__new__(cls, name, bases, attrs)
|
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
|
# attach signals
|
||||||
signals.post_save.connect(call_at_first_save, sender=new_class)
|
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
|
return new_class
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue