Add account-object link button to admin
This commit is contained in:
parent
14968e4b42
commit
a7f1e24c9c
4 changed files with 167 additions and 46 deletions
|
|
@ -6,7 +6,7 @@ from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.contrib.admin.options import IS_POPUP_VAR
|
from django.contrib.admin.options import IS_POPUP_VAR
|
||||||
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
|
from django.contrib.admin.widgets import ForeignKeyRawIdWidget, FilteredSelectMultiple
|
||||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
|
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
|
||||||
|
|
@ -236,6 +236,39 @@ class AccountAttributeInline(AttributeInline):
|
||||||
model = AccountDB.db_attributes.through
|
model = AccountDB.db_attributes.through
|
||||||
related_field = "accountdb"
|
related_field = "accountdb"
|
||||||
|
|
||||||
|
class ObjectPuppetInline(admin.StackedInline):
|
||||||
|
"""
|
||||||
|
Inline creation of puppet-Object in Account.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from .objects import ObjectCreateForm
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
|
||||||
@admin.register(AccountDB)
|
@admin.register(AccountDB)
|
||||||
class AccountAdmin(BaseUserAdmin):
|
class AccountAdmin(BaseUserAdmin):
|
||||||
|
|
@ -246,8 +279,8 @@ class AccountAdmin(BaseUserAdmin):
|
||||||
list_display = ("username", "email", "is_staff", "is_superuser")
|
list_display = ("username", "email", "is_staff", "is_superuser")
|
||||||
form = AccountChangeForm
|
form = AccountChangeForm
|
||||||
add_form = AccountCreationForm
|
add_form = AccountCreationForm
|
||||||
inlines = [AccountTagInline, AccountAttributeInline]
|
inlines = [AccountTagInline, AccountAttributeInline, ObjectPuppetInline]
|
||||||
readonly_fields = ["db_date_created", "serialized_string"]
|
readonly_fields = ["db_date_created", "serialized_string", "link_button"]
|
||||||
view_on_site = False
|
view_on_site = False
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,8 @@ class MsgAdmin(admin.ModelAdmin):
|
||||||
save_as = True
|
save_as = True
|
||||||
save_on_top = True
|
save_on_top = True
|
||||||
list_select_related = True
|
list_select_related = True
|
||||||
|
view_on_site = False
|
||||||
|
|
||||||
raw_id_fields = (
|
raw_id_fields = (
|
||||||
"db_date_created", "db_sender_accounts",
|
"db_date_created", "db_sender_accounts",
|
||||||
"db_sender_objects", "db_sender_scripts",
|
"db_sender_objects", "db_sender_scripts",
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,16 @@
|
||||||
# This sets up how models are displayed
|
# This sets up how models are displayed
|
||||||
# in the web admin interface.
|
# in the web admin interface.
|
||||||
#
|
#
|
||||||
from django import forms
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django import forms
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.conf import settings
|
||||||
|
from django.conf.urls import url
|
||||||
|
from django.contrib import admin, messages
|
||||||
from django.contrib.admin.utils import flatten_fieldsets
|
from django.contrib.admin.utils import flatten_fieldsets
|
||||||
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
|
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
|
||||||
|
from django.utils.html import format_html
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
|
|
@ -68,26 +73,66 @@ class ObjectCreateForm(forms.ModelForm):
|
||||||
"This string should be on the form "
|
"This string should be on the form "
|
||||||
"<i>type:lockfunction(args);type2:lockfunction2(args);...",
|
"<i>type:lockfunction(args);type2:lockfunction2(args);...",
|
||||||
)
|
)
|
||||||
|
|
||||||
db_cmdset_storage = forms.CharField(
|
db_cmdset_storage = forms.CharField(
|
||||||
label="CmdSet",
|
label="CmdSet",
|
||||||
initial="",
|
initial="",
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.TextInput(attrs={"size": "78"}),
|
widget=forms.TextInput(attrs={"size": "78"}),
|
||||||
help_text="Most non-character objects don't need a cmdset"
|
|
||||||
" and can leave this field blank.",
|
|
||||||
)
|
)
|
||||||
|
db_location = forms.ModelChoiceField(
|
||||||
db_account = forms.ModelChoiceField(
|
ObjectDB.objects.all(),
|
||||||
AccountDB.objects.all(),
|
label="Location",
|
||||||
label="Controlling Account",
|
|
||||||
required=False,
|
required=False,
|
||||||
widget=ForeignKeyRawIdWidget(
|
widget=ForeignKeyRawIdWidget(
|
||||||
ObjectDB._meta.get_field('db_account').remote_field, admin.site),
|
ObjectDB._meta.get_field('db_location').remote_field, admin.site),
|
||||||
help_text="Only needed for characters in MULTISESSION_MODE=1 or 2."
|
help_text="The (current) in-game location.<BR>"
|
||||||
|
"Usually a Room but can be<BR>"
|
||||||
|
"empty for un-puppeted Characters."
|
||||||
)
|
)
|
||||||
|
db_home = forms.ModelChoiceField(
|
||||||
|
ObjectDB.objects.all(),
|
||||||
|
label="Home",
|
||||||
|
required=False,
|
||||||
|
widget=ForeignKeyRawIdWidget(
|
||||||
|
ObjectDB._meta.get_field('db_location').remote_field, admin.site),
|
||||||
|
help_text="Fallback in-game location.<BR>"
|
||||||
|
"All objects should usually have<BR>"
|
||||||
|
"a home location."
|
||||||
|
)
|
||||||
|
db_destination = forms.ModelChoiceField(
|
||||||
|
ObjectDB.objects.all(),
|
||||||
|
label="Destination",
|
||||||
|
required=False,
|
||||||
|
widget=ForeignKeyRawIdWidget(
|
||||||
|
ObjectDB._meta.get_field('db_destination').remote_field, admin.site),
|
||||||
|
help_text="Only used by Exits."
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Tweak some fields dynamically.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# set default home
|
||||||
|
home_id = str(settings.DEFAULT_HOME)
|
||||||
|
home_id = home_id[1:] if home_id.startswith("#") else home_id
|
||||||
|
default_home = ObjectDB.objects.filter(id=home_id)
|
||||||
|
if default_home:
|
||||||
|
default_home = default_home[0]
|
||||||
|
self.fields["db_home"].initial = default_home
|
||||||
|
self.fields["db_location"].initial = default_home
|
||||||
|
|
||||||
|
# better help text for cmdset_storage
|
||||||
|
char_cmdset = settings.CMDSET_CHARACTER
|
||||||
|
account_cmdset = settings.CMDSET_ACCOUNT
|
||||||
|
self.fields["db_cmdset_storage"].help_text = (
|
||||||
|
"Path to Command-set path. Most non-character objects don't need a cmdset"
|
||||||
|
" and can leave this field blank. Some common cmdset-paths<BR> are "
|
||||||
|
f"<strong>{char_cmdset}</strong> and <strong>{account_cmdset}</strong>"
|
||||||
|
)
|
||||||
|
|
||||||
raw_id_fields = ("db_destination", "db_location", "db_home")
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectEditForm(ObjectCreateForm):
|
class ObjectEditForm(ObjectCreateForm):
|
||||||
|
|
@ -100,33 +145,16 @@ class ObjectEditForm(ObjectCreateForm):
|
||||||
model = ObjectDB
|
model = ObjectDB
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
|
db_account = forms.ModelChoiceField(
|
||||||
class ObjectInline(admin.StackedInline):
|
AccountDB.objects.all(),
|
||||||
"""
|
label="Puppeting Account",
|
||||||
Inline creation of Object.
|
required=False,
|
||||||
|
widget=ForeignKeyRawIdWidget(
|
||||||
"""
|
ObjectDB._meta.get_field('db_account').remote_field, admin.site),
|
||||||
model = ObjectDB
|
help_text="An Account puppeting this Object (if any).<BR>Note that when a user logs "
|
||||||
# template = "admin/accounts/stacked.html"
|
"off/unpuppets, this<BR>field will be empty again. This is normal."
|
||||||
form = ObjectCreateForm
|
|
||||||
fieldsets = (
|
|
||||||
(
|
|
||||||
None,
|
|
||||||
{
|
|
||||||
"fields": (
|
|
||||||
("db_key", "db_typeclass_path"),
|
|
||||||
("db_location", "db_home", "db_destination", "db_account"),
|
|
||||||
"db_cmdset_storage",
|
|
||||||
"db_lock_storage",
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
extra = 1
|
|
||||||
max_num = 1
|
|
||||||
raw_id_fields = ("db_destination", "db_location", "db_home", "db_account")
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(ObjectDB)
|
@admin.register(ObjectDB)
|
||||||
class ObjectAdmin(admin.ModelAdmin):
|
class ObjectAdmin(admin.ModelAdmin):
|
||||||
|
|
@ -141,7 +169,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||||
ordering = ["db_account", "db_typeclass_path", "id"]
|
ordering = ["db_account", "db_typeclass_path", "id"]
|
||||||
search_fields = ["=id", "^db_key", "db_typeclass_path", "^db_account__db_key"]
|
search_fields = ["=id", "^db_key", "db_typeclass_path", "^db_account__db_key"]
|
||||||
raw_id_fields = ("db_destination", "db_location", "db_home", "db_account")
|
raw_id_fields = ("db_destination", "db_location", "db_home", "db_account")
|
||||||
readonly_fields = ("serialized_string", )
|
readonly_fields = ("serialized_string", "link_button")
|
||||||
|
|
||||||
save_as = True
|
save_as = True
|
||||||
save_on_top = True
|
save_on_top = True
|
||||||
|
|
@ -158,7 +186,8 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||||
{
|
{
|
||||||
"fields": (
|
"fields": (
|
||||||
("db_key", "db_typeclass_path"),
|
("db_key", "db_typeclass_path"),
|
||||||
("db_location", "db_home", "db_destination", "db_account"),
|
("db_location", "db_home", "db_destination"),
|
||||||
|
("db_account", "link_button"),
|
||||||
"db_cmdset_storage",
|
"db_cmdset_storage",
|
||||||
"db_lock_storage",
|
"db_lock_storage",
|
||||||
"serialized_string"
|
"serialized_string"
|
||||||
|
|
@ -174,7 +203,7 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||||
{
|
{
|
||||||
"fields": (
|
"fields": (
|
||||||
("db_key", "db_typeclass_path"),
|
("db_key", "db_typeclass_path"),
|
||||||
("db_location", "db_home", "db_destination", "db_account"),
|
("db_location", "db_home", "db_destination"),
|
||||||
"db_cmdset_storage",
|
"db_cmdset_storage",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -227,6 +256,63 @@ class ObjectAdmin(admin.ModelAdmin):
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return super().get_form(request, obj, **defaults)
|
return super().get_form(request, obj, **defaults)
|
||||||
|
|
||||||
|
def get_urls(self):
|
||||||
|
urls = super().get_urls()
|
||||||
|
custom_urls = [
|
||||||
|
url(
|
||||||
|
r"^account-object-link/(?P<object_id>.+)/$",
|
||||||
|
self.admin_site.admin_view(self.link_object_to_account),
|
||||||
|
name="object-account-link"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
return custom_urls + urls
|
||||||
|
|
||||||
|
def link_button(self, obj):
|
||||||
|
return format_html(
|
||||||
|
'<a class="button" href="{}">Link to Account</a> ',
|
||||||
|
reverse("admin:object-account-link", args=[obj.pk])
|
||||||
|
)
|
||||||
|
link_button.short_description = "Create puppet links for MULTISESSION_MODE 0/1"
|
||||||
|
link_button.allow_tags = True
|
||||||
|
|
||||||
|
def link_object_to_account(self, request, object_id):
|
||||||
|
"""
|
||||||
|
Link object and account when pressing the button.
|
||||||
|
|
||||||
|
This will:
|
||||||
|
|
||||||
|
- Set account.db._last_puppet to this object
|
||||||
|
- Add object to account.db._playable_characters
|
||||||
|
- Change object locks to allow puppeting by account
|
||||||
|
|
||||||
|
"""
|
||||||
|
obj = self.get_object(request, object_id)
|
||||||
|
account = obj.db_account
|
||||||
|
|
||||||
|
if account:
|
||||||
|
account.db._last_puppet = obj
|
||||||
|
if not account.db._playable_characters:
|
||||||
|
account.db._playable_characters = []
|
||||||
|
if obj not in account.db._playable_characters:
|
||||||
|
account.db._playable_characters.append(obj)
|
||||||
|
if not obj.access(account, "puppet"):
|
||||||
|
lock = obj.locks.get("puppet")
|
||||||
|
lock += f" or pid({account.id})"
|
||||||
|
obj.locks.add(lock)
|
||||||
|
self.message_user(request,
|
||||||
|
"Did the following (where possible): "
|
||||||
|
f"Set Account.db._last_puppet = {obj}, "
|
||||||
|
f"Added {obj} to Account.db._playable_characters list, "
|
||||||
|
f"Added 'puppet:pid({account.id})' lock to {obj}.")
|
||||||
|
else:
|
||||||
|
self.message_user(request, "Account must be connected to set up puppet links "
|
||||||
|
"(set Puppeting Account and save this page first).", level=messages.ERROR)
|
||||||
|
|
||||||
|
# stay on the same page
|
||||||
|
return HttpResponseRedirect(reverse("admin:objects_objectdb_change", args=[obj.pk]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
"""
|
"""
|
||||||
Model-save hook.
|
Model-save hook.
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,9 @@
|
||||||
|
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
<h4><a href="{% url "admin:server_serverconfig_changelist" %}">ServerConfig</a></h4>
|
<h4><a href="{% url "admin:server_serverconfig_changelist" %}">ServerConfig</a></h4>
|
||||||
ServerConfigs store variables set by the running server. While possibly
|
ServerConfigs store variables set by the running server. While
|
||||||
interesting for debugging, you should usually not modify these
|
possibly interesting for debugging, you should usually not modify
|
||||||
manually unless you <i>really</i> know what you are doing. For
|
these manually unless you <i>really</i> know what you are doing. For
|
||||||
example, the <i>BASE_*_TYPECLASS</i> fields are stored in order to
|
example, the <i>BASE_*_TYPECLASS</i> fields are stored in order to
|
||||||
auto-update when their setting changes; they must <i>not</i> be
|
auto-update when their setting changes; they must <i>not</i> be
|
||||||
changed manually here.
|
changed manually here.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue