diff --git a/src/commands/default/unloggedin.py b/src/commands/default/unloggedin.py index efdd2e4c1e..dc23596969 100644 --- a/src/commands/default/unloggedin.py +++ b/src/commands/default/unloggedin.py @@ -1,6 +1,7 @@ """ Commands that are available from the connect screen. """ +import re import traceback from django.conf import settings from django.contrib.auth.models import User @@ -121,9 +122,9 @@ class CmdCreate(MuxCommand): string = "\n\r Usage (without <>): create \"\" " session.msg(string) return - if not playername: - # entered an empty string - session.msg("\n\r You have to supply a longer playername, surrounded by quotes.") + if not re.findall('^[\w. @+-]+$', playername) or not (0 < len(playername) <= 30): + session.msg("\n\r Playername can max be 30 characters or fewer. Letters, spaces, dig\ +its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth module. return if not email or not password: session.msg("\n\r You have to supply an e-mail address followed by a password." ) diff --git a/src/comms/admin.py b/src/comms/admin.py index 14e923aaa1..4ce9886978 100644 --- a/src/comms/admin.py +++ b/src/comms/admin.py @@ -15,7 +15,6 @@ class MsgAdmin(admin.ModelAdmin): save_as = True save_on_top = True list_select_related = True - #admin.site.register(Msg, MsgAdmin) class PlayerChannelConnectionInline(admin.TabularInline): @@ -24,7 +23,8 @@ class PlayerChannelConnectionInline(admin.TabularInline): (None, { 'fields':(('db_player', 'db_channel')), 'classes':('collapse',)}),) - max_num = 1 + extra = 1 + class ExternalChannelConnectionInline(admin.StackedInline): model = ExternalChannelConnection fieldsets = ( @@ -32,7 +32,7 @@ class ExternalChannelConnectionInline(admin.StackedInline): 'fields':(('db_is_enabled','db_external_key', 'db_channel'), 'db_external_send_code', 'db_external_config'), 'classes':('collapse',) }),) - max_num = 1 + extra = 1 class ChannelAdmin(admin.ModelAdmin): inlines = (PlayerChannelConnectionInline, ExternalChannelConnectionInline) diff --git a/src/help/admin.py b/src/help/admin.py index 62014cd278..b82b12b0db 100644 --- a/src/help/admin.py +++ b/src/help/admin.py @@ -1,8 +1,24 @@ +""" +This defines how to edit help entries in Admin. +""" +from django import forms from django.contrib import admin from src.help.models import HelpEntry + + +class HelpEntryForm(forms.ModelForm): + "Defines how to display the help entry" + class Meta: + model = HelpEntry + db_help_category = forms.CharField(label="Help category", initial='General', + help_text="organizes help entries in lists") + db_lock_storage = forms.CharField(label="Locks", initial='view:all()',required=False, + widget=forms.TextInput(attrs={'size':'40'}),) + class HelpEntryAdmin(admin.ModelAdmin): - + "Sets up the admin manaager for help entries" + list_display = ('id', 'db_key', 'db_help_category', 'db_lock_storage') list_display_links = ('id', 'db_key') search_fields = ['^db_key', 'db_entrytext'] @@ -10,6 +26,8 @@ class HelpEntryAdmin(admin.ModelAdmin): save_as = True save_on_top = True list_select_related = True + + form = HelpEntryForm fieldsets = ( (None, {'fields':(('db_key', 'db_help_category'), 'db_entrytext', 'db_lock_storage'), 'description':"Sets a Help entry. Set lock to view:all() unless you want to restrict it."}),) diff --git a/src/help/models.py b/src/help/models.py index 184111fa9b..87cc74ac0c 100644 --- a/src/help/models.py +++ b/src/help/models.py @@ -42,16 +42,16 @@ class HelpEntry(SharedMemoryModel): # named same as the field, but withtout the db_* prefix. # title of the help - db_key = models.CharField('help key', max_length=255, unique=True, help_text='Key to search for.') + db_key = models.CharField('help key', max_length=255, unique=True, help_text='key to search for') # help category db_help_category = models.CharField("help category", max_length=255, default="General", - help_text='Organizes help entries in lists.') + help_text='organizes help entries in lists') # the actual help entry text, in any formatting. - db_entrytext = models.TextField('help entry', blank=True, help_text='The main body of help text.') - # a string of permissionstrings, separated by commas. + db_entrytext = models.TextField('help entry', blank=True, help_text='the main body of help text') + # a string of permissionstrings, separated by commas. Not used by help entries. db_permissions = models.CharField('permissions', max_length=255, blank=True) # lock string storage - db_lock_storage = models.CharField('locks', max_length=512, blank=True, help_text='Normally view:all().') + db_lock_storage = models.CharField('locks', max_length=512, blank=True, help_text='normally view:all().') # (deprecated, only here to allow MUX helpfile load (don't use otherwise)). # TODO: remove this when not needed anymore. db_staff_only = models.BooleanField(default=False) diff --git a/src/locks/lockhandler.py b/src/locks/lockhandler.py index f42121e1af..98610bf716 100644 --- a/src/locks/lockhandler.py +++ b/src/locks/lockhandler.py @@ -193,7 +193,11 @@ class LockHandler(object): wlist = [] # warnings for raw_lockstring in storage_lockstring.split(';'): lock_funcs = [] - access_type, rhs = (part.strip() for part in raw_lockstring.split(':', 1)) + try: + access_type, rhs = (part.strip() for part in raw_lockstring.split(':', 1)) + except ValueError: + logger.log_trace() + return locks # parse the lock functions and separators funclist = RE_FUNCS.findall(rhs) diff --git a/src/objects/admin.py b/src/objects/admin.py index b51ddcbe80..6cb6fee30e 100644 --- a/src/objects/admin.py +++ b/src/objects/admin.py @@ -3,15 +3,51 @@ # in the web admin interface. # -from src.objects.models import ObjAttribute, ObjectDB +from django import forms +from django.conf import settings from django.contrib import admin +from src.objects.models import ObjAttribute, ObjectDB +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') - max_num = 1 + extra = 1 +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, + 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, + 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':'1'}), + 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): + "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, + 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 mainly of use for Character objects. Character permissions overload permissions defined on a controlling Player. Most objects normally don't have any permissions defined.") + + class ObjectDBAdmin(admin.ModelAdmin): + list_display = ('id', 'db_key', 'db_location', 'db_player', 'db_typeclass_path') list_display_links = ('id', 'db_key') ordering = ['db_player', 'db_typeclass_path', 'id'] @@ -20,6 +56,56 @@ class ObjectDBAdmin(admin.ModelAdmin): save_as = True save_on_top = True list_select_related = True - inlines = [ObjAttributeInline] + + # editing fields setup + + form = ObjectEditForm + fieldsets = ( + (None, { + 'fields': (('db_key','db_typeclass_path'), ('db_permissions', 'db_lock_storage'), + ('db_location', 'db_home'), 'db_destination','db_cmdset_storage' + )}), + ) + + #deactivated temporarily, they cause empty objects to be created in admin + #inlines = [ObjAttributeInline] + + + # Custom modification to give two different forms wether adding or not. + + add_form = ObjectCreateForm + add_fieldsets = ( + (None, { + 'fields': (('db_key','db_typeclass_path'), 'db_permissions', + ('db_location', 'db_home'), 'db_destination','db_cmdset_storage' + )}), + ) + def get_fieldsets(self, request, obj=None): + if not obj: + return self.add_fieldsets + return super(ObjectDBAdmin, 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(ObjectDBAdmin, self).get_form(request, obj, **defaults) + + def save_model(self, request, obj, form, change): + if not change: + # adding a new object + obj = obj.typeclass + obj.basetype_setup() + obj.basetype_posthook_setup() + obj.at_object_creation() + obj.at_init() + admin.site.register(ObjectDB, ObjectDBAdmin) diff --git a/src/objects/models.py b/src/objects/models.py index d97b01c8ff..9cfea701a4 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -178,7 +178,8 @@ class ObjectDB(TypedObject): blank=True, null=True, verbose_name='destination', help_text='a destination, used only by exit objects.') # 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, blank=True, + help_text="optional python path to a cmdset class.") # Database manager objects = ObjectManager() diff --git a/src/players/admin.py b/src/players/admin.py index 6460292fdd..8b1b86d8d9 100644 --- a/src/players/admin.py +++ b/src/players/admin.py @@ -3,43 +3,99 @@ # in the web admin interface. # +from django import forms +from django.db import models +from django.conf import settings from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin -from django.contrib.auth.models import User, Group +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 # remove User itself from admin site admin.site.unregister(User) -#admin.site.unregister(Group) -class PlayerInline(admin.TabularInline): - model = PlayerDB +# 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'}), + help_text = "This should be the same as the connected Player's key name. 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.", + error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."}) +class CustomUserCreationForm(UserCreationForm): + username = 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 connected Player's key name. 30 characters or fewer. Letters, spaces, digits and @/./+/-/_ only.", + error_messages = {'invalid': "This value may contain only letters, spaces, numbers and @/./+/-/_ characters."}) + class UserAdmin(BaseUserAdmin): + "This will pop up from the Player admin." + + 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':'Note that whereas Player name supports spaces, This User field does not!'},), - ) - + '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) -# class PlayerAttributeAdmin(admin.ModelAdmin): -# fields = ('db_key', 'db_value') -# admin.site.register(PlayerAttribute, PlayerAttributeAdmin) + +# 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 - #fields = ('db_key', 'db_value') + extra = 1 + form = PlayerAttributeForm fieldsets = ( - ("Attributes", - {'fields' : (('db_key', 'db_value')), - 'classes' : ('wide',)}), ) + (None, {'fields' : (('db_key', 'db_value'))}),) - max_num = 1 +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'}), + 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_lock_storage = forms.CharField(label="Locks", widget=forms.Textarea(attrs={'cols':'100', 'rows':'1'}), + 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.") +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): - inlines = [PlayerAttributeInline] + "Setting up and tying the player administration together" list_display = ('id', 'db_key', 'user', 'db_permissions', 'db_typeclass_path') list_display_links = ('id', 'db_key') @@ -48,10 +104,64 @@ class PlayerDBAdmin(admin.ModelAdmin): save_as = True save_on_top = True list_select_related = True + + # editing/adding player + form = PlayerEditForm fieldsets = ( - (None, - {'fields' : (('db_key', 'db_typeclass_path'), 'user', ('db_permissions','db_lock_storage'), 'db_obj'), - 'description': 'To create a new Player, a User object must also be created and/or assigned.', + (None, + {'fields' : (('db_key', 'db_typeclass_path'), 'user', ('db_permissions','db_lock_storage'), 'db_cmdset_storage', 'db_obj'), + 'description': 'To create a new Player, a User object must also be created and/or assigned. When deleting a Player, its connected User will also be deleted. A Character object is optional, but required for IC interactions in the game.', '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 must also be created and/or assigned. When deleting a Player, its connected User will also be deleted. A Character object is optional, but required for IC interactions in the game.', + '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: + # 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) diff --git a/src/players/models.py b/src/players/models.py index d6c7153892..7bcf1966e2 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -151,10 +151,11 @@ class PlayerDB(TypedObject): help_text="The User object holds django-related authentication. Each Player must have a unique User account linked to them at all times.") # 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 (optional).') + db_obj = models.ForeignKey("objects.ObjectDB", null=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) + db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True, + help_text="optional python path to a cmdset class.") # Database manager objects = manager.PlayerManager() diff --git a/src/scripts/admin.py b/src/scripts/admin.py index 6aa84d9e30..b6ba5610b6 100644 --- a/src/scripts/admin.py +++ b/src/scripts/admin.py @@ -12,7 +12,7 @@ class ScriptAttributeInline(admin.TabularInline): max_num = 1 class ScriptDBAdmin(admin.ModelAdmin): - inlines = [ScriptAttributeInline] + list_display = ('id', 'db_key', 'db_typeclass_path', 'db_obj', 'db_interval', 'db_repeats', 'db_persistent') list_display_links = ('id', 'db_key') ordering = ['db_obj', 'db_typeclass_path'] @@ -25,5 +25,7 @@ class ScriptDBAdmin(admin.ModelAdmin): (None, { 'fields':(('db_key', 'db_typeclass_path'), 'db_interval', 'db_repeats', 'db_start_delay', 'db_persistent', 'db_obj')}), ) + #inlines = [ScriptAttributeInline] + admin.site.register(ScriptDB, ScriptDBAdmin) diff --git a/src/settings_default.py b/src/settings_default.py index 52ee2933ff..5779d756d6 100644 --- a/src/settings_default.py +++ b/src/settings_default.py @@ -243,6 +243,7 @@ PERMISSION_PLAYER_DEFAULT = "Players" # inside these modules will be available as lock functions. LOCK_FUNC_MODULES = ("src.locks.lockfuncs",) + ################################################### # In-game Channels created from server start ################################################### diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index 9ba1fe64da..c7fdffff6b 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -549,9 +549,9 @@ class TypedObject(SharedMemoryModel): # Creation date db_date_created = models.DateTimeField('creation date', editable=False, auto_now_add=True) # Permissions (access these through the 'permissions' property) - db_permissions = models.CharField('permissions', max_length=255, blank=True, 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. Normal players use 'Players' by default.") + db_permissions = models.CharField('permissions', max_length=255, blank=True, 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. Character objects use 'Players' by default. Most other objects don't have any permissions.") # Lock storage - db_lock_storage = models.CharField('locks', max_length=512, blank=True, 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 Players to set this to a valid string.") + db_lock_storage = models.CharField('locks', max_length=512, blank=True, 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. Not defining a lock means no access is granted.") # Database manager objects = managers.TypedObjectManager() diff --git a/src/web/templates/admin/index.html b/src/web/templates/admin/index.html index f94f6c14cf..e5a757fdf4 100644 --- a/src/web/templates/admin/index.html +++ b/src/web/templates/admin/index.html @@ -18,6 +18,14 @@ {% if app.name in evennia_userapps %} + {% 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.

