-
+ {% block object-tools-items %}
+
- {% trans "History" %} + {% if has_absolute_url %}
- {% trans "View on site" %} {% endif%} + {% endblock %} +
diff --git a/src/objects/admin.py b/src/objects/admin.py
index aad61004ab..995f005d04 100644
--- a/src/objects/admin.py
+++ b/src/objects/admin.py
@@ -6,52 +6,55 @@
from django import forms
from django.conf import settings
from django.contrib import admin
-from src.objects.models import ObjAttribute, ObjectDB
+from src.objects.models import ObjAttribute, ObjectDB, ObjectNick, Alias
from src.utils.utils import mod_import
-# class ObjectAttributeAdmin(admin.ModelAdmin):
-# list_display = ('id', 'db_key', 'db_obj')
-# list_display_links = ('id', 'db_key')
-# ordering = ('db_obj','db_key', 'id')
-# search_fields = ('^db_key', 'db_obj')
-# save_as = True
-# save_on_top = True
-# list_select_related = True
-# admin.site.register(ObjAttribute, ObjectAttributeAdmin)
class ObjAttributeInline(admin.TabularInline):
model = ObjAttribute
fields = ('db_key', 'db_value')
extra = 0
-class ObjectEditForm(forms.ModelForm):
- "This form details the look of the fields"
- class Meta:
- model = ObjectDB
- db_typeclass_path = forms.CharField(label="Typeclass",
- initial=settings.BASE_OBJECT_TYPECLASS,
- widget=forms.TextInput(attrs={'size':'78'}),
- help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.")
- db_permissions = forms.CharField(label="Permissions",
- required=False,
- widget=forms.TextInput(attrs={'size':'78'}),
- help_text="a comma-separated list of text strings checked by certain locks. They are mainly of use for Character objects. Character permissions overload permissions defined on a controlling Player. Most objects normally don't have any permissions defined.")
- db_lock_storage = forms.CharField(label="Locks",
- required=False,
- widget=forms.Textarea(attrs={'cols':'100', 'rows':'2'}),
- help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. Take a look at other Objects to see valid strings. An empty lock means no access is given to anything for anyone. ")
+class NickInline(admin.TabularInline):
+ model = ObjectNick
+ fields = ('db_nick', 'db_real', 'db_type')
+ extra = 0
+
+class AliasInline(admin.TabularInline):
+ model = Alias
+ fields = ("db_key",)
+ extra = 0
class ObjectCreateForm(forms.ModelForm):
"This form details the look of the fields"
class Meta:
model = ObjectDB
- fields = ('db_key',)
- db_typeclass_path = forms.CharField(label="Typeclass",initial=settings.BASE_OBJECT_TYPECLASS,
+ db_key = forms.CharField(label="Name/Key",
+ widget=forms.TextInput(attrs={'size':'78'}),
+ help_text="Main identifier, like 'apple', 'strong guy', 'Elizabeth' etc. If creating a Character, check so the name is unique among characters!",)
+ db_typeclass_path = forms.CharField(label="Typeclass",initial="Change to (for example) %s or %s." % (settings.BASE_OBJECT_TYPECLASS, settings.BASE_CHARACTER_TYPECLASS),
widget=forms.TextInput(attrs={'size':'78'}),
- help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.")
- db_permissions = forms.CharField(label="Permissions", initial=settings.PERMISSION_PLAYER_DEFAULT, required=False,
+ help_text="This defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass. If you are creating a Character you should use the typeclass defined by settings.BASE_CHARACTER_TYPECLASS or one derived from that.")
+ db_permissions = forms.CharField(label="Permissions",
+ initial=settings.PERMISSION_PLAYER_DEFAULT,
+ required=False,
widget=forms.TextInput(attrs={'size':'78'}),
help_text="a comma-separated list of text strings checked by certain locks. They are mainly of use for Character objects. Character permissions overload permissions defined on a controlling Player. Most objects normally don't have any permissions defined.")
+ db_cmdset_storage = forms.CharField(label="CmdSet",
+ initial=settings.CMDSET_DEFAULT,
+ required=False,
+ widget=forms.TextInput(attrs={'size':'78'}),
+ help_text="Most non-character objects don't need a cmdset and can leave this field blank.")
+
+
+
+class ObjectEditForm(ObjectCreateForm):
+ "Form used for editing. Extends the create one with more fields"
+
+ db_lock_storage = forms.CharField(label="Locks",
+ required=False,
+ widget=forms.Textarea(attrs={'cols':'100', 'rows':'2'}),
+ help_text="In-game lock definition string. If not given, defaults will be used. This string should be on the form type:lockfunction(args);type2:lockfunction2(args);...")
class ObjectDBAdmin(admin.ModelAdmin):
@@ -77,7 +80,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
)
#deactivated temporarily, they cause empty objects to be created in admin
- inlines = [ObjAttributeInline]
+ inlines = [AliasInline, ObjAttributeInline]
# Custom modification to give two different forms wether adding or not.
diff --git a/src/players/admin.py b/src/players/admin.py
index 29903ac84c..5d2106cf0a 100644
--- a/src/players/admin.py
+++ b/src/players/admin.py
@@ -12,19 +12,19 @@ from django.contrib.admin import widgets
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.contrib.auth.models import User
from src.players.models import PlayerDB, PlayerAttribute
-
+from src.utils import logger, create
+
# remove User itself from admin site
admin.site.unregister(User)
# handle the custom User editor
-
class CustomUserChangeForm(UserChangeForm):
username = forms.RegexField(label="Username",
max_length=30,
regex=r'^[\w. @+-]+$',
widget=forms.TextInput(attrs={'size':'30'}),
error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."},
- help_text = "This should be the same as the connected Player's key name. 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.")
+ help_text = "30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.")
class CustomUserCreationForm(UserCreationForm):
username = forms.RegexField(label="Username",
@@ -32,166 +32,117 @@ class CustomUserCreationForm(UserCreationForm):
regex=r'^[\w. @+-]+$',
widget=forms.TextInput(attrs={'size':'30'}),
error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."},
- help_text = "This should be the same as the connected Player's key name. 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.")
+ help_text = "30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.")
+# # The Player editor
+# class PlayerAttributeForm(forms.ModelForm):
+# "Defines how to display the atttributes"
+# class Meta:
+# model = PlayerAttribute
+# db_key = forms.CharField(label="Key",
+# widget=forms.TextInput(attrs={'size':'15'}))
+# db_value = forms.CharField(label="Value",
+# widget=forms.Textarea(attrs={'rows':'2'}))
-class UserAdmin(BaseUserAdmin):
- "This will pop up from the Player admin."
+# class PlayerAttributeInline(admin.TabularInline):
+# "Inline creation of player attributes"
+# model = PlayerAttribute
+# extra = 0
+# form = PlayerAttributeForm
+# fieldsets = (
+# (None, {'fields' : (('db_key', 'db_value'))}),)
- list_display = ('username', 'email', 'is_staff', 'is_superuser')
- form = CustomUserChangeForm
- add_form = CustomUserCreationForm
- add_fieldsets = (
- (None,
- {'fields': ('username', 'email', 'password1', 'password2', ('is_staff', 'is_superuser')),
- 'description':"The User object holds all authentication information and bits for using the admin site. A superuser account represents a 'God user' in-game. This User account should have the same username as its corresponding Player object has; the two are always uniquely connected to each other."},),)
-admin.site.register(User, UserAdmin)
-
-# The Player editor
-class PlayerAttributeForm(forms.ModelForm):
- "Defines how to display the atttributes"
- class Meta:
- model = PlayerAttribute
- db_key = forms.CharField(label="Key",
- widget=forms.TextInput(attrs={'size':'15'}))
- db_value = forms.CharField(label="Value",
- widget=forms.Textarea(attrs={'rows':'2'}))
-
-class PlayerAttributeInline(admin.TabularInline):
- "Inline creation of player attributes"
- model = PlayerAttribute
- extra = 0
- form = PlayerAttributeForm
- fieldsets = (
- (None, {'fields' : (('db_key', 'db_value'))}),)
-
-class PlayerEditForm(forms.ModelForm):
- "This form details the look of the fields"
+class PlayerForm(forms.ModelForm):
+ "Defines how to display Players"
class Meta:
- # important! This allows us to not excplicitly add all fields.
model = PlayerDB
-
db_key = forms.RegexField(label="Username",
- max_length=30, regex=r'^[\w. @+-]+$',
+ initial="PlayerDummy",
+ max_length=30,
+ regex=r'^[\w. @+-]+$',
+ required=False,
widget=forms.TextInput(attrs={'size':'30'}),
- error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."},
- help_text = "this should be the same as the User's name. 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.")
+ error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."},
+ help_text = "This should be the same as the connected Player's key name. 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.")
+
db_typeclass_path = forms.CharField(label="Typeclass",
initial=settings.BASE_PLAYER_TYPECLASS,
widget=forms.TextInput(attrs={'size':'78'}),
- help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.")
+ help_text="Required. Defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass. Defaults to settings.BASE_PLAYER_TYPECLASS.")
db_permissions = forms.CharField(label="Permissions",
initial=settings.PERMISSION_PLAYER_DEFAULT,
required=False,
widget=forms.TextInput(attrs={'size':'78'}),
- help_text="a comma-separated list of text strings checked by certain locks. They are often used for hierarchies, such as letting a Player have permission 'Wizards', 'Builders' etc. A Player permission can be overloaded by the permissions of a controlled Character. Normal players use 'Players' by default.")
+ help_text="In-game permissions. A comma-separated list of text strings checked by certain locks. They are often used for hierarchies, such as letting a Player have permission 'Wizards', 'Builders' etc. A Player permission can be overloaded by the permissions of a controlled Character. Normal players use 'Players' by default.")
db_lock_storage = forms.CharField(label="Locks",
widget=forms.Textarea(attrs={'cols':'100', 'rows':'2'}),
required=False,
- help_text="locks limit access to an entity. A lock is defined as a 'lock string' on the form 'type:lockfunctions', defining what functionality is locked and how to determine access. This is set to a default upon creation.")
+ help_text="In-game lock definition string. If not given, defaults will be used. This string should be on the form type:lockfunction(args);type2:lockfunction2(args);...")
db_cmdset_storage = forms.CharField(label="cmdset",
initial=settings.CMDSET_OOC,
widget=forms.TextInput(attrs={'size':'78'}),
required=False,
- help_text="python path to cmdset class.")
- user = forms.ModelChoiceField(queryset=User.objects.all(),
- widget=forms.Select(attrs={'disabled':'true'}))
-
-
-
-class PlayerCreateForm(forms.ModelForm):
- "This form details the look of the fields"
-
- class Meta:
- # important! This allows us to not excplicitly add all fields.
- model = PlayerDB
-
- db_key = forms.RegexField(label="Username", max_length=30, regex=r'^[\w. @+-]+$', widget=forms.TextInput(attrs={'size':'30'}),
- help_text = "this should be the same as the User's name. 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.")
- db_typeclass_path = forms.CharField(label="Typeclass",
- initial=settings.BASE_PLAYER_TYPECLASS,
- widget=forms.TextInput(attrs={'size':'78'}),
- help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.")
- db_permissions = forms.CharField(label="Permissions",
- initial=settings.PERMISSION_PLAYER_DEFAULT,
- required=False,
- help_text="a comma-separated list of text strings checked by certain locks. They are often used for hierarchies, such as letting a Player have permission 'Wizards', 'Builders' etc. A Player permission can be overloaded by the permissions of a controlled Character. Normal players use 'Players' by default.")
- db_cmdset_storage = forms.CharField(label="cmdset",
- initial=settings.CMDSET_OOC,
- widget=forms.TextInput(attrs={'size':'78'}),
- required=False,
- help_text="python path to cmdset class.")
-
-class PlayerDBAdmin(admin.ModelAdmin):
- "Setting up and tying the player administration together"
-
- list_display = ('id', 'db_key', 'user', 'db_obj', 'db_permissions', 'db_typeclass_path')
- list_display_links = ('id', 'db_key')
- ordering = ['db_key', 'db_typeclass_path']
- search_fields = ['^db_key', 'db_typeclass_path']
- save_as = True
- save_on_top = True
- list_select_related = True
- list_filter = ('db_permissions',)
-
-
- # editing/adding player
- form = PlayerEditForm
+ help_text="python path to player cmdset class (settings.CMDSET_OOC by default)")
+
+class PlayerInline(admin.StackedInline):
+ "Inline creation of Player"
+ model = PlayerDB
+ template = "admin/players/stacked.html"
+ form = PlayerForm
fieldsets = (
- (None,
- {'fields' : (('db_key', 'db_typeclass_path'), 'user', ('db_permissions','db_lock_storage'), 'db_cmdset_storage', 'db_obj'),
- 'classes' : ('wide', 'extrapretty')}),)
- # deactivated, they cause empty players to be created in admin.
- inlines = [PlayerAttributeInline]
+ ("In-game Permissions and Locks",
+ {'fields': ('db_permissions', 'db_lock_storage'),
+ 'description':"These are permissions/locks for in-game use. They are unrelated to website access rights."}),
+ ("In-game Player data",
+ {'fields':('db_typeclass_path', 'db_cmdset_storage'),
+ 'description':"These fields define in-game-specific properties for the Player object in-game."}),
+ ("Evennia In-game Character",
+ {'fields':('db_obj',),
+ 'description': "To actually play the game, a Player must control a Character. This could be added in-game instead of from here if some sort of character creation system is in play. If not, you should normally create a new Character here rather than assigning an existing one. Observe that the admin does not check for puppet-access rights when assigning Characters! If not creating a new Character, make sure the one you assign is not puppeted by someone else!"}))
- add_form = PlayerCreateForm
+
+ extra = 1
+ max_num = 1
+
+class UserAdmin(BaseUserAdmin):
+ "This is the main creation screen for Users/players"
+
+ list_display = ('username','email', 'is_staff', 'is_superuser')
+ form = CustomUserChangeForm
+ add_form = CustomUserCreationForm
+ inlines = [PlayerInline]
+ add_form_template = "admin/players/add_form.html"
+ change_form_template = "admin/players/change_form.html"
+ change_list_template = "admin/players/change_list.html"
+ fieldsets = (
+ (None, {'fields': ('username', 'password', 'email')}),
+ ('Website profile', {'fields': ('first_name', 'last_name'),
+ 'description':"These are not used in the default system."}),
+ ('Website dates', {'fields': ('last_login', 'date_joined'),
+ 'description':'Relevant only to the website.'}),
+ ('Website Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'user_permissions','groups'),
+ 'description': "These are permissions/permission groups for accessing the admin site. They are unrelated to in-game access rights."}),)
+
+
add_fieldsets = (
- (None,
- {'fields' : (('db_key', 'db_typeclass_path'), 'user', 'db_permissions', 'db_cmdset_storage', 'db_obj'),
- 'description': 'To create a new Player, a User object must also be created to match. Never connect a Player to a User already assigned to another Player. When deleting a Player, its connected User will also be deleted.',
- 'classes' : ('wide', 'extrapretty')}),)
+ (None,
+ {'fields': ('username', 'password1', 'password2', 'email'),
+ 'description':"These account details are shared by the admin system and the game."},),)
- def get_fieldsets(self, request, obj=None):
- if not obj:
- return self.add_fieldsets
- return super(PlayerDBAdmin, self).get_fieldsets(request, obj)
-
- def get_form(self, request, obj=None, **kwargs):
- """
- Use special form during creation
- """
- defaults = {}
- if obj is None:
- defaults.update({
- 'form': self.add_form,
- 'fields': admin.util.flatten_fieldsets(self.add_fieldsets),
- })
- defaults.update(kwargs)
- return super(PlayerDBAdmin, self).get_form(request, obj, **defaults)
-
- def save_model(self, request, obj, form, change):
- if not change:
- # adding a new object
- new_obj = obj.typeclass
- new_obj.basetype_setup()
- new_obj.at_player_creation()
- if new_obj.obj:
- char = new_obj.db_obj
- char.db_player = obj
- char.save()
- new_obj.at_init()
- else:
- if obj.db_obj:
- char = obj.db_obj
- char.db_player = obj
- char.save()
-
- obj.at_init()
-
- def delete_model(self, request, obj):
- # called when deleting a player object. Makes sure to also delete user.
- user = obj.user
- user.delete()
-
-admin.site.register(PlayerDB, PlayerDBAdmin)
+ def save_formset(self, request, form, formset, change):
+ "Run all hooks on the player object"
+ super(UserAdmin, self).save_formset(request, form, formset, change)
+ playerdb = form.instance.get_profile()
+ if not change:
+ create.create_player("", "", "",
+ typeclass=playerdb.db_typeclass_path,
+ create_character=False,
+ player_dbobj=playerdb)
+ if playerdb.db_obj:
+ playerdb.db_obj.db_player = playerdb
+ playerdb.db_obj.save()
+
+ #assert False, (form.instance, form.instance.get_profile())
+
+admin.site.register(User, UserAdmin)
diff --git a/src/players/models.py b/src/players/models.py
index 088a8242fa..cd8dcbc120 100644
--- a/src/players/models.py
+++ b/src/players/models.py
@@ -151,11 +151,11 @@ class PlayerDB(TypedObject):
help_text="The User object holds django-specific authentication for each Player. A unique User should be created and tied to each Player, the two should never be switched or changed around. The User will be deleted automatically when the Player is.")
# the in-game object connected to this player (if any).
# Use the property 'obj' to access.
- db_obj = models.ForeignKey("objects.ObjectDB", null=True, verbose_name="character", help_text='In-game object.')
+ db_obj = models.ForeignKey("objects.ObjectDB", null=True, blank=True, verbose_name="character", help_text='In-game object.')
# database storage of persistant cmdsets.
db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True,
- help_text="optional python path to a cmdset class.")
+ help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_DEFAULT.")
# Database manager
objects = manager.PlayerManager()
diff --git a/src/utils/create.py b/src/utils/create.py
index 2b57b5c704..9793a27822 100644
--- a/src/utils/create.py
+++ b/src/utils/create.py
@@ -351,12 +351,18 @@ def create_player(name, email, password,
is_superuser=False,
locks=None, permissions=None,
create_character=True, character_typeclass=None,
- character_location=None, character_home=None):
+ character_location=None, character_home=None,
+ player_dbobj=None):
"""
This creates a new player, handling the creation of the User
object and its associated Player object.
+
+ If player_dbobj is given, this player object is used instead of
+ creating a new one. This is called by the admin interface since it
+ needs to create the player object in order to relate it automatically
+ to the user.
If create_character is
True, a game player object with the same name as the User/Player will
@@ -414,9 +420,12 @@ def create_player(name, email, password,
# this is already an object typeclass, extract its path
typeclass = typeclass.path
- # create new database object
- new_db_player = PlayerDB(db_key=name, user=new_user)
- new_db_player.save()
+ if player_dbobj:
+ new_db_player = player_dbobj
+ else:
+ # create new database object
+ new_db_player = PlayerDB(db_key=name, user=new_user)
+ new_db_player.save()
# assign the typeclass
typeclass = utils.to_unicode(typeclass)
diff --git a/src/web/templates/admin/index.html b/src/web/templates/admin/index.html
index e5a757fdf4..d22920b15e 100644
--- a/src/web/templates/admin/index.html
+++ b/src/web/templates/admin/index.html
@@ -20,10 +20,9 @@
{% if app.name == 'Auth' %}
Admin
-
Note: Users hold django-specific authentication and should - not be created stand-alone. Groups - define permissions only relevant to admin-site access. - To create a new In-game user, create a new Player.
+Players are the out-of-character representation of a + game account. A Player can potentially control any number of + in-game character Objects (depending on game).
{% endif %}{% trans "First, enter a username and password. Then you'll be able to edit more Player options." %}
+ {% else %} +{% trans "Enter a username and password." %}
+ {% endif %} +{% endblock %} + +{% block after_field_sets %} + +{% endblock %} diff --git a/src/web/templates/admin/players/change_form.html b/src/web/templates/admin/players/change_form.html new file mode 100644 index 0000000000..4db01138dd --- /dev/null +++ b/src/web/templates/admin/players/change_form.html @@ -0,0 +1,71 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_modify adminmedia %} +{% load url from future %} + +{% block extrahead %}{{ block.super }} +{% url 'admin:jsi18n' as jsi18nurl %} + +{{ media }} +{% endblock %} + +{% block extrastyle %}{{ block.super }}{% endblock %} + +{% block coltype %}{% if ordered_objects %}colMS{% else %}colM{% endif %}{% endblock %} + +{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} +change-form{% endblock %} + +{% block breadcrumbs %}{% if not is_popup %} + +{% endif %}{% endblock %} +{% block content %}+ {% blocktrans count cl.formset.errors|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %} +
+ {{ cl.formset.non_form_errors }} + {% endif %} +