Fixed User/Player creation in Admin, much more intuitive now. Remains issues with creating attributes since these need to be pickled to/from the database in order to be properly handled.

This commit is contained in:
Griatch 2011-11-13 18:46:14 +01:00
parent ce0e3c4857
commit a60cd9bf1f
9 changed files with 420 additions and 185 deletions

View file

@ -6,52 +6,55 @@
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
from src.objects.models import ObjAttribute, ObjectDB from src.objects.models import ObjAttribute, ObjectDB, ObjectNick, Alias
from src.utils.utils import mod_import 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): class ObjAttributeInline(admin.TabularInline):
model = ObjAttribute model = ObjAttribute
fields = ('db_key', 'db_value') fields = ('db_key', 'db_value')
extra = 0 extra = 0
class ObjectEditForm(forms.ModelForm): class NickInline(admin.TabularInline):
"This form details the look of the fields" model = ObjectNick
class Meta: fields = ('db_nick', 'db_real', 'db_type')
model = ObjectDB extra = 0
db_typeclass_path = forms.CharField(label="Typeclass",
initial=settings.BASE_OBJECT_TYPECLASS, class AliasInline(admin.TabularInline):
widget=forms.TextInput(attrs={'size':'78'}), model = Alias
help_text="this defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass.") fields = ("db_key",)
db_permissions = forms.CharField(label="Permissions", extra = 0
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 ObjectCreateForm(forms.ModelForm): class ObjectCreateForm(forms.ModelForm):
"This form details the look of the fields" "This form details the look of the fields"
class Meta: class Meta:
model = ObjectDB model = ObjectDB
fields = ('db_key',) db_key = forms.CharField(label="Name/Key",
db_typeclass_path = forms.CharField(label="Typeclass",initial=settings.BASE_OBJECT_TYPECLASS,
widget=forms.TextInput(attrs={'size':'78'}), 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="Main identifier, like 'apple', 'strong guy', 'Elizabeth' etc. If creating a Character, check so the name is unique among characters!",)
db_permissions = forms.CharField(label="Permissions", initial=settings.PERMISSION_PLAYER_DEFAULT, required=False, 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. 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'}), 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.") 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 <i>type:lockfunction(args);type2:lockfunction2(args);...")
class ObjectDBAdmin(admin.ModelAdmin): class ObjectDBAdmin(admin.ModelAdmin):
@ -77,7 +80,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
) )
#deactivated temporarily, they cause empty objects to be created in admin #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. # Custom modification to give two different forms wether adding or not.

View file

@ -12,19 +12,19 @@ from django.contrib.admin import widgets
from django.contrib.auth.forms import UserChangeForm, UserCreationForm from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from src.players.models import PlayerDB, PlayerAttribute from src.players.models import PlayerDB, PlayerAttribute
from src.utils import logger, create
# remove User itself from admin site # remove User itself from admin site
admin.site.unregister(User) admin.site.unregister(User)
# handle the custom User editor # handle the custom User editor
class CustomUserChangeForm(UserChangeForm): class CustomUserChangeForm(UserChangeForm):
username = forms.RegexField(label="Username", username = forms.RegexField(label="Username",
max_length=30, max_length=30,
regex=r'^[\w. @+-]+$', regex=r'^[\w. @+-]+$',
widget=forms.TextInput(attrs={'size':'30'}), widget=forms.TextInput(attrs={'size':'30'}),
error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."}, 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): class CustomUserCreationForm(UserCreationForm):
username = forms.RegexField(label="Username", username = forms.RegexField(label="Username",
@ -32,166 +32,117 @@ class CustomUserCreationForm(UserCreationForm):
regex=r'^[\w. @+-]+$', regex=r'^[\w. @+-]+$',
widget=forms.TextInput(attrs={'size':'30'}), widget=forms.TextInput(attrs={'size':'30'}),
error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."}, error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."},
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 PlayerAttributeInline(admin.TabularInline):
# "Inline creation of player attributes"
# model = PlayerAttribute
# extra = 0
# form = PlayerAttributeForm
# fieldsets = (
# (None, {'fields' : (('db_key', 'db_value'))}),)
class PlayerForm(forms.ModelForm):
"Defines how to display Players"
class Meta:
model = PlayerDB
db_key = forms.RegexField(label="Username",
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 connected Player's key name. 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.") 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="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="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="In-game lock definition string. If not given, defaults will be used. This string should be on the form <i>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 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 = (
("In-game Permissions and Locks",
{'fields': ('db_permissions', 'db_lock_storage'),
'description':"<i>These are permissions/locks for in-game use. They are unrelated to website access rights.</i>"}),
("In-game Player data",
{'fields':('db_typeclass_path', 'db_cmdset_storage'),
'description':"<i>These fields define in-game-specific properties for the Player object in-game.</i>"}),
("Evennia In-game Character",
{'fields':('db_obj',),
'description': "<i>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!</i>"}))
extra = 1
max_num = 1
class UserAdmin(BaseUserAdmin): class UserAdmin(BaseUserAdmin):
"This will pop up from the Player admin." "This is the main creation screen for Users/players"
list_display = ('username','email', 'is_staff', 'is_superuser') list_display = ('username','email', 'is_staff', 'is_superuser')
form = CustomUserChangeForm form = CustomUserChangeForm
add_form = CustomUserCreationForm 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':"<i>These are not used in the default system.</i>"}),
('Website dates', {'fields': ('last_login', 'date_joined'),
'description':'<i>Relevant only to the website.</i>'}),
('Website Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser', 'user_permissions','groups'),
'description': "<i>These are permissions/permission groups for accessing the admin site. They are unrelated to in-game access rights.</i>"}),)
add_fieldsets = ( add_fieldsets = (
(None, (None,
{'fields': ('username', 'email', 'password1', 'password2', ('is_staff', 'is_superuser')), {'fields': ('username', 'password1', 'password2', 'email'),
'description':"The <i>User</i> object holds all authentication information and bits for using the admin site. A <i>superuser</i> account represents a 'God user' in-game. This User account should have the same username as its corresponding <i>Player</i> object has; the two are always uniquely connected to each other."},),) 'description':"<i>These account details are shared by the admin system and the game.</i>"},),)
admin.site.register(User, UserAdmin)
# The Player editor def save_formset(self, request, form, formset, change):
class PlayerAttributeForm(forms.ModelForm): "Run all hooks on the player object"
"Defines how to display the atttributes" super(UserAdmin, self).save_formset(request, form, formset, change)
class Meta: playerdb = form.instance.get_profile()
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 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'}),
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.")
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,
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.")
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.")
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
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]
add_form = PlayerCreateForm
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 <i>must</i> 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')}),)
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: if not change:
# adding a new object create.create_player("", "", "",
new_obj = obj.typeclass typeclass=playerdb.db_typeclass_path,
new_obj.basetype_setup() create_character=False,
new_obj.at_player_creation() player_dbobj=playerdb)
if new_obj.obj: if playerdb.db_obj:
char = new_obj.db_obj playerdb.db_obj.db_player = playerdb
char.db_player = obj playerdb.db_obj.save()
char.save()
new_obj.at_init()
else:
if obj.db_obj:
char = obj.db_obj
char.db_player = obj
char.save()
obj.at_init() #assert False, (form.instance, form.instance.get_profile())
def delete_model(self, request, obj): admin.site.register(User, UserAdmin)
# called when deleting a player object. Makes sure to also delete user.
user = obj.user
user.delete()
admin.site.register(PlayerDB, PlayerDBAdmin)

