More admin cleanup and doc writing

This commit is contained in:
Griatch 2021-05-22 20:12:48 +02:00
parent b95ec69d66
commit b1b26bf489
7 changed files with 245 additions and 53 deletions

View file

@ -0,0 +1,9 @@
# The Web Admin
The Evennia _Web admin_ is a customized [Django admin site](https://docs.djangoproject.com/en/3.2/ref/contrib/admin/)
and is used for manipulating the game database using a graphical interface.
All actions done in the admin can also be done in-game by use of Admin- or
Builder-commands. The only exception is the assigning of `staff` flags for
allowing users access to the admin itself.

View file

@ -4,7 +4,7 @@ When Evennia starts it also spins up its own Twisted-based web server. The
webserver is responsible for serving the html pages of the game's website. It
can also serve static resources like images and music.
The webclient runs as part of the [Server](Portal-And-Server) process of
The webclient runs as part of the [Server](./Portal-And-Server) process of
Evennia. This means that it can directly access cached objects modified
in-game, and there is no risk of working with objects that are temporarily
out-of-sync in the database.
@ -12,15 +12,15 @@ out-of-sync in the database.
The webserver runs on Twisted and is meant to be used in a production
environment. It leverages the Django web framework and provides:
- A [Game Website](Website) - this is what you see when you go to
- `localhost:4001`. The look of the website is meant to be customized to your
- game. Users logged into the website will be auto-logged into the game if they
- do so with the webclient since they share the same login credentials (there
- is no way to safely do auto-login with telnet clients).
- A [Game Website](./Website) - this is what you see when you go to
`localhost:4001`. The look of the website is meant to be customized to your
game. Users logged into the website will be auto-logged into the game if they
do so with the webclient since they share the same login credentials (there
is no way to safely do auto-login with telnet clients).
- The [Web Admin](Web-Admin) is based on the Django web admin and allows you to
- edit the game database in a graphical interface.
- The [Webclient](Webclient) page is served by the webserver, but the actual
- game communication (sending/receiving data) is done by the javascript client
edit the game database in a graphical interface.
- The [Webclient](./Webclient) page is served by the webserver, but the actual
game communication (sending/receiving data) is done by the javascript client
on the page opening a websocket connection directly to Evennia's Portal.
@ -60,7 +60,7 @@ it operates independently from Evennia. Small snippets of javascript can be
used on a page to have buttons react, make small animations etc that doesn't
require the server.
In the case of the [Webclient](Webclient), Evennia will load the Webclient page
In the case of the [Webclient](./Webclient), Evennia will load the Webclient page
as above, but the page then initiates Javascript code (a lot of it) responsible
for actually displaying the client GUI, allows you to resize windows etc.

View file

@ -1,13 +1,13 @@
# Game Website
When Evennia starts it will also start a [Webserver](Webserver) as part of the
[Server](Portal-And-Server) process. This uses Django to serve a simple but
functional default game website. With the default setup, open your browser to
`localhost:4001` or `127.0.0.1:4001` to see it.
When Evennia starts it will also start a [Webserver](./Webserver) as part of the
[Server](./Portal-And-Server) process. This uses [Django](https://docs.djangoproject.com)
to present a simple but functional default game website. With the default setup,
open your browser to `localhost:4001` or `127.0.0.1:4001` to see it.
The website allows existing players to log in using an account-name and
password they previously used to register with the game. If a user logs in with
the [Webclient](Webclient) they will also log into the website and vice-versa.
the [Webclient](./Webclient) they will also log into the website and vice-versa.
So if you are logged into the website, opening the webclient will automatically
log you into the game as that account.
@ -27,12 +27,11 @@ In the top menu you can find
show a list of all channels available to you and allow you to view the latest
discussions. Most channels require logging in, but the `Public` channel can
also be viewed by non-loggedin users.
- Help - This ties the in-game [Help system](Help-System) to the website. All
- Help - This ties the in-game [Help system](./Help-System) to the website. All
database-based help entries that are publicly available or accessible to your
account can be read. This is a good way to present a body of help for people
to read outside of the game.
- Play Online - This opens the [Webclient](Webclient) in the browser.
- Play Online - This opens the [Webclient](./Webclient) in the browser.
## Modifying the default Website
@ -41,7 +40,13 @@ You'll mostly be doing so in your settings file
(`mygame/server/conf/settings.py` and in the gamedir's `web/folder`
(`mygame/web/` if your game folder is `mygame/`).
As explained on the [Webserver](Webserver) page, the process for getting a web
> When testing your modifications, it's a good idea to add `DEBUG = True` to
> your settings file. This will give you nice informative tracebacks directly
> in your browser instead of generic 404 or 500 error pages. Just remember that
> DEBUG mode leaks memory (for retaining debug info) and is *not* safe to use
> for a production game!
As explained on the [Webserver](./Webserver) page, the process for getting a web
page is
1. Web browser sends HTTP request to server with an URL
@ -74,7 +79,7 @@ structure (leaving out stuff not relevant to the website):
website/
urls.py
views/
(all python files related to website)
(python files related to website)
urls.py
@ -126,28 +131,40 @@ Overridden views (Python modules) also need an additional tweak to the
`website/urls.py` file - you must make sure to repoint the url to the new
version rather than it using the original.
### Title and blurb
## Examples of commom web changes
```important::
Django is a very mature web-design framework. There are endless
internet-tutorials, courses and books available to explain how to use Django.
So these examples only serve as a first primer to get you started.
```
### Change Title and blurb
The website's title and blurb are simply changed by tweaking
`settings.SERVERNAME` and `settings.GAME_SLOGAN`. Your settings file is in
`mygame/server/conf/settings.py`, just change `SERVERNAME = "My Awesome Game"`
and add `GAME_SLOGAN = "The best game in the world"` or something.
`mygame/server/conf/settings.py`, just set/add
### Logo
SERVERNAME = "My Awesome Game"
GAME_SLOGAN = "The best game in the world"
### Change the Logo
The Evennia googly-eyed snake logo is probably not what you want for your game.
The template looks for a file `web/static/website/images/evennia_logo.png`. Just
plop your own PNG logo (64x64 pixels large) in there and name it the same.
### Index page
This is the front page of the website (the 'index' in HTML parlance).
### Change front page HTML
#### Index HTML template
The front page of the website is usually referred to as the 'index' in HTML
parlance.
The frontpage template is found in
`evennia/web/templates/website/index.html`. Just copy this to the equivalent place in
`mygame/web/`. Modify it there and reload the server to see your changes.
The frontpage template is found in `evennia/web/templates/website/index.html`.
Just copy this to the equivalent place in `mygame/web/`. Modify it there and
reload the server to see your changes.
Django templates has a few special features that separate them from normal HTML
documents - they contain a special templating language marked with `{% ... %}` and
@ -176,18 +193,50 @@ Some important things to know:
that as `{{ stats.hp }}` to display `10` at that location to display `10` at
that location.
This allows for template inheritance (making it easier to make all
pages look the same without rewriting the same thing over and over)
There's a lot more information to be found in the [Django template language documentation](https://docs.djangoproject.com/en/3.2/ref/templates/language/).
#### Index View
#### Change front page functionality
To find where the index view is found, we look in `evennia/web/website/urls.py`. Here
we find the following line:
The logic is all in the view. To find where the index-page view is found, we
look in `evennia/web/website/urls.py`. Here we find the following line:
This is found in `evennia/web/website/views/index.py`. If you don't know
```python
# in evennia/web/website/urls.py
...
# website front page
path("", index.EvenniaIndexView.as_view(), name="index"),
...
```
The first `""` is the empty url - root - what you get if you just enter `localhost:4001/`
with no extra path. As expected, this leads to the index page. By looking at the imports
we find the view is in in `evennia/web/website/views/index.py`.
Copy this file to the corresponding location in `mygame/web`. Then tweak your `mygame/web/website/urls.py`
file to point to the new file:
```python
# in mygame/web/website/urls.py
# ...
from web.website.views import index
urlpatterns = [
path("", index.EvenniaIndexView.as_view(), name="index")
]
# ...
```
So we just import `index` from the new location and point to it. After a reload
the front page will now redirect to use your copy rather than the original.
The frontpage view is a class `EvenniaIndexView`. This is a [Django class-based view](https://docs.djangoproject.com/en/3.2/topics/class-based-views/).
It's a little less visible what happens in a class-based view than in a function (since
@ -203,7 +252,111 @@ template. In the index-page's case we want the game stats (number of recent
players etc). These are then made available to use in `{{ ... }}` slots in the
template as described in the previous section.
### Other website pages
### Change other website pages
The other sub pages are handled in the same way - copy the template or static
resource to the right place
resource to the right place, or copy the view and repoint your `website/urls.py` to
your copy. Just remember to reload.
## Adding a new web page
### Using Flat Pages
The absolutely simplest way to add a new web page is to use the `Flat Pages`
app available in the [Web Admin](Web-Admin). The page will appear with the same
styling as the rest of the site.
For the `Flat pages` module to work you must first set up a _Site_ (or
domain) to use. You only need to this once.
- Go to the Web admin and select `Sites`. If your
game is at `mygreatgame.com`, that's the domain you need to add. For local
experimentation, add the domain `localhost:4001`. Note the `id` of the domain
(look at the url when you click on the new domain, if it's for example
`http://localhost:4001/admin/sites/site/2/change/`, then the id is `2`).
- Now add the line `SITE_ID = <id>` to your settings file.
Next you create new pages easily.
- Go the `Flat Pages` web admin and choose to add a new flat page.
- Set the url. If you want the page to appear as e.g. `localhost:4001/test/`, then
add `/test/` here. You need to add both leading and trailing slashes.
- Set `Title` to the name of the page.
- The `Content` is the HTML content of the body of the page. Go wild!
- Finally pick the `Site` you made before, and save.
- (in the advanced section you can make it so that you have to login to see the page etc).
You can now go to `localhost:4001/test/` and see your new page!
### Add Custom new page
The `Flat Pages` page doesn't allow for (much) dynamic content and customization. For
this you need to add the needed components yourself.
Let's see how to make a `/test/` page from scratch.
- Add a new `test.html` file under `mygame/web/templates/website/`. Easiest is to base
this off an existing file. Make sure to `{% extend base.html %}` if you want to
get the same styling as the rest of your site.
- Add a new view `testview.py` under `mygame/web/website/views/` (don't name it `test.py` or
Django/Evennia will think it contains unit tests). Add a view there to process
your page. This is a minimal view to start from (read much more [in the Django docs](https://docs.djangoproject.com/en/3.2/topics/class-based-views/)):
```python
# mygame/web/website/views/testview.py
from django.views.generic import TemplateView
class MyTestView(TemplateView):
template_name = "website/test.html"
```
- Finally, point to your view from the `mygame/web/website/urls.py`:
```python
# in mygame/web/website/urls.py
# ...
from web.website.views import testview
urlpatterns = [
# ...
# we can skip the initial / here
path("test/", testview.MyTestView.as_view())
]
```
- Reload the server and your new page is available. You can now continue to add
all sorts of advanced dynamic content through your view and template!
## User forms
All the pages created so far deal with _presenting_ information to the user.
It's also possible for the user to _input_ data on the page through _forms_. An
example would be a page of fields and sliders you fill in to create a
character, with a big 'Submit' button at the bottom.
Firstly, this must be represented in HTML. The `<form> ... </form>` is a
standard HTML element you need to add to your template. It also has some other
requirements, such as `<input>` and often Javascript components as well (but
usually Django will help with this). If you are unfamiliar with how HTML forms
work, [read about them here](https://docs.djangoproject.com/en/3.2/topics/forms/#html-forms).
The basic gist of it is that when you click to 'submit' the form, a POST HTML
request will be sent to the server containing the data the user entered. It's
now up to the server to make sure the data makes sense (validation) and then
process the input somehow (like creating a new character).
On the backend side, we need to specify the logic for validating and processing
the form data. This is done by the `Form` [Django class](https://docs.djangoproject.com/en/3.2/topics/forms/#forms-in-django).
This specifies _fields_ on itself that define how to validate that piece of data.
The form is then linked into the view-class by adding `form_class = MyFormClass` to
the view (next to `template_name`).
There are several example forms in `evennia/web/website/forms.py`. It's also a good
idea to read [Building a form in Django](https://docs.djangoproject.com/en/3.2/topics/forms/#building-a-form-in-django)
on the Django website - it covers all you need.

View file

@ -340,7 +340,8 @@ div.code-block-caption {
}
.versionchanged {
background-color: #f7f7f7;
background-color: #b7e6e8;
padding-left: 10px;
}
.deprecated {

View file

@ -1,5 +1,5 @@
# Toc
- [API root](api/evennia-api.rst)
- [Coding/Coding Introduction](Coding/Coding-Introduction)
- [Coding/Coding Overview](Coding/Coding-Overview)
- [Coding/Continuous Integration](Coding/Continuous-Integration)
@ -50,6 +50,7 @@
- [Components/Typeclasses](Components/Typeclasses)
- [Components/Webclient](Components/Webclient)
- [Components/Webserver](Components/Webserver)
- [Components/Website](Components/Website)
- [Concepts/Async Process](Concepts/Async-Process)
- [Concepts/Banning](Concepts/Banning)
- [Concepts/Bootstrap & Evennia](Concepts/Bootstrap-&-Evennia)

View file

@ -3,6 +3,7 @@
# in the web admin interface.
#
from django import forms
from django.utils.safestring import mark_safe
from django.conf import settings
from django.contrib import admin, messages
from django.contrib.admin.options import IS_POPUP_VAR
@ -76,6 +77,16 @@ class AccountChangeForm(UserChangeForm):
required=False,
)
# is_superuser = forms.BooleanField(
# label = "Superuser status",
# required=False,
# help_text="Superusers bypass all in-game locks and has all "
# "permissions without explicitly assigning them. Usually "
# "only one superuser (user #1) is needed, new superusers "
# "can be created by setting the `is_superuser` flag in code "
# "or by the `evennia createsuperuser` CLI command."
# )
def clean_username(self):
"""
Clean the username and check its existence.
@ -209,8 +220,8 @@ class AccountAdmin(BaseUserAdmin):
search_fields = ["=id", "^username", "db_typeclass_path"]
ordering = ["-db_date_created", "id"]
list_filter = ["is_superuser", "is_staff", "db_typeclass_path"]
inlines = [AccountTagInline, AccountAttributeInline, ObjectPuppetInline]
readonly_fields = ["db_date_created", "serialized_string"]
inlines = [AccountTagInline, AccountAttributeInline]
readonly_fields = ["db_date_created", "serialized_string", "puppeted_objects"]
view_on_site = False
fieldsets = (
(
@ -223,6 +234,7 @@ class AccountAdmin(BaseUserAdmin):
"db_date_created",
"db_lock_storage",
"db_cmdset_storage",
"puppeted_objects",
"serialized_string",
)
},
@ -265,11 +277,31 @@ class AccountAdmin(BaseUserAdmin):
from evennia.utils import dbserialize
return str(dbserialize.pack_dbobj(obj))
serialized_string.help_text = (
"Copy & paste this string into an Attribute's `value` field to store it there."
)
def puppeted_objects(self, obj):
"""
Get any currently puppeted objects (read only list)
"""
return mark_safe(
", ".join(
'<a href="{url}">{name}</a>'.format(
url=reverse("admin:objects_objectdb_change", args=[obj.id]),
name=obj.db_key)
for obj in ObjectDB.objects.filter(db_account=obj)
)
)
puppeted_objects.help_text = (
"Objects currently puppeted by this Account. "
"Link new ones from the `Objects` admin page.<BR>"
"Note that these will disappear when a user unpuppets or goes offline - "
"this is normal."
)
def get_form(self, request, obj=None, **kwargs):
"""
Overrides help texts.
@ -277,7 +309,9 @@ class AccountAdmin(BaseUserAdmin):
"""
help_texts = kwargs.get("help_texts", {})
help_texts["serialized_string"] = self.serialized_string.help_text
help_texts["puppeted_objects"] = self.puppeted_objects.help_text
kwargs["help_texts"] = help_texts
return super().get_form(request, obj, **kwargs)
@sensitive_post_parameters_m

View file

@ -1,17 +1,16 @@
"""
This structures the website.
This redirects to website sub-pages.
"""
from django.conf import settings
from django.contrib import admin
from django.urls import path, include
from django import views as django_views
from .views import (index, errors, accounts, help as helpviews, channels,
characters)
from .views import (
index, errors, accounts, help as helpviews, channels, characters)
urlpatterns = [
# website front page
#url(r"^$", index.EvenniaIndexView.as_view(), name="index"),
path("", index.EvenniaIndexView.as_view(), name="index"),
# errors
@ -23,18 +22,12 @@ urlpatterns = [
# Help Topics
path("help/", helpviews.HelpListView.as_view(), name="help"),
# url(r"^help/(?P<category>[\w\d\-]+)/(?P<topic>[\w\d\-]+)/$",
# helpviews.HelpDetailView.as_view(),
# name="help-entry-detail"),
path(r"help/<str:category>/<str:topic>/",
helpviews.HelpDetailView.as_view(),
name="help-entry-detail"),
# Channels
path("channels/", channels.ChannelListView.as_view(), name="channels"),
# url(r"^channels/(?P<slug>[\w\d\-]+)/$",
# channels.ChannelDetailView.as_view(),
# name="channel-detail"),
path("channels/<str:slug>/",
channels.ChannelDetailView.as_view(),
name="channel-detail"),
@ -65,6 +58,7 @@ urlpatterns = [
# This sets up the server if the user want to run the Django test server (this
# is not recommended and is usually unnecessary).
if settings.SERVE_MEDIA:
from django import views as django_views
urlpatterns.extend(
[
path("media/<str:path>",