+ {% endif %} +
@@ -49,12 +57,17 @@ {% endif %} {% endfor %} -

In-game

+

Game entities

{% for app in app_list %} {% if app.name in evennia_entityapps %} + {% if app.name == 'Comms' %} +

This defines entities that has an in-game precense or + effect of some kind.

+ {% endif %} +
{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
@@ -86,80 +99,6 @@ {% endif %} {% endfor %} -

configs

- - {% for app in app_list %} - - {% if app.name in evennia_setupapps %} - -
-
{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
- - {% for model in app.models %} - - {% if model.perms.change %} - - {% else %} - - {% endif %} - - {% if model.perms.add %} - - {% else %} - - {% endif %} - - {% if model.perms.change %} - - {% else %} - - {% endif %} - - - {% endfor %} -
{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
{{ model.name }}{{ model.name }}{% trans 'Add' %} {% trans 'Change' %} 
-
- - {% endif %} - {% endfor %} - -

Connections

- - {% for app in app_list %} - - {% if app.name in evennia_connectapps %} - -
- - - {% for model in app.models %} - - {% if model.perms.change %} - - {% else %} - - {% endif %} - - {% if model.perms.add %} - - {% else %} - - {% endif %} - - {% if model.perms.change %} - - {% else %} - - {% endif %} - - - {% endfor %} -
{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
{{ model.name }}{{ model.name }}{% trans 'Add' %} {% trans 'Change' %} 
-
- - {% endif %} - {% endfor %} -

Website

@@ -168,6 +107,11 @@ {% if app.name in evennia_websiteapps %} + {% if app.name == 'Flatpages' %} +

Miscellaneous objects related to the running and + managing of the Web presence.

+ {% endif %} +
diff --git a/src/web/utils/general_context.py b/src/web/utils/general_context.py index 39d6fed790..f63dc40588 100644 --- a/src/web/utils/general_context.py +++ b/src/web/utils/general_context.py @@ -22,7 +22,7 @@ SERVER_VERSION = get_evennia_version() # Setup lists of the most relevant apps so # the adminsite becomes more readable. -USER_RELATED = ['Players'] +USER_RELATED = ['Players', 'Auth'] GAME_ENTITIES = ['Objects', 'Scripts', 'Comms', 'Help'] GAME_SETUP = ['Permissions', 'Config'] CONNECTIONS = ['Irc', 'Imc2']
{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}