Format code with black. Add makefile to run fmt/tests
This commit is contained in:
parent
d00bce9288
commit
c2c7fa311a
299 changed files with 19037 additions and 11611 deletions
|
|
@ -14,11 +14,9 @@ from django.views.generic import RedirectView
|
|||
urlpatterns = [
|
||||
# Front page (note that we shouldn't specify namespace here since we will
|
||||
# not be able to load django-auth/admin stuff (will probably work in Django>1.9)
|
||||
url(r'^', include('evennia.web.website.urls')), # , namespace='website', app_name='website')),
|
||||
|
||||
url(r"^", include("evennia.web.website.urls")), # , namespace='website', app_name='website')),
|
||||
# webclient
|
||||
url(r'^webclient/', include('evennia.web.webclient.urls', namespace='webclient')),
|
||||
|
||||
url(r"^webclient/", include("evennia.web.webclient.urls", namespace="webclient")),
|
||||
# favicon
|
||||
url(r'^favicon\.ico$', RedirectView.as_view(url='/media/images/favicon.ico', permanent=False))
|
||||
url(r"^favicon\.ico$", RedirectView.as_view(url="/media/images/favicon.ico", permanent=False)),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -29,17 +29,18 @@ def set_game_name_and_slogan():
|
|||
GAME_SLOGAN = settings.GAME_SLOGAN.strip()
|
||||
except AttributeError:
|
||||
GAME_SLOGAN = SERVER_VERSION
|
||||
|
||||
|
||||
set_game_name_and_slogan()
|
||||
|
||||
# Setup lists of the most relevant apps so
|
||||
# the adminsite becomes more readable.
|
||||
|
||||
ACCOUNT_RELATED = ['Accounts']
|
||||
GAME_ENTITIES = ['Objects', 'Scripts', 'Comms', 'Help']
|
||||
GAME_SETUP = ['Permissions', 'Config']
|
||||
CONNECTIONS = ['Irc']
|
||||
WEBSITE = ['Flatpages', 'News', 'Sites']
|
||||
|
||||
ACCOUNT_RELATED = ["Accounts"]
|
||||
GAME_ENTITIES = ["Objects", "Scripts", "Comms", "Help"]
|
||||
GAME_SETUP = ["Permissions", "Config"]
|
||||
CONNECTIONS = ["Irc"]
|
||||
WEBSITE = ["Flatpages", "News", "Sites"]
|
||||
|
||||
|
||||
def set_webclient_settings():
|
||||
|
|
@ -56,9 +57,13 @@ def set_webclient_settings():
|
|||
# if we are working through a proxy or uses docker port-remapping, the webclient port encoded
|
||||
# in the webclient should be different than the one the server expects. Use the environment
|
||||
# variable WEBSOCKET_CLIENT_PROXY_PORT if this is the case.
|
||||
WEBSOCKET_PORT = int(os.environ.get("WEBSOCKET_CLIENT_PROXY_PORT", settings.WEBSOCKET_CLIENT_PORT))
|
||||
WEBSOCKET_PORT = int(
|
||||
os.environ.get("WEBSOCKET_CLIENT_PROXY_PORT", settings.WEBSOCKET_CLIENT_PORT)
|
||||
)
|
||||
# this is determined dynamically by the client and is less of an issue
|
||||
WEBSOCKET_URL = settings.WEBSOCKET_CLIENT_URL
|
||||
|
||||
|
||||
set_webclient_settings()
|
||||
|
||||
# The main context processor function
|
||||
|
|
@ -72,22 +77,22 @@ def general_context(request):
|
|||
account = request.user
|
||||
|
||||
puppet = None
|
||||
if account and request.session.get('puppet'):
|
||||
pk = int(request.session.get('puppet'))
|
||||
if account and request.session.get("puppet"):
|
||||
pk = int(request.session.get("puppet"))
|
||||
puppet = next((x for x in account.characters if x.pk == pk), None)
|
||||
|
||||
return {
|
||||
'account': account,
|
||||
'puppet': puppet,
|
||||
'game_name': GAME_NAME,
|
||||
'game_slogan': GAME_SLOGAN,
|
||||
'evennia_userapps': ACCOUNT_RELATED,
|
||||
'evennia_entityapps': GAME_ENTITIES,
|
||||
'evennia_setupapps': GAME_SETUP,
|
||||
'evennia_connectapps': CONNECTIONS,
|
||||
'evennia_websiteapps': WEBSITE,
|
||||
"account": account,
|
||||
"puppet": puppet,
|
||||
"game_name": GAME_NAME,
|
||||
"game_slogan": GAME_SLOGAN,
|
||||
"evennia_userapps": ACCOUNT_RELATED,
|
||||
"evennia_entityapps": GAME_ENTITIES,
|
||||
"evennia_setupapps": GAME_SETUP,
|
||||
"evennia_connectapps": CONNECTIONS,
|
||||
"evennia_websiteapps": WEBSITE,
|
||||
"webclient_enabled": WEBCLIENT_ENABLED,
|
||||
"websocket_enabled": WEBSOCKET_CLIENT_ENABLED,
|
||||
"websocket_port": WEBSOCKET_PORT,
|
||||
"websocket_url": WEBSOCKET_URL
|
||||
"websocket_url": WEBSOCKET_URL,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ class SharedLoginMiddleware(object):
|
|||
Handle the shared login between website and webclient.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
# One-time configuration and initialization.
|
||||
self.get_response = get_response
|
||||
|
|
|
|||
|
|
@ -3,44 +3,48 @@ from django.test import RequestFactory, TestCase
|
|||
from mock import MagicMock, patch
|
||||
from . import general_context
|
||||
|
||||
|
||||
class TestGeneralContext(TestCase):
|
||||
maxDiff = None
|
||||
|
||||
@patch('evennia.web.utils.general_context.GAME_NAME', "test_name")
|
||||
@patch('evennia.web.utils.general_context.GAME_SLOGAN', "test_game_slogan")
|
||||
@patch('evennia.web.utils.general_context.WEBSOCKET_CLIENT_ENABLED', "websocket_client_enabled_testvalue")
|
||||
@patch('evennia.web.utils.general_context.WEBCLIENT_ENABLED', "webclient_enabled_testvalue")
|
||||
@patch('evennia.web.utils.general_context.WEBSOCKET_PORT', "websocket_client_port_testvalue")
|
||||
@patch('evennia.web.utils.general_context.WEBSOCKET_URL', "websocket_client_url_testvalue")
|
||||
@patch("evennia.web.utils.general_context.GAME_NAME", "test_name")
|
||||
@patch("evennia.web.utils.general_context.GAME_SLOGAN", "test_game_slogan")
|
||||
@patch(
|
||||
"evennia.web.utils.general_context.WEBSOCKET_CLIENT_ENABLED",
|
||||
"websocket_client_enabled_testvalue",
|
||||
)
|
||||
@patch("evennia.web.utils.general_context.WEBCLIENT_ENABLED", "webclient_enabled_testvalue")
|
||||
@patch("evennia.web.utils.general_context.WEBSOCKET_PORT", "websocket_client_port_testvalue")
|
||||
@patch("evennia.web.utils.general_context.WEBSOCKET_URL", "websocket_client_url_testvalue")
|
||||
def test_general_context(self):
|
||||
request = RequestFactory().get('/')
|
||||
request = RequestFactory().get("/")
|
||||
request.user = AnonymousUser()
|
||||
request.session = {
|
||||
'account': None,
|
||||
'puppet': None,
|
||||
}
|
||||
|
||||
request.session = {"account": None, "puppet": None}
|
||||
|
||||
response = general_context.general_context(request)
|
||||
|
||||
self.assertEqual(response, {
|
||||
'account': None,
|
||||
'puppet': None,
|
||||
'game_name': "test_name",
|
||||
'game_slogan': "test_game_slogan",
|
||||
'evennia_userapps': ['Accounts'],
|
||||
'evennia_entityapps': ['Objects', 'Scripts', 'Comms', 'Help'],
|
||||
'evennia_setupapps': ['Permissions', 'Config'],
|
||||
'evennia_connectapps': ['Irc'],
|
||||
'evennia_websiteapps': ['Flatpages', 'News', 'Sites'],
|
||||
"webclient_enabled": "webclient_enabled_testvalue",
|
||||
"websocket_enabled": "websocket_client_enabled_testvalue",
|
||||
"websocket_port": "websocket_client_port_testvalue",
|
||||
"websocket_url": "websocket_client_url_testvalue"
|
||||
})
|
||||
|
||||
self.assertEqual(
|
||||
response,
|
||||
{
|
||||
"account": None,
|
||||
"puppet": None,
|
||||
"game_name": "test_name",
|
||||
"game_slogan": "test_game_slogan",
|
||||
"evennia_userapps": ["Accounts"],
|
||||
"evennia_entityapps": ["Objects", "Scripts", "Comms", "Help"],
|
||||
"evennia_setupapps": ["Permissions", "Config"],
|
||||
"evennia_connectapps": ["Irc"],
|
||||
"evennia_websiteapps": ["Flatpages", "News", "Sites"],
|
||||
"webclient_enabled": "webclient_enabled_testvalue",
|
||||
"websocket_enabled": "websocket_client_enabled_testvalue",
|
||||
"websocket_port": "websocket_client_port_testvalue",
|
||||
"websocket_url": "websocket_client_url_testvalue",
|
||||
},
|
||||
)
|
||||
|
||||
# spec being an empty list will initially raise AttributeError in set_game_name_and_slogan to test defaults
|
||||
@patch('evennia.web.utils.general_context.settings', spec=[])
|
||||
@patch('evennia.web.utils.general_context.get_evennia_version')
|
||||
@patch("evennia.web.utils.general_context.settings", spec=[])
|
||||
@patch("evennia.web.utils.general_context.get_evennia_version")
|
||||
def test_set_game_name_and_slogan(self, mock_get_version, mock_settings):
|
||||
mock_get_version.return_value = "version 1"
|
||||
# test default/fallback values
|
||||
|
|
@ -54,7 +58,7 @@ class TestGeneralContext(TestCase):
|
|||
self.assertEqual(general_context.GAME_NAME, "test_name")
|
||||
self.assertEqual(general_context.GAME_SLOGAN, "test_game_slogan")
|
||||
|
||||
@patch('evennia.web.utils.general_context.settings')
|
||||
@patch("evennia.web.utils.general_context.settings")
|
||||
def test_set_webclient_settings(self, mock_settings):
|
||||
mock_settings.WEBCLIENT_ENABLED = "webclient"
|
||||
mock_settings.WEBSOCKET_CLIENT_URL = "websocket_url"
|
||||
|
|
|
|||
|
|
@ -6,5 +6,4 @@ from django.conf.urls import *
|
|||
from evennia.web.webclient import views as webclient_views
|
||||
|
||||
app_name = "webclient"
|
||||
urlpatterns = [
|
||||
url(r'^$', webclient_views.webclient, name="index")]
|
||||
urlpatterns = [url(r"^$", webclient_views.webclient, name="index")]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
"""
|
||||
This contains a simple view for rendering the webclient
|
||||
page and serve it eventual static content.
|
||||
|
|
@ -26,6 +25,6 @@ def webclient(request):
|
|||
raise Http404
|
||||
|
||||
# make sure to store the browser session's hash so the webclient can get to it!
|
||||
pagevars = {'browser_sessid': request.session.session_key}
|
||||
pagevars = {"browser_sessid": request.session.session_key}
|
||||
|
||||
return render(request, 'webclient.html', pagevars)
|
||||
return render(request, "webclient.html", pagevars)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from django.forms import ModelForm
|
|||
from django.utils.html import escape
|
||||
from evennia.utils import class_from_module
|
||||
|
||||
|
||||
class EvenniaForm(forms.Form):
|
||||
"""
|
||||
This is a stock Django form, but modified so that all values provided
|
||||
|
|
@ -17,6 +18,7 @@ class EvenniaForm(forms.Form):
|
|||
https://www.owasp.org/index.php/Input_Validation_Cheat_Sheet#Goals_of_Input_Validation
|
||||
|
||||
"""
|
||||
|
||||
def clean(self):
|
||||
"""
|
||||
Django hook. Performed on form submission.
|
||||
|
|
@ -29,9 +31,10 @@ class EvenniaForm(forms.Form):
|
|||
cleaned = super(EvenniaForm, self).clean()
|
||||
|
||||
# Escape all values provided by user
|
||||
cleaned = {k:escape(v) for k,v in cleaned.items()}
|
||||
cleaned = {k: escape(v) for k, v in cleaned.items()}
|
||||
return cleaned
|
||||
|
||||
|
||||
class AccountForm(UserCreationForm):
|
||||
"""
|
||||
This is a generic Django form tailored to the Account model.
|
||||
|
|
@ -40,12 +43,14 @@ class AccountForm(UserCreationForm):
|
|||
core User model fields (username, email, password).
|
||||
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
This is a Django construct that provides additional configuration to
|
||||
the form.
|
||||
|
||||
"""
|
||||
|
||||
# The model/typeclass this form creates
|
||||
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||
|
||||
|
|
@ -53,11 +58,14 @@ class AccountForm(UserCreationForm):
|
|||
fields = ("username", "email")
|
||||
|
||||
# Any overrides of field classes
|
||||
field_classes = {'username': UsernameField}
|
||||
field_classes = {"username": UsernameField}
|
||||
|
||||
# Username is collected as part of the core UserCreationForm, so we just need
|
||||
# to add a field to (optionally) capture email.
|
||||
email = forms.EmailField(help_text="A valid email address. Optional; used for password resets.", required=False)
|
||||
email = forms.EmailField(
|
||||
help_text="A valid email address. Optional; used for password resets.", required=False
|
||||
)
|
||||
|
||||
|
||||
class ObjectForm(EvenniaForm, ModelForm):
|
||||
"""
|
||||
|
|
@ -70,12 +78,14 @@ class ObjectForm(EvenniaForm, ModelForm):
|
|||
a simple example of how to do this.
|
||||
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
This is a Django construct that provides additional configuration to
|
||||
the form.
|
||||
|
||||
"""
|
||||
|
||||
# The model/typeclass this form creates
|
||||
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
|
||||
|
||||
|
|
@ -83,9 +93,8 @@ class ObjectForm(EvenniaForm, ModelForm):
|
|||
fields = ("db_key",)
|
||||
|
||||
# This lets us rename ugly db-specific keys to something more human
|
||||
labels = {
|
||||
'db_key': 'Name',
|
||||
}
|
||||
labels = {"db_key": "Name"}
|
||||
|
||||
|
||||
class CharacterForm(ObjectForm):
|
||||
"""
|
||||
|
|
@ -122,12 +131,14 @@ class CharacterForm(ObjectForm):
|
|||
https://docs.djangoproject.com/en/1.11/ref/forms/widgets/
|
||||
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""
|
||||
This is a Django construct that provides additional configuration to
|
||||
the form.
|
||||
|
||||
"""
|
||||
|
||||
# Get the correct object model
|
||||
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
|
||||
|
||||
|
|
@ -135,15 +146,16 @@ class CharacterForm(ObjectForm):
|
|||
fields = ("db_key",)
|
||||
|
||||
# Rename 'key' to something more intelligible
|
||||
labels = {
|
||||
'db_key': 'Name',
|
||||
}
|
||||
labels = {"db_key": "Name"}
|
||||
|
||||
# Fields pertaining to configurable attributes on the Character object.
|
||||
desc = forms.CharField(
|
||||
label='Description', max_length=2048, required=False,
|
||||
widget=forms.Textarea(attrs={'rows': 3}),
|
||||
help_text="A brief description of your character.")
|
||||
label="Description",
|
||||
max_length=2048,
|
||||
required=False,
|
||||
widget=forms.Textarea(attrs={"rows": 3}),
|
||||
help_text="A brief description of your character.",
|
||||
)
|
||||
|
||||
|
||||
class CharacterUpdateForm(CharacterForm):
|
||||
|
|
@ -156,4 +168,5 @@ class CharacterUpdateForm(CharacterForm):
|
|||
wish to allow.
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ from django import template
|
|||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(name='addclass')
|
||||
@register.filter(name="addclass")
|
||||
def addclass(field, given_class):
|
||||
existing_classes = field.field.widget.attrs.get('class', None)
|
||||
existing_classes = field.field.widget.attrs.get("class", None)
|
||||
if existing_classes:
|
||||
if existing_classes.find(given_class) == -1:
|
||||
# if the given class doesn't exist in the existing classes
|
||||
classes = existing_classes + ' ' + given_class
|
||||
classes = existing_classes + " " + given_class
|
||||
else:
|
||||
classes = existing_classes
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from django.urls import reverse
|
|||
from evennia.utils import class_from_module
|
||||
from evennia.utils.test_resources import EvenniaTest
|
||||
|
||||
|
||||
class EvenniaWebTest(EvenniaTest):
|
||||
|
||||
# Use the same classes the views are expecting
|
||||
|
|
@ -17,7 +18,7 @@ class EvenniaWebTest(EvenniaTest):
|
|||
channel_typeclass = settings.BASE_CHANNEL_TYPECLASS
|
||||
|
||||
# Default named url
|
||||
url_name = 'index'
|
||||
url_name = "index"
|
||||
|
||||
# Response to expect for unauthenticated requests
|
||||
unauthenticated_response = 200
|
||||
|
|
@ -34,14 +35,14 @@ class EvenniaWebTest(EvenniaTest):
|
|||
|
||||
for account in (self.account, self.account2):
|
||||
# Demote accounts to Player permissions
|
||||
account.permissions.add('Player')
|
||||
account.permissions.remove('Developer')
|
||||
account.permissions.add("Player")
|
||||
account.permissions.remove("Developer")
|
||||
|
||||
# Grant permissions to chars
|
||||
for char in account.db._playable_characters:
|
||||
char.locks.add('edit:id(%s) or perm(Admin)' % account.pk)
|
||||
char.locks.add('delete:id(%s) or perm(Admin)' % account.pk)
|
||||
char.locks.add('view:all()')
|
||||
char.locks.add("edit:id(%s) or perm(Admin)" % account.pk)
|
||||
char.locks.add("delete:id(%s) or perm(Admin)" % account.pk)
|
||||
char.locks.add("view:all()")
|
||||
|
||||
def test_valid_chars(self):
|
||||
"Make sure account has playable characters"
|
||||
|
|
@ -57,11 +58,11 @@ class EvenniaWebTest(EvenniaTest):
|
|||
self.assertEqual(response.status_code, self.unauthenticated_response)
|
||||
|
||||
def login(self):
|
||||
return self.client.login(username='TestAccount', password='testpassword')
|
||||
return self.client.login(username="TestAccount", password="testpassword")
|
||||
|
||||
def test_get_authenticated(self):
|
||||
logged_in = self.login()
|
||||
self.assertTrue(logged_in, 'Account failed to log in!')
|
||||
self.assertTrue(logged_in, "Account failed to log in!")
|
||||
|
||||
# Try accessing page while logged in
|
||||
response = self.client.get(reverse(self.url_name, kwargs=self.get_kwargs()), follow=True)
|
||||
|
|
@ -71,28 +72,35 @@ class EvenniaWebTest(EvenniaTest):
|
|||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class AdminTest(EvenniaWebTest):
|
||||
url_name = 'django_admin'
|
||||
url_name = "django_admin"
|
||||
unauthenticated_response = 302
|
||||
|
||||
|
||||
class IndexTest(EvenniaWebTest):
|
||||
url_name = 'index'
|
||||
url_name = "index"
|
||||
|
||||
|
||||
class RegisterTest(EvenniaWebTest):
|
||||
url_name = 'register'
|
||||
url_name = "register"
|
||||
|
||||
|
||||
class LoginTest(EvenniaWebTest):
|
||||
url_name = 'login'
|
||||
url_name = "login"
|
||||
|
||||
|
||||
class LogoutTest(EvenniaWebTest):
|
||||
url_name = 'logout'
|
||||
url_name = "logout"
|
||||
|
||||
|
||||
class PasswordResetTest(EvenniaWebTest):
|
||||
url_name = 'password_change'
|
||||
url_name = "password_change"
|
||||
unauthenticated_response = 302
|
||||
|
||||
|
||||
class WebclientTest(EvenniaWebTest):
|
||||
url_name = 'webclient:index'
|
||||
url_name = "webclient:index"
|
||||
|
||||
@override_settings(WEBCLIENT_ENABLED=True)
|
||||
def test_get(self):
|
||||
|
|
@ -106,11 +114,13 @@ class WebclientTest(EvenniaWebTest):
|
|||
self.unauthenticated_response = 404
|
||||
super(WebclientTest, self).test_get()
|
||||
|
||||
|
||||
class ChannelListTest(EvenniaWebTest):
|
||||
url_name = 'channels'
|
||||
url_name = "channels"
|
||||
|
||||
|
||||
class ChannelDetailTest(EvenniaWebTest):
|
||||
url_name = 'channel-detail'
|
||||
url_name = "channel-detail"
|
||||
|
||||
def setUp(self):
|
||||
super(ChannelDetailTest, self).setUp()
|
||||
|
|
@ -118,15 +128,14 @@ class ChannelDetailTest(EvenniaWebTest):
|
|||
klass = class_from_module(self.channel_typeclass)
|
||||
|
||||
# Create a channel
|
||||
klass.create('demo')
|
||||
klass.create("demo")
|
||||
|
||||
def get_kwargs(self):
|
||||
return {
|
||||
'slug': slugify('demo')
|
||||
}
|
||||
return {"slug": slugify("demo")}
|
||||
|
||||
|
||||
class CharacterCreateView(EvenniaWebTest):
|
||||
url_name = 'character-create'
|
||||
url_name = "character-create"
|
||||
unauthenticated_response = 302
|
||||
|
||||
@override_settings(MULTISESSION_MODE=0)
|
||||
|
|
@ -138,16 +147,17 @@ class CharacterCreateView(EvenniaWebTest):
|
|||
self.login()
|
||||
|
||||
# Post data for a new character
|
||||
data = {
|
||||
'db_key': 'gannon',
|
||||
'desc': 'Some dude.'
|
||||
}
|
||||
data = {"db_key": "gannon", "desc": "Some dude."}
|
||||
|
||||
response = self.client.post(reverse(self.url_name), data=data, follow=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Make sure the character was actually created
|
||||
self.assertTrue(len(self.account.db._playable_characters) == 1, 'Account only has the following characters attributed to it: %s' % self.account.db._playable_characters)
|
||||
self.assertTrue(
|
||||
len(self.account.db._playable_characters) == 1,
|
||||
"Account only has the following characters attributed to it: %s"
|
||||
% self.account.db._playable_characters,
|
||||
)
|
||||
|
||||
@override_settings(MULTISESSION_MODE=2)
|
||||
@override_settings(MAX_NR_CHARACTERS=10)
|
||||
|
|
@ -157,26 +167,25 @@ class CharacterCreateView(EvenniaWebTest):
|
|||
self.login()
|
||||
|
||||
# Post data for a new character
|
||||
data = {
|
||||
'db_key': 'gannon',
|
||||
'desc': 'Some dude.'
|
||||
}
|
||||
data = {"db_key": "gannon", "desc": "Some dude."}
|
||||
|
||||
response = self.client.post(reverse(self.url_name), data=data, follow=True)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Make sure the character was actually created
|
||||
self.assertTrue(len(self.account.db._playable_characters) > 1, 'Account only has the following characters attributed to it: %s' % self.account.db._playable_characters)
|
||||
self.assertTrue(
|
||||
len(self.account.db._playable_characters) > 1,
|
||||
"Account only has the following characters attributed to it: %s"
|
||||
% self.account.db._playable_characters,
|
||||
)
|
||||
|
||||
|
||||
class CharacterPuppetView(EvenniaWebTest):
|
||||
url_name = 'character-puppet'
|
||||
url_name = "character-puppet"
|
||||
unauthenticated_response = 302
|
||||
|
||||
def get_kwargs(self):
|
||||
return {
|
||||
'pk': self.char1.pk,
|
||||
'slug': slugify(self.char1.name)
|
||||
}
|
||||
return {"pk": self.char1.pk, "slug": slugify(self.char1.name)}
|
||||
|
||||
def test_invalid_access(self):
|
||||
"Account1 should not be able to puppet Account2:Char2"
|
||||
|
|
@ -184,30 +193,31 @@ class CharacterPuppetView(EvenniaWebTest):
|
|||
self.login()
|
||||
|
||||
# Try to access puppet page for char2
|
||||
kwargs = {
|
||||
'pk': self.char2.pk,
|
||||
'slug': slugify(self.char2.name)
|
||||
}
|
||||
kwargs = {"pk": self.char2.pk, "slug": slugify(self.char2.name)}
|
||||
response = self.client.get(reverse(self.url_name, kwargs=kwargs), follow=True)
|
||||
self.assertTrue(response.status_code >= 400, "Invalid access should return a 4xx code-- either obj not found or permission denied! (Returned %s)" % response.status_code)
|
||||
self.assertTrue(
|
||||
response.status_code >= 400,
|
||||
"Invalid access should return a 4xx code-- either obj not found or permission denied! (Returned %s)"
|
||||
% response.status_code,
|
||||
)
|
||||
|
||||
|
||||
class CharacterListView(EvenniaWebTest):
|
||||
url_name = 'characters'
|
||||
url_name = "characters"
|
||||
unauthenticated_response = 302
|
||||
|
||||
|
||||
class CharacterManageView(EvenniaWebTest):
|
||||
url_name = 'character-manage'
|
||||
url_name = "character-manage"
|
||||
unauthenticated_response = 302
|
||||
|
||||
|
||||
class CharacterUpdateView(EvenniaWebTest):
|
||||
url_name = 'character-update'
|
||||
url_name = "character-update"
|
||||
unauthenticated_response = 302
|
||||
|
||||
def get_kwargs(self):
|
||||
return {
|
||||
'pk': self.char1.pk,
|
||||
'slug': slugify(self.char1.name)
|
||||
}
|
||||
return {"pk": self.char1.pk, "slug": slugify(self.char1.name)}
|
||||
|
||||
def test_valid_access(self):
|
||||
"Account1 should be able to update Account1:Char1"
|
||||
|
|
@ -219,12 +229,14 @@ class CharacterUpdateView(EvenniaWebTest):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Try to update char1 desc
|
||||
data = {'db_key': self.char1.db_key, 'desc': "Just a regular type of dude."}
|
||||
response = self.client.post(reverse(self.url_name, kwargs=self.get_kwargs()), data=data, follow=True)
|
||||
data = {"db_key": self.char1.db_key, "desc": "Just a regular type of dude."}
|
||||
response = self.client.post(
|
||||
reverse(self.url_name, kwargs=self.get_kwargs()), data=data, follow=True
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Make sure the change was made successfully
|
||||
self.assertEqual(self.char1.db.desc, data['desc'])
|
||||
self.assertEqual(self.char1.db.desc, data["desc"])
|
||||
|
||||
def test_invalid_access(self):
|
||||
"Account1 should not be able to update Account2:Char2"
|
||||
|
|
@ -232,22 +244,17 @@ class CharacterUpdateView(EvenniaWebTest):
|
|||
self.login()
|
||||
|
||||
# Try to access update page for char2
|
||||
kwargs = {
|
||||
'pk': self.char2.pk,
|
||||
'slug': slugify(self.char2.name)
|
||||
}
|
||||
kwargs = {"pk": self.char2.pk, "slug": slugify(self.char2.name)}
|
||||
response = self.client.get(reverse(self.url_name, kwargs=kwargs), follow=True)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
|
||||
class CharacterDeleteView(EvenniaWebTest):
|
||||
url_name = 'character-delete'
|
||||
url_name = "character-delete"
|
||||
unauthenticated_response = 302
|
||||
|
||||
def get_kwargs(self):
|
||||
return {
|
||||
'pk': self.char1.pk,
|
||||
'slug': slugify(self.char1.name)
|
||||
}
|
||||
return {"pk": self.char1.pk, "slug": slugify(self.char1.name)}
|
||||
|
||||
def test_valid_access(self):
|
||||
"Account1 should be able to delete Account1:Char1"
|
||||
|
|
@ -259,13 +266,17 @@ class CharacterDeleteView(EvenniaWebTest):
|
|||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Proceed with deleting it
|
||||
data = {'value': 'yes'}
|
||||
response = self.client.post(reverse(self.url_name, kwargs=self.get_kwargs()), data=data, follow=True)
|
||||
data = {"value": "yes"}
|
||||
response = self.client.post(
|
||||
reverse(self.url_name, kwargs=self.get_kwargs()), data=data, follow=True
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Make sure it deleted
|
||||
self.assertFalse(self.char1 in self.account.db._playable_characters,
|
||||
'Char1 is still in Account playable characters list.')
|
||||
self.assertFalse(
|
||||
self.char1 in self.account.db._playable_characters,
|
||||
"Char1 is still in Account playable characters list.",
|
||||
)
|
||||
|
||||
def test_invalid_access(self):
|
||||
"Account1 should not be able to delete Account2:Char2"
|
||||
|
|
@ -273,9 +284,6 @@ class CharacterDeleteView(EvenniaWebTest):
|
|||
self.login()
|
||||
|
||||
# Try to access delete page for char2
|
||||
kwargs = {
|
||||
'pk': self.char2.pk,
|
||||
'slug': slugify(self.char2.name)
|
||||
}
|
||||
kwargs = {"pk": self.char2.pk, "slug": slugify(self.char2.name)}
|
||||
response = self.client.get(reverse(self.url_name, kwargs=kwargs), follow=True)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
|
|
|||
|
|
@ -9,53 +9,89 @@ from django import views as django_views
|
|||
from evennia.web.website import views as website_views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', website_views.EvenniaIndexView.as_view(), name="index"),
|
||||
url(r'^tbi/', website_views.to_be_implemented, name='to_be_implemented'),
|
||||
|
||||
url(r"^$", website_views.EvenniaIndexView.as_view(), name="index"),
|
||||
url(r"^tbi/", website_views.to_be_implemented, name="to_be_implemented"),
|
||||
# User Authentication (makes login/logout url names available)
|
||||
url(r'^auth/register', website_views.AccountCreateView.as_view(), name="register"),
|
||||
url(r'^auth/', include('django.contrib.auth.urls')),
|
||||
|
||||
url(r"^auth/register", website_views.AccountCreateView.as_view(), name="register"),
|
||||
url(r"^auth/", include("django.contrib.auth.urls")),
|
||||
# Help Topics
|
||||
url(r'^help/$', website_views.HelpListView.as_view(), name="help"),
|
||||
url(r'^help/(?P<category>[\w\d\-]+)/(?P<topic>[\w\d\-]+)/$', website_views.HelpDetailView.as_view(), name="help-entry-detail"),
|
||||
|
||||
url(r"^help/$", website_views.HelpListView.as_view(), name="help"),
|
||||
url(
|
||||
r"^help/(?P<category>[\w\d\-]+)/(?P<topic>[\w\d\-]+)/$",
|
||||
website_views.HelpDetailView.as_view(),
|
||||
name="help-entry-detail",
|
||||
),
|
||||
# Channels
|
||||
url(r'^channels/$', website_views.ChannelListView.as_view(), name="channels"),
|
||||
url(r'^channels/(?P<slug>[\w\d\-]+)/$', website_views.ChannelDetailView.as_view(), name="channel-detail"),
|
||||
|
||||
url(r"^channels/$", website_views.ChannelListView.as_view(), name="channels"),
|
||||
url(
|
||||
r"^channels/(?P<slug>[\w\d\-]+)/$",
|
||||
website_views.ChannelDetailView.as_view(),
|
||||
name="channel-detail",
|
||||
),
|
||||
# Character management
|
||||
url(r'^characters/$', website_views.CharacterListView.as_view(), name="characters"),
|
||||
url(r'^characters/create/$', website_views.CharacterCreateView.as_view(), name="character-create"),
|
||||
url(r'^characters/manage/$', website_views.CharacterManageView.as_view(), name="character-manage"),
|
||||
url(r'^characters/detail/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$', website_views.CharacterDetailView.as_view(), name="character-detail"),
|
||||
url(r'^characters/puppet/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$', website_views.CharacterPuppetView.as_view(), name="character-puppet"),
|
||||
url(r'^characters/update/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$', website_views.CharacterUpdateView.as_view(), name="character-update"),
|
||||
url(r'^characters/delete/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$', website_views.CharacterDeleteView.as_view(), name="character-delete"),
|
||||
|
||||
url(r"^characters/$", website_views.CharacterListView.as_view(), name="characters"),
|
||||
url(
|
||||
r"^characters/create/$",
|
||||
website_views.CharacterCreateView.as_view(),
|
||||
name="character-create",
|
||||
),
|
||||
url(
|
||||
r"^characters/manage/$",
|
||||
website_views.CharacterManageView.as_view(),
|
||||
name="character-manage",
|
||||
),
|
||||
url(
|
||||
r"^characters/detail/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$",
|
||||
website_views.CharacterDetailView.as_view(),
|
||||
name="character-detail",
|
||||
),
|
||||
url(
|
||||
r"^characters/puppet/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$",
|
||||
website_views.CharacterPuppetView.as_view(),
|
||||
name="character-puppet",
|
||||
),
|
||||
url(
|
||||
r"^characters/update/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$",
|
||||
website_views.CharacterUpdateView.as_view(),
|
||||
name="character-update",
|
||||
),
|
||||
url(
|
||||
r"^characters/delete/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$",
|
||||
website_views.CharacterDeleteView.as_view(),
|
||||
name="character-delete",
|
||||
),
|
||||
# Django original admin page. Make this URL is always available, whether
|
||||
# we've chosen to use Evennia's custom admin or not.
|
||||
url(r'django_admin/', website_views.admin_wrapper, name="django_admin"),
|
||||
|
||||
url(r"django_admin/", website_views.admin_wrapper, name="django_admin"),
|
||||
# Admin docs
|
||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls'))
|
||||
url(r"^admin/doc/", include("django.contrib.admindocs.urls")),
|
||||
]
|
||||
|
||||
if settings.EVENNIA_ADMIN:
|
||||
urlpatterns += [
|
||||
# Our override for the admin.
|
||||
url('^admin/$', website_views.evennia_admin, name="evennia_admin"),
|
||||
|
||||
url("^admin/$", website_views.evennia_admin, name="evennia_admin"),
|
||||
# Makes sure that other admin pages get loaded.
|
||||
url(r'^admin/', admin.site.urls)]
|
||||
url(r"^admin/", admin.site.urls),
|
||||
]
|
||||
else:
|
||||
# Just include the normal Django admin.
|
||||
urlpatterns += [url(r'^admin/', admin.site.urls)]
|
||||
urlpatterns += [url(r"^admin/", admin.site.urls)]
|
||||
|
||||
# This sets up the server if the user want to run the Django
|
||||
# test server (this should normally not be needed).
|
||||
if settings.SERVE_MEDIA:
|
||||
urlpatterns.extend([
|
||||
url(r'^media/(?P<path>.*)$', django_views.static.serve, {'document_root': settings.MEDIA_ROOT}),
|
||||
url(r'^static/(?P<path>.*)$', django_views.static.serve, {'document_root': settings.STATIC_ROOT})
|
||||
])
|
||||
urlpatterns.extend(
|
||||
[
|
||||
url(
|
||||
r"^media/(?P<path>.*)$",
|
||||
django_views.static.serve,
|
||||
{"document_root": settings.MEDIA_ROOT},
|
||||
),
|
||||
url(
|
||||
r"^static/(?P<path>.*)$",
|
||||
django_views.static.serve,
|
||||
{"document_root": settings.STATIC_ROOT},
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ def _gamestats():
|
|||
"num_exits": nexits or "no",
|
||||
"num_objects": nobjs or "none",
|
||||
"num_characters": nchars or "no",
|
||||
"num_others": nothers or "no"
|
||||
"num_others": nothers or "no",
|
||||
}
|
||||
return pagevars
|
||||
|
||||
|
|
@ -78,11 +78,9 @@ def to_be_implemented(request):
|
|||
implemented yet.
|
||||
"""
|
||||
|
||||
pagevars = {
|
||||
"page_title": "To Be Implemented...",
|
||||
}
|
||||
pagevars = {"page_title": "To Be Implemented..."}
|
||||
|
||||
return render(request, 'tbi.html', pagevars)
|
||||
return render(request, "tbi.html", pagevars)
|
||||
|
||||
|
||||
@staff_member_required
|
||||
|
|
@ -90,9 +88,7 @@ def evennia_admin(request):
|
|||
"""
|
||||
Helpful Evennia-specific admin page.
|
||||
"""
|
||||
return render(
|
||||
request, 'evennia_admin.html', {
|
||||
'accountdb': AccountDB})
|
||||
return render(request, "evennia_admin.html", {"accountdb": AccountDB})
|
||||
|
||||
|
||||
def admin_wrapper(request):
|
||||
|
|
@ -106,6 +102,7 @@ def admin_wrapper(request):
|
|||
# Class-based views
|
||||
#
|
||||
|
||||
|
||||
class EvenniaIndexView(TemplateView):
|
||||
"""
|
||||
This is a basic example of a Django class-based view, which are functionally
|
||||
|
|
@ -126,8 +123,9 @@ class EvenniaIndexView(TemplateView):
|
|||
This particular example displays the index page.
|
||||
|
||||
"""
|
||||
|
||||
# Tell the view what HTML template to use for the page
|
||||
template_name = 'website/index.html'
|
||||
template_name = "website/index.html"
|
||||
|
||||
# This method tells the view what data should be displayed on the template.
|
||||
def get_context_data(self, **kwargs):
|
||||
|
|
@ -176,6 +174,7 @@ class TypeclassMixin(object):
|
|||
Django models interchangeably.
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def typeclass(self):
|
||||
return self.model
|
||||
|
|
@ -193,10 +192,11 @@ class EvenniaCreateView(CreateView, TypeclassMixin):
|
|||
otherwise.
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def page_title(self):
|
||||
# Makes sure the page has a sensible title.
|
||||
return 'Create %s' % self.typeclass._meta.verbose_name.title()
|
||||
return "Create %s" % self.typeclass._meta.verbose_name.title()
|
||||
|
||||
|
||||
class EvenniaDetailView(DetailView, TypeclassMixin):
|
||||
|
|
@ -207,10 +207,11 @@ class EvenniaDetailView(DetailView, TypeclassMixin):
|
|||
otherwise.
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def page_title(self):
|
||||
# Makes sure the page has a sensible title.
|
||||
return '%s Detail' % self.typeclass._meta.verbose_name.title()
|
||||
return "%s Detail" % self.typeclass._meta.verbose_name.title()
|
||||
|
||||
|
||||
class EvenniaUpdateView(UpdateView, TypeclassMixin):
|
||||
|
|
@ -221,10 +222,11 @@ class EvenniaUpdateView(UpdateView, TypeclassMixin):
|
|||
otherwise.
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def page_title(self):
|
||||
# Makes sure the page has a sensible title.
|
||||
return 'Update %s' % self.typeclass._meta.verbose_name.title()
|
||||
return "Update %s" % self.typeclass._meta.verbose_name.title()
|
||||
|
||||
|
||||
class EvenniaDeleteView(DeleteView, TypeclassMixin):
|
||||
|
|
@ -235,16 +237,18 @@ class EvenniaDeleteView(DeleteView, TypeclassMixin):
|
|||
otherwise.
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def page_title(self):
|
||||
# Makes sure the page has a sensible title.
|
||||
return 'Delete %s' % self.typeclass._meta.verbose_name.title()
|
||||
return "Delete %s" % self.typeclass._meta.verbose_name.title()
|
||||
|
||||
|
||||
#
|
||||
# Object views
|
||||
#
|
||||
|
||||
|
||||
class ObjectDetailView(EvenniaDetailView):
|
||||
"""
|
||||
This is an important view.
|
||||
|
|
@ -255,6 +259,7 @@ class ObjectDetailView(EvenniaDetailView):
|
|||
permissions to actually *do* things to it.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
#
|
||||
# Choose what class of object this view will display. Note that this should
|
||||
|
|
@ -267,18 +272,18 @@ class ObjectDetailView(EvenniaDetailView):
|
|||
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
|
||||
|
||||
# What HTML template you wish to use to display this page.
|
||||
template_name = 'website/object_detail.html'
|
||||
template_name = "website/object_detail.html"
|
||||
|
||||
# -- Evennia constructs --
|
||||
#
|
||||
# What lock type to check for the requesting user, authenticated or not.
|
||||
# https://github.com/evennia/evennia/wiki/Locks#valid-access_types
|
||||
access_type = 'view'
|
||||
access_type = "view"
|
||||
|
||||
# What attributes of the object you wish to display on the page. Model-level
|
||||
# attributes will take precedence over identically-named db.attributes!
|
||||
# The order you specify here will be followed.
|
||||
attributes = ['name', 'desc']
|
||||
attributes = ["name", "desc"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
|
|
@ -304,15 +309,15 @@ class ObjectDetailView(EvenniaDetailView):
|
|||
for attribute in self.attributes:
|
||||
# Check if the attribute is a core fieldname (name, desc)
|
||||
if attribute in self.typeclass._meta._property_names:
|
||||
attribute_list[attribute.title()] = getattr(obj, attribute, '')
|
||||
attribute_list[attribute.title()] = getattr(obj, attribute, "")
|
||||
|
||||
# Check if the attribute is a db attribute (char1.db.favorite_color)
|
||||
else:
|
||||
attribute_list[attribute.title()] = getattr(obj.db, attribute, '')
|
||||
attribute_list[attribute.title()] = getattr(obj.db, attribute, "")
|
||||
|
||||
# Add our attribute map to the Django request context, so it gets
|
||||
# displayed on the template
|
||||
context['attribute_list'] = attribute_list
|
||||
context["attribute_list"] = attribute_list
|
||||
|
||||
# Return the comprehensive context object
|
||||
return context
|
||||
|
|
@ -336,18 +341,19 @@ class ObjectDetailView(EvenniaDetailView):
|
|||
queryset = self.get_queryset()
|
||||
|
||||
# Get the object, ignoring all checks and filters for now
|
||||
obj = self.typeclass.objects.get(pk=self.kwargs.get('pk'))
|
||||
obj = self.typeclass.objects.get(pk=self.kwargs.get("pk"))
|
||||
|
||||
# Check if this object was requested in a valid manner
|
||||
if slugify(obj.name) != self.kwargs.get(self.slug_url_kwarg):
|
||||
raise HttpResponseBadRequest(
|
||||
"No %(verbose_name)s found matching the query" %
|
||||
{'verbose_name': queryset.model._meta.verbose_name})
|
||||
"No %(verbose_name)s found matching the query"
|
||||
% {"verbose_name": queryset.model._meta.verbose_name}
|
||||
)
|
||||
|
||||
# Check if the requestor account has permissions to access object
|
||||
account = self.request.user
|
||||
if not obj.access(account, self.access_type):
|
||||
raise PermissionDenied(u"You are not authorized to %s this object." % self.access_type)
|
||||
raise PermissionDenied("You are not authorized to %s this object." % self.access_type)
|
||||
|
||||
# Get the object, if it is in the specified queryset
|
||||
obj = super(ObjectDetailView, self).get_object(queryset)
|
||||
|
|
@ -365,6 +371,7 @@ class ObjectCreateView(LoginRequiredMixin, EvenniaCreateView):
|
|||
default title for the page.
|
||||
|
||||
"""
|
||||
|
||||
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
|
||||
|
||||
|
||||
|
|
@ -378,12 +385,13 @@ class ObjectDeleteView(LoginRequiredMixin, ObjectDetailView, EvenniaDeleteView):
|
|||
permissions to delete the requested object.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
|
||||
template_name = 'website/object_confirm_delete.html'
|
||||
template_name = "website/object_confirm_delete.html"
|
||||
|
||||
# -- Evennia constructs --
|
||||
access_type = 'delete'
|
||||
access_type = "delete"
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
"""
|
||||
|
|
@ -420,11 +428,12 @@ class ObjectUpdateView(LoginRequiredMixin, ObjectDetailView, EvenniaUpdateView):
|
|||
it does not update core model fields, *only* object attributes!
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
model = class_from_module(settings.BASE_OBJECT_TYPECLASS)
|
||||
|
||||
# -- Evennia constructs --
|
||||
access_type = 'edit'
|
||||
access_type = "edit"
|
||||
|
||||
def get_success_url(self):
|
||||
"""
|
||||
|
|
@ -454,10 +463,10 @@ class ObjectUpdateView(LoginRequiredMixin, ObjectDetailView, EvenniaUpdateView):
|
|||
obj = self.get_object()
|
||||
|
||||
# Get attributes
|
||||
data = {k: getattr(obj.db, k, '') for k in self.form_class.base_fields}
|
||||
data = {k: getattr(obj.db, k, "") for k in self.form_class.base_fields}
|
||||
|
||||
# Get model fields
|
||||
data.update({k: getattr(obj, k, '') for k in self.form_class.Meta.fields})
|
||||
data.update({k: getattr(obj, k, "") for k in self.form_class.Meta.fields})
|
||||
|
||||
return data
|
||||
|
||||
|
|
@ -493,6 +502,7 @@ class ObjectUpdateView(LoginRequiredMixin, ObjectDetailView, EvenniaUpdateView):
|
|||
# Account views
|
||||
#
|
||||
|
||||
|
||||
class AccountMixin(TypeclassMixin):
|
||||
"""
|
||||
This is a "mixin", a modifier of sorts.
|
||||
|
|
@ -501,6 +511,7 @@ class AccountMixin(TypeclassMixin):
|
|||
with Account objects instead of generic Objects or otherwise.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
model = class_from_module(settings.BASE_ACCOUNT_TYPECLASS)
|
||||
form_class = website_forms.AccountForm
|
||||
|
|
@ -511,9 +522,10 @@ class AccountCreateView(AccountMixin, EvenniaCreateView):
|
|||
Account creation view.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
template_name = 'website/registration/register.html'
|
||||
success_url = reverse_lazy('login')
|
||||
template_name = "website/registration/register.html"
|
||||
success_url = reverse_lazy("login")
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
|
|
@ -526,15 +538,12 @@ class AccountCreateView(AccountMixin, EvenniaCreateView):
|
|||
|
||||
"""
|
||||
# Get values provided
|
||||
username = form.cleaned_data['username']
|
||||
password = form.cleaned_data['password1']
|
||||
email = form.cleaned_data.get('email', '')
|
||||
username = form.cleaned_data["username"]
|
||||
password = form.cleaned_data["password1"]
|
||||
email = form.cleaned_data.get("email", "")
|
||||
|
||||
# Create account
|
||||
account, errs = self.typeclass.create(
|
||||
username=username,
|
||||
password=password,
|
||||
email=email,)
|
||||
account, errs = self.typeclass.create(username=username, password=password, email=email)
|
||||
|
||||
# If unsuccessful, display error messages to user
|
||||
if not account:
|
||||
|
|
@ -544,8 +553,11 @@ class AccountCreateView(AccountMixin, EvenniaCreateView):
|
|||
return self.form_invalid(form)
|
||||
|
||||
# Inform user of success
|
||||
messages.success(self.request, "Your account '%s' was successfully created! "
|
||||
"You may log in using it now." % account.name)
|
||||
messages.success(
|
||||
self.request,
|
||||
"Your account '%s' was successfully created! "
|
||||
"You may log in using it now." % account.name,
|
||||
)
|
||||
|
||||
# Redirect the user to the login page
|
||||
return HttpResponseRedirect(self.success_url)
|
||||
|
|
@ -555,6 +567,7 @@ class AccountCreateView(AccountMixin, EvenniaCreateView):
|
|||
# Character views
|
||||
#
|
||||
|
||||
|
||||
class CharacterMixin(TypeclassMixin):
|
||||
"""
|
||||
This is a "mixin", a modifier of sorts.
|
||||
|
|
@ -563,10 +576,11 @@ class CharacterMixin(TypeclassMixin):
|
|||
with Character objects instead of generic Objects or otherwise.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
model = class_from_module(settings.BASE_CHARACTER_TYPECLASS)
|
||||
form_class = website_forms.CharacterForm
|
||||
success_url = reverse_lazy('character-manage')
|
||||
success_url = reverse_lazy("character-manage")
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
|
|
@ -580,10 +594,10 @@ class CharacterMixin(TypeclassMixin):
|
|||
"""
|
||||
# Get IDs of characters owned by account
|
||||
account = self.request.user
|
||||
ids = [getattr(x, 'id') for x in account.characters if x]
|
||||
ids = [getattr(x, "id") for x in account.characters if x]
|
||||
|
||||
# Return a queryset consisting of those characters
|
||||
return self.typeclass.objects.filter(id__in=ids).order_by(Lower('db_key'))
|
||||
return self.typeclass.objects.filter(id__in=ids).order_by(Lower("db_key"))
|
||||
|
||||
|
||||
class CharacterListView(LoginRequiredMixin, CharacterMixin, ListView):
|
||||
|
|
@ -595,13 +609,14 @@ class CharacterListView(LoginRequiredMixin, CharacterMixin, ListView):
|
|||
human stalkers and automated bots/scrapers from harvesting data on your users.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
template_name = 'website/character_list.html'
|
||||
template_name = "website/character_list.html"
|
||||
paginate_by = 100
|
||||
|
||||
# -- Evennia constructs --
|
||||
page_title = 'Character List'
|
||||
access_type = 'view'
|
||||
page_title = "Character List"
|
||||
access_type = "view"
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
|
|
@ -617,10 +632,11 @@ class CharacterListView(LoginRequiredMixin, CharacterMixin, ListView):
|
|||
|
||||
# Return a queryset consisting of characters the user is allowed to
|
||||
# see.
|
||||
ids = [obj.id for obj in self.typeclass.objects.all()
|
||||
if obj.access(account, self.access_type)]
|
||||
ids = [
|
||||
obj.id for obj in self.typeclass.objects.all() if obj.access(account, self.access_type)
|
||||
]
|
||||
|
||||
return self.typeclass.objects.filter(id__in=ids).order_by(Lower('db_key'))
|
||||
return self.typeclass.objects.filter(id__in=ids).order_by(Lower("db_key"))
|
||||
|
||||
|
||||
class CharacterPuppetView(LoginRequiredMixin, CharacterMixin, RedirectView, ObjectDetailView):
|
||||
|
|
@ -632,6 +648,7 @@ class CharacterPuppetView(LoginRequiredMixin, CharacterMixin, RedirectView, Obje
|
|||
and that their intended puppet is one that they own.
|
||||
|
||||
"""
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
"""
|
||||
Django hook.
|
||||
|
|
@ -647,17 +664,17 @@ class CharacterPuppetView(LoginRequiredMixin, CharacterMixin, RedirectView, Obje
|
|||
char = self.get_object()
|
||||
|
||||
# Get the page the user came from
|
||||
next_page = self.request.GET.get('next', self.success_url)
|
||||
next_page = self.request.GET.get("next", self.success_url)
|
||||
|
||||
if char:
|
||||
# If the account owns the char, store the ID of the char in the
|
||||
# Django request's session (different from Evennia session!).
|
||||
# We do this because characters don't serialize well.
|
||||
self.request.session['puppet'] = int(char.pk)
|
||||
self.request.session["puppet"] = int(char.pk)
|
||||
messages.success(self.request, "You become '%s'!" % char)
|
||||
else:
|
||||
# If the puppeting failed, clear out the cached puppet value
|
||||
self.request.session['puppet'] = None
|
||||
self.request.session["puppet"] = None
|
||||
messages.error(self.request, "You cannot become '%s'." % char)
|
||||
|
||||
return next_page
|
||||
|
|
@ -669,12 +686,13 @@ class CharacterManageView(LoginRequiredMixin, CharacterMixin, ListView):
|
|||
edit, or delete their own characters.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
paginate_by = 10
|
||||
template_name = 'website/character_manage_list.html'
|
||||
template_name = "website/character_manage_list.html"
|
||||
|
||||
# -- Evennia constructs --
|
||||
page_title = 'Manage Characters'
|
||||
page_title = "Manage Characters"
|
||||
|
||||
|
||||
class CharacterUpdateView(CharacterMixin, ObjectUpdateView):
|
||||
|
|
@ -683,9 +701,10 @@ class CharacterUpdateView(CharacterMixin, ObjectUpdateView):
|
|||
ObjectUpdateView) can edit the attributes of a character they own.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
form_class = website_forms.CharacterUpdateForm
|
||||
template_name = 'website/character_form.html'
|
||||
template_name = "website/character_form.html"
|
||||
|
||||
|
||||
class CharacterDetailView(CharacterMixin, ObjectDetailView):
|
||||
|
|
@ -694,13 +713,14 @@ class CharacterDetailView(CharacterMixin, ObjectDetailView):
|
|||
a character, owned by them or not.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
template_name = 'website/object_detail.html'
|
||||
template_name = "website/object_detail.html"
|
||||
|
||||
# -- Evennia constructs --
|
||||
# What attributes to display for this object
|
||||
attributes = ['name', 'desc']
|
||||
access_type = 'view'
|
||||
attributes = ["name", "desc"]
|
||||
access_type = "view"
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
|
|
@ -715,10 +735,11 @@ class CharacterDetailView(CharacterMixin, ObjectDetailView):
|
|||
|
||||
# Return a queryset consisting of characters the user is allowed to
|
||||
# see.
|
||||
ids = [obj.id for obj in self.typeclass.objects.all()
|
||||
if obj.access(account, self.access_type)]
|
||||
ids = [
|
||||
obj.id for obj in self.typeclass.objects.all() if obj.access(account, self.access_type)
|
||||
]
|
||||
|
||||
return self.typeclass.objects.filter(id__in=ids).order_by(Lower('db_key'))
|
||||
return self.typeclass.objects.filter(id__in=ids).order_by(Lower("db_key"))
|
||||
|
||||
|
||||
class CharacterDeleteView(CharacterMixin, ObjectDeleteView):
|
||||
|
|
@ -727,6 +748,7 @@ class CharacterDeleteView(CharacterMixin, ObjectDeleteView):
|
|||
ObjectDeleteView) can delete a character they own.
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
|
@ -736,8 +758,9 @@ class CharacterCreateView(CharacterMixin, ObjectCreateView):
|
|||
ObjectCreateView) can create a new character.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
template_name = 'website/character_form.html'
|
||||
template_name = "website/character_form.html"
|
||||
|
||||
def form_valid(self, form):
|
||||
"""
|
||||
|
|
@ -755,8 +778,8 @@ class CharacterCreateView(CharacterMixin, ObjectCreateView):
|
|||
|
||||
# Get attributes from the form
|
||||
self.attributes = {k: form.cleaned_data[k] for k in form.cleaned_data.keys()}
|
||||
charname = self.attributes.pop('db_key')
|
||||
description = self.attributes.pop('desc')
|
||||
charname = self.attributes.pop("db_key")
|
||||
description = self.attributes.pop("desc")
|
||||
# Create a character
|
||||
character, errors = self.typeclass.create(charname, account, description=description)
|
||||
|
||||
|
|
@ -783,6 +806,7 @@ class CharacterCreateView(CharacterMixin, ObjectCreateView):
|
|||
# Channel views
|
||||
#
|
||||
|
||||
|
||||
class ChannelMixin(TypeclassMixin):
|
||||
"""
|
||||
This is a "mixin", a modifier of sorts.
|
||||
|
|
@ -791,15 +815,16 @@ class ChannelMixin(TypeclassMixin):
|
|||
with HelpEntry objects instead of generic Objects or otherwise.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
model = class_from_module(settings.BASE_CHANNEL_TYPECLASS)
|
||||
|
||||
# -- Evennia constructs --
|
||||
page_title = 'Channels'
|
||||
page_title = "Channels"
|
||||
|
||||
# What lock type to check for the requesting user, authenticated or not.
|
||||
# https://github.com/evennia/evennia/wiki/Locks#valid-access_types
|
||||
access_type = 'listen'
|
||||
access_type = "listen"
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
|
|
@ -816,14 +841,10 @@ class ChannelMixin(TypeclassMixin):
|
|||
channels = self.typeclass.objects.all().iterator()
|
||||
|
||||
# Now figure out which ones the current user is allowed to see
|
||||
bucket = [channel.id for channel in channels if channel.access(account, 'listen')]
|
||||
bucket = [channel.id for channel in channels if channel.access(account, "listen")]
|
||||
|
||||
# Re-query and set a sorted list
|
||||
filtered = self.typeclass.objects.filter(
|
||||
id__in=bucket
|
||||
).order_by(
|
||||
Lower('db_key')
|
||||
)
|
||||
filtered = self.typeclass.objects.filter(id__in=bucket).order_by(Lower("db_key"))
|
||||
|
||||
return filtered
|
||||
|
||||
|
|
@ -834,9 +855,10 @@ class ChannelListView(ChannelMixin, ListView):
|
|||
or not.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
paginate_by = 100
|
||||
template_name = 'website/channel_list.html'
|
||||
template_name = "website/channel_list.html"
|
||||
|
||||
# -- Evennia constructs --
|
||||
page_title = "Channel Index"
|
||||
|
|
@ -854,10 +876,11 @@ class ChannelListView(ChannelMixin, ListView):
|
|||
context = super(ChannelListView, self).get_context_data(**kwargs)
|
||||
|
||||
# Calculate which channels are most popular
|
||||
context['most_popular'] = sorted(
|
||||
context["most_popular"] = sorted(
|
||||
list(self.get_queryset()),
|
||||
key=lambda channel: len(channel.subscriptions.all()),
|
||||
reverse=True)[:self.max_popular]
|
||||
reverse=True,
|
||||
)[: self.max_popular]
|
||||
|
||||
return context
|
||||
|
||||
|
|
@ -867,14 +890,15 @@ class ChannelDetailView(ChannelMixin, ObjectDetailView):
|
|||
Returns the log entries for a given channel.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
template_name = 'website/channel_detail.html'
|
||||
template_name = "website/channel_detail.html"
|
||||
|
||||
# -- Evennia constructs --
|
||||
# What attributes of the object you wish to display on the page. Model-level
|
||||
# attributes will take precedence over identically-named db.attributes!
|
||||
# The order you specify here will be followed.
|
||||
attributes = ['name']
|
||||
attributes = ["name"]
|
||||
|
||||
# How many log entries to read and display.
|
||||
max_num_lines = 10000
|
||||
|
|
@ -893,27 +917,24 @@ class ChannelDetailView(ChannelMixin, ObjectDetailView):
|
|||
|
||||
# Get the filename this Channel is recording to
|
||||
filename = self.object.attributes.get(
|
||||
"log_file", default="channel_%s.log" % self.object.key)
|
||||
"log_file", default="channel_%s.log" % self.object.key
|
||||
)
|
||||
|
||||
# Split log entries so we can filter by time
|
||||
bucket = []
|
||||
for log in (x.strip() for x in tail_log_file(filename, 0, self.max_num_lines)):
|
||||
if not log:
|
||||
continue
|
||||
time, msg = log.split(' [-] ')
|
||||
time_key = time.split(':')[0]
|
||||
time, msg = log.split(" [-] ")
|
||||
time_key = time.split(":")[0]
|
||||
|
||||
bucket.append({
|
||||
'key': time_key,
|
||||
'timestamp': time,
|
||||
'message': msg
|
||||
})
|
||||
bucket.append({"key": time_key, "timestamp": time, "message": msg})
|
||||
|
||||
# Add the processed entries to the context
|
||||
context['object_list'] = bucket
|
||||
context["object_list"] = bucket
|
||||
|
||||
# Get a list of unique timestamps by hour and sort them
|
||||
context['object_filters'] = sorted(set([x['key'] for x in bucket]))
|
||||
context["object_filters"] = sorted(set([x["key"] for x in bucket]))
|
||||
|
||||
return context
|
||||
|
||||
|
|
@ -931,14 +952,15 @@ class ChannelDetailView(ChannelMixin, ObjectDetailView):
|
|||
queryset = self.get_queryset()
|
||||
|
||||
# Find the object in the queryset
|
||||
channel = slugify(self.kwargs.get('slug', ''))
|
||||
channel = slugify(self.kwargs.get("slug", ""))
|
||||
obj = next((x for x in queryset if slugify(x.db_key) == channel), None)
|
||||
|
||||
# Check if this object was requested in a valid manner
|
||||
if not obj:
|
||||
raise HttpResponseBadRequest(
|
||||
"No %(verbose_name)s found matching the query" %
|
||||
{'verbose_name': queryset.model._meta.verbose_name})
|
||||
"No %(verbose_name)s found matching the query"
|
||||
% {"verbose_name": queryset.model._meta.verbose_name}
|
||||
)
|
||||
|
||||
return obj
|
||||
|
||||
|
|
@ -947,6 +969,7 @@ class ChannelDetailView(ChannelMixin, ObjectDetailView):
|
|||
# Help views
|
||||
#
|
||||
|
||||
|
||||
class HelpMixin(TypeclassMixin):
|
||||
"""
|
||||
This is a "mixin", a modifier of sorts.
|
||||
|
|
@ -955,11 +978,12 @@ class HelpMixin(TypeclassMixin):
|
|||
with HelpEntry objects instead of generic Objects or otherwise.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
model = HelpEntry
|
||||
|
||||
# -- Evennia constructs --
|
||||
page_title = 'Help'
|
||||
page_title = "Help"
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
|
|
@ -976,15 +1000,13 @@ class HelpMixin(TypeclassMixin):
|
|||
entries = self.typeclass.objects.all().iterator()
|
||||
|
||||
# Now figure out which ones the current user is allowed to see
|
||||
bucket = [entry.id for entry in entries if entry.access(account, 'view')]
|
||||
bucket = [entry.id for entry in entries if entry.access(account, "view")]
|
||||
|
||||
# Re-query and set a sorted list
|
||||
filtered = self.typeclass.objects.filter(
|
||||
id__in=bucket
|
||||
).order_by(
|
||||
Lower('db_key')
|
||||
).order_by(
|
||||
Lower('db_help_category')
|
||||
filtered = (
|
||||
self.typeclass.objects.filter(id__in=bucket)
|
||||
.order_by(Lower("db_key"))
|
||||
.order_by(Lower("db_help_category"))
|
||||
)
|
||||
|
||||
return filtered
|
||||
|
|
@ -996,9 +1018,10 @@ class HelpListView(HelpMixin, ListView):
|
|||
or not.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
paginate_by = 500
|
||||
template_name = 'website/help_list.html'
|
||||
template_name = "website/help_list.html"
|
||||
|
||||
# -- Evennia constructs --
|
||||
page_title = "Help Index"
|
||||
|
|
@ -1009,8 +1032,9 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
|
|||
Returns the detail page for a given help entry.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
template_name = 'website/help_detail.html'
|
||||
template_name = "website/help_detail.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
|
|
@ -1027,9 +1051,12 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
|
|||
obj = self.get_object()
|
||||
|
||||
# Get queryset and filter out non-related categories
|
||||
queryset = self.get_queryset().filter(
|
||||
db_help_category=obj.db_help_category).order_by(Lower('db_key'))
|
||||
context['topic_list'] = queryset
|
||||
queryset = (
|
||||
self.get_queryset()
|
||||
.filter(db_help_category=obj.db_help_category)
|
||||
.order_by(Lower("db_key"))
|
||||
)
|
||||
context["topic_list"] = queryset
|
||||
|
||||
# Find the index position of the given obj in the queryset
|
||||
objs = list(queryset)
|
||||
|
|
@ -1039,23 +1066,23 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
|
|||
|
||||
# Find the previous and next topics, if either exist
|
||||
try:
|
||||
assert i+1 <= len(objs) and objs[i+1] is not obj
|
||||
context['topic_next'] = objs[i+1]
|
||||
assert i + 1 <= len(objs) and objs[i + 1] is not obj
|
||||
context["topic_next"] = objs[i + 1]
|
||||
except:
|
||||
context['topic_next'] = None
|
||||
context["topic_next"] = None
|
||||
|
||||
try:
|
||||
assert i-1 >= 0 and objs[i-1] is not obj
|
||||
context['topic_previous'] = objs[i-1]
|
||||
assert i - 1 >= 0 and objs[i - 1] is not obj
|
||||
context["topic_previous"] = objs[i - 1]
|
||||
except:
|
||||
context['topic_previous'] = None
|
||||
context["topic_previous"] = None
|
||||
|
||||
# Format the help entry using HTML instead of newlines
|
||||
text = obj.db_entrytext
|
||||
text = text.replace('\r\n\r\n', '\n\n')
|
||||
text = text.replace('\r\n', '\n')
|
||||
text = text.replace('\n', '<br />')
|
||||
context['entry_text'] = text
|
||||
text = text.replace("\r\n\r\n", "\n\n")
|
||||
text = text.replace("\r\n", "\n")
|
||||
text = text.replace("\n", "<br />")
|
||||
context["entry_text"] = text
|
||||
|
||||
return context
|
||||
|
||||
|
|
@ -1073,16 +1100,22 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
|
|||
queryset = self.get_queryset()
|
||||
|
||||
# Find the object in the queryset
|
||||
category = slugify(self.kwargs.get('category', ''))
|
||||
topic = slugify(self.kwargs.get('topic', ''))
|
||||
obj = next((x for x in queryset
|
||||
if slugify(x.db_help_category) == category and
|
||||
slugify(x.db_key) == topic), None)
|
||||
category = slugify(self.kwargs.get("category", ""))
|
||||
topic = slugify(self.kwargs.get("topic", ""))
|
||||
obj = next(
|
||||
(
|
||||
x
|
||||
for x in queryset
|
||||
if slugify(x.db_help_category) == category and slugify(x.db_key) == topic
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
# Check if this object was requested in a valid manner
|
||||
if not obj:
|
||||
raise HttpResponseBadRequest(
|
||||
"No %(verbose_name)s found matching the query" %
|
||||
{'verbose_name': queryset.model._meta.verbose_name})
|
||||
"No %(verbose_name)s found matching the query"
|
||||
% {"verbose_name": queryset.model._meta.verbose_name}
|
||||
)
|
||||
|
||||
return obj
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue