#
# This sets up how models are displayed
# in the web admin interface.
#
from django import forms
from django.conf import settings
from django.contrib import admin, messages
from django.contrib.admin.options import IS_POPUP_VAR
from django.contrib.admin.utils import unquote
from django.contrib.admin.widgets import FilteredSelectMultiple, ForeignKeyRawIdWidget
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpResponseRedirect
from django.template.response import TemplateResponse
from django.urls import path, reverse
from django.utils.decorators import method_decorator
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
from django.views.decorators.debug import sensitive_post_parameters
from evennia.accounts.models import AccountDB
from evennia.objects.models import ObjectDB
from evennia.utils import create
from . import utils as adminutils
from .attributes import AttributeInline
from .tags import TagInline
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
# handle the custom User editor
[docs]class AccountTagInline(TagInline):
"""
Inline Account Tags.
"""
model = AccountDB.db_tags.through
related_field = "accountdb"
[docs]class AccountAttributeInline(AttributeInline):
"""
Inline Account Attributes.
"""
model = AccountDB.db_attributes.through
related_field = "accountdb"
[docs]class ObjectPuppetInline(admin.StackedInline):
"""
Inline creation of puppet-Object in Account.
"""
from .objects import ObjectCreateForm
verbose_name = "Puppeted Object"
model = ObjectDB
view_on_site = False
show_change_link = True
# template = "admin/accounts/stacked.html"
form = ObjectCreateForm
fieldsets = (
(
None,
{
"fields": (
("db_key", "db_typeclass_path"),
("db_location", "db_home", "db_destination"),
"db_cmdset_storage",
"db_lock_storage",
),
"description": "Object currently puppeted by the account (note that this "
"will go away if account logs out or unpuppets)",
},
),
)
extra = 0
readonly_fields = (
"db_key",
"db_typeclass_path",
"db_destination",
"db_location",
"db_home",
"db_account",
"db_cmdset_storage",
"db_lock_storage",
)
# disable adding/deleting this inline - read-only!
[docs] def has_add_permission(self, request, obj=None):
return False
[docs] def has_delete_permission(self, request, obj=None):
return False
[docs]@admin.register(AccountDB)
class AccountAdmin(BaseUserAdmin):
"""
This is the main creation screen for Users/accounts
"""
list_display = (
"id",
"username",
"is_staff",
"is_superuser",
"db_typeclass_path",
"db_date_created",
)
list_display_links = ("id", "username")
form = AccountChangeForm
add_form = AccountCreationForm
search_fields = ["=id", "^username", "db_typeclass_path"]
ordering = ["-db_date_created", "id"]
list_filter = ["is_superuser", "is_staff", "db_typeclass_path"]
inlines = [AccountTagInline, AccountAttributeInline]
readonly_fields = ["db_date_created", "serialized_string", "puppeted_objects"]
view_on_site = False
fieldsets = (
(
None,
{
"fields": (
("username", "db_typeclass_path"),
"password",
"email",
"db_date_created",
"db_lock_storage",
"db_cmdset_storage",
"puppeted_objects",
"serialized_string",
)
},
),
(
"Admin/Website properties",
{
"fields": (
("first_name", "last_name"),
"last_login",
"date_joined",
"is_active",
"is_staff",
"is_superuser",
"user_permissions",
"groups",
),
"description": "<i>Used by the website/Django admin. "
"Except for `superuser status`, the permissions are not used in-game.</i>",
},
),
)
add_fieldsets = (
(
None,
{
"fields": ("username", "password1", "password2", "email"),
"description": "<i>These account details are shared by the admin "
"system and the game.</i>",
},
),
)
[docs] def serialized_string(self, obj):
"""
Get the serialized version of the object.
"""
from evennia.utils import dbserialize
return str(dbserialize.pack_dbobj(obj))
serialized_string.help_text = (
"Copy & paste this string into an Attribute's `value` field to store this account there."
)
[docs] def puppeted_objects(self, obj):
"""
Get any currently puppeted objects (read only list)
"""
return mark_safe(
", ".join(
'<a href="{url}">{name}</a>'.format(
url=reverse("admin:objects_objectdb_change", args=[obj.id]), name=obj.db_key
)
for obj in ObjectDB.objects.filter(db_account=obj)
)
)
puppeted_objects.help_text = (
"Objects currently puppeted by this Account. "
"Link new ones from the `Objects` admin page.<BR>"
"Note that these will disappear when a user unpuppets or goes offline - "
"this is normal."
)
[docs] @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,
)
[docs] def save_model(self, request, obj, form, change):
"""
Custom save actions.
Args:
request (Request): Incoming request.
obj (Object): Object to save.
form (Form): Related form instance.
change (bool): False if this is a new save and not an update.
"""
obj.save()
if not change:
# calling hooks for new account
obj.set_class_from_typeclass(typeclass_path=settings.BASE_ACCOUNT_TYPECLASS)
obj.basetype_setup()
obj.at_account_creation()
[docs] def response_add(self, request, obj, post_url_continue=None):
from django.http import HttpResponseRedirect
from django.urls import reverse
return HttpResponseRedirect(reverse("admin:accounts_accountdb_change", args=[obj.id]))