View file

@ -151,11 +151,11 @@ class PlayerDB(TypedObject):
help_text="The <I>User</I> 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.") help_text="The <I>User</I> 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). # the in-game object connected to this player (if any).
# Use the property 'obj' to access. # 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. # database storage of persistant cmdsets.
db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, 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 # Database manager
objects = manager.PlayerManager() objects = manager.PlayerManager()

View file

@ -351,13 +351,19 @@ def create_player(name, email, password,
is_superuser=False, is_superuser=False,
locks=None, permissions=None, locks=None, permissions=None,
create_character=True, character_typeclass=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 This creates a new player, handling the creation of the User
object and its associated Player object. 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 If create_character is
True, a game player object with the same name as the User/Player will True, a game player object with the same name as the User/Player will
also be created. Its typeclass and base properties can also be given. also be created. Its typeclass and base properties can also be given.
@ -414,6 +420,9 @@ def create_player(name, email, password,
# this is already an object typeclass, extract its path # this is already an object typeclass, extract its path
typeclass = typeclass.path typeclass = typeclass.path
if player_dbobj:
new_db_player = player_dbobj
else:
# create new database object # create new database object
new_db_player = PlayerDB(db_key=name, user=new_user) new_db_player = PlayerDB(db_key=name, user=new_user)
new_db_player.save() new_db_player.save()

View file

@ -20,10 +20,9 @@
{% if app.name == 'Auth' %} {% if app.name == 'Auth' %}
<h2>Admin</h2> <h2>Admin</h2>
<p><i>Note: Users hold django-specific authentication and should <p><i>Players are the out-of-character representation of a
not be created stand-alone. Groups game account. A Player can potentially control any number of
define permissions only relevant to admin-site access. in-game character Objects (depending on game).</i></p>
To create a new In-game user, create a new <b>Player</b></i>.</p>
{% endif %} {% endif %}
<div class="module"> <div class="module">
@ -31,10 +30,11 @@
<caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption> <caption><a href="{{ app.app_url }}" class="section">{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}</a></caption>
{% for model in app.models %} {% for model in app.models %}
<tr> <tr>
{% if model.name == "Users" %}
{% if model.perms.change %} {% if model.perms.change %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th> <th scope="row"><a href="{{ model.admin_url }}">Player</a></th>
{% else %} {% else %}
<th scope="row">{{ model.name }}</th> <th scope="row">Player</th>
{% endif %} {% endif %}
{% if model.perms.add %} {% if model.perms.add %}
@ -49,6 +49,7 @@
<td>&nbsp;</td> <td>&nbsp;</td>
{% endif %} {% endif %}
</tr> </tr>
{% endif %}
{% endfor %} {% endfor %}
</table> </table>

View file

@ -0,0 +1,14 @@
{% extends "admin/players/change_form.html" %}
{% load i18n %}
{% block form_top %}
{% if not is_popup %}
<p>{% trans "First, enter a username and password. Then you'll be able to edit more Player options." %}</p>
{% else %}
<p>{% trans "Enter a username and password." %}</p>
{% endif %}
{% endblock %}
{% block after_field_sets %}
<script type="text/javascript">document.getElementById("id_username").focus();</script>
{% endblock %}

View file

@ -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 %}
<script type="text/javascript" src="{{ jsi18nurl|default:"../../../jsi18n/" }}"></script>
{{ media }}
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% 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 %}
<div class="breadcrumbs">
<a href="../../../">{% trans "Home" %}</a> &rsaquo;
<a href="../../">{{ app_label|capfirst|escape }}</a> &rsaquo;
{% if has_change_permission %}<a href="../">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %} &rsaquo;
{% if add %}{% trans "Add" %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div>
{% endif %}{% endblock %}
{% block content %}<div id="content-main">
{% block object-tools %}
{% if change %}{% if not is_popup %}
<ul class="object-tools">
{% block object-tools-items %}
<li><a href="history/" class="historylink">{% trans "History" %}</a></li>
{% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
{% endblock %}
</ul>
{% endif %}{% endif %}
{% endblock %}
<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
<div>
{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
{% if save_on_top %}{% submit_row %}{% endif %}
{% if errors %}
<p class="errornote">
{% blocktrans count errors|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p>
{{ adminform.form.non_field_errors }}
{% endif %}
{% for fieldset in adminform %}
{% include "admin/includes/fieldset.html" %}
{% endfor %}
{% block after_field_sets %}{% endblock %}
{% for inline_admin_formset in inline_admin_formsets %}
{% include inline_admin_formset.opts.template %}
{% endfor %}
{% block after_related_objects %}{% endblock %}
{% submit_row %}
{% if adminform and add %}
<script type="text/javascript">document.getElementById("{{ adminform.first_field.id_for_label }}").focus();</script>
{% endif %}
{# JavaScript for prepopulated fields #}
{% prepopulated_fields_js %}
</div>
</form></div>
{% endblock %}

View file

@ -0,0 +1,104 @@
{% extends "admin/base_site.html" %}
{% load adminmedia admin_list i18n %}
{% load url from future %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/changelists.css" />
{% if cl.formset %}
<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />
{% endif %}
{% if cl.formset or action_form %}
{% url 'admin:jsi18n' as jsi18nurl %}
<script type="text/javascript" src="{{ jsi18nurl|default:'../../jsi18n/' }}"></script>
{% endif %}
{{ media.css }}
{% if not actions_on_top and not actions_on_bottom %}
<style>
#changelist table thead th:first-child {width: inherit}
</style>
{% endif %}
{% endblock %}
{% block extrahead %}
{{ block.super }}
{{ media.js }}
{% if action_form %}{% if actions_on_top or actions_on_bottom %}
<script type="text/javascript">
(function($) {
$(document).ready(function($) {
$("tr input.action-select").actions();
});
})(django.jQuery);
</script>
{% endif %}{% endif %}
{% endblock %}
{% block bodyclass %}change-list{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="../../">
{% trans "Home" %}
</a>
&rsaquo;
<a href="../">
{{ app_label|capfirst }}
</a>
&rsaquo;
{{ cl.opts.verbose_name_plural|capfirst }}
</div>
{% endblock %}
{% endif %}
{% block coltype %}flex{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
{% if has_add_permission %}
<ul class="object-tools">
{% block object-tools-items %}
<li>
<a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">
{% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
</a>
</li>
{% endblock %}
</ul>
{% endif %}
{% endblock %}
{% if cl.formset.errors %}
<p class="errornote">
{% blocktrans count cl.formset.errors|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
</p>
{{ cl.formset.non_form_errors }}
{% endif %}
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
{% block search %}{% search_form cl %}{% endblock %}
{% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
</div>
{% endif %}
{% endblock %}
<form id="changelist-form" action="" method="post"{% if cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %}>{% csrf_token %}
{% if cl.formset %}
<div>{{ cl.formset.management_form }}</div>
{% endif %}
{% block result_list %}
{% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %}
{% result_list cl %}
{% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %}
{% endblock %}
{% block pagination %}{% pagination cl %}{% endblock %}
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,82 @@
{% load i18n adminmedia %}
<div class="inline-group" id="{{ inline_admin_formset.formset.prefix }}-group">
<!--h2>{{ inline_admin_formset.opts.verbose_name_plural|title }}</h2-->
{{ inline_admin_formset.formset.management_form }}
{{ inline_admin_formset.formset.non_form_errors }}
{% for inline_admin_form in inline_admin_formset %}<div class="inline-related{% if forloop.last %} empty-form last-related{% endif %}" id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
<!--h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %}#{{ forloop.counter }}{% endif %}</span>
{% if inline_admin_form.show_url %}<a href="../../../r/{{ inline_admin_form.original_content_type_id }}/{{ inline_admin_form.original.id }}/">{% trans "View on site" %}</a>{% endif %}
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
</h3-->
{% if inline_admin_form.form.non_field_errors %}{{ inline_admin_form.form.non_field_errors }}{% endif %}
{% for fieldset in inline_admin_form %}
{% include "admin/includes/fieldset.html" %}
{% endfor %}
{% if inline_admin_form.has_auto_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
{{ inline_admin_form.fk_field.field }}
</div>{% endfor %}
</div>
<script type="text/javascript">
(function($) {
$(document).ready(function() {
var rows = "#{{ inline_admin_formset.formset.prefix }}-group .inline-related";
var updateInlineLabel = function(row) {
$(rows).find(".inline_label").each(function(i) {
var count = i + 1;
$(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
});
}
var reinitDateTimeShortCuts = function() {
// Reinitialize the calendar and clock widgets by force, yuck.
if (typeof DateTimeShortcuts != "undefined") {
$(".datetimeshortcuts").remove();
DateTimeShortcuts.init();
}
}
var updateSelectFilter = function() {
// If any SelectFilter widgets were added, instantiate a new instance.
if (typeof SelectFilter != "undefined"){
$(".selectfilter").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% admin_media_prefix %}");
});
$(".selectfilterstacked").each(function(index, value){
var namearr = value.name.split('-');
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% admin_media_prefix %}");
});
}
}
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this);
var input = field.find('input, select, textarea');
var dependency_list = input.data('dependency_list') || [];
var dependencies = [];
$.each(dependency_list, function(i, field_name) {
dependencies.push('#' + row.find(field_name).find('input, select, textarea').attr('id'));
});
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
}
$(rows).formset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
formCssClass: "dynamic-{{ inline_admin_formset.formset.prefix }}",
deleteCssClass: "inline-deletelink",
deleteText: "{% trans "Remove" %}",
emptyCssClass: "empty-form",
removed: updateInlineLabel,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
updateInlineLabel(row);
})
});
});
})(django.jQuery);
</script>