Fix web tutorial formatting and app creation. Resolve #3175.
This commit is contained in:
parent
4a0e71278d
commit
46c7b26c42
6 changed files with 132 additions and 351 deletions
|
|
@ -3,7 +3,10 @@
|
||||||
## Evennia main branch
|
## Evennia main branch
|
||||||
|
|
||||||
- Contrib: Large-language-model (LLM) AI integration; allows NPCs to talk using
|
- Contrib: Large-language-model (LLM) AI integration; allows NPCs to talk using
|
||||||
responses from a neural network server.
|
responses from an LLM server.
|
||||||
|
- Fix: Make sure `at_server_reload` is called also on non-repeating Scripts.
|
||||||
|
- Fix: Webclient was not giving a proper error when sending an unknown outputfunc to it.
|
||||||
|
- Documentation fixes.
|
||||||
|
|
||||||
## Evennia 2.1.0
|
## Evennia 2.1.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
## Evennia main branch
|
## Evennia main branch
|
||||||
|
|
||||||
- Contrib: Large-language-model (LLM) AI integration; allows NPCs to talk using
|
- Contrib: Large-language-model (LLM) AI integration; allows NPCs to talk using
|
||||||
responses from a neural network server.
|
responses from an LLM server.
|
||||||
|
- Fix: Make sure `at_server_reload` is called also on non-repeating Scripts.
|
||||||
|
- Fix: Webclient was not giving a proper error when sending an unknown outputfunc to it.
|
||||||
|
- Documentation fixes.
|
||||||
|
|
||||||
## Evennia 2.1.0
|
## Evennia 2.1.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,23 +3,11 @@
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This tutorial will create a simple web-based interface for generating a new in-game Character.
|
This tutorial will create a simple web-based interface for generating a new in-game Character. Accounts will need to have first logged into the website (with their `AccountDB` account). Once finishing character generation the Character will be created immediately and the Accounts can then log into the game and play immediately (the Character will not require staff approval or anything like that). This guide does not go over how to create an AccountDB on the website with the right permissions to transfer to their web-created characters.
|
||||||
Accounts will need to have first logged into the website (with their `AccountDB` account). Once
|
|
||||||
finishing character generation the Character will be created immediately and the Accounts can then
|
|
||||||
log into the game and play immediately (the Character will not require staff approval or anything
|
|
||||||
like that). This guide does not go over how to create an AccountDB on the website with the right
|
|
||||||
permissions to transfer to their web-created characters.
|
|
||||||
|
|
||||||
It is probably most useful to set `AUTO_CREATE_CHARACTER_WITH_ACCOUNT = False` so that all player
|
It is probably most useful to set `AUTO_CREATE_CHARACTER_WITH_ACCOUNT = False` so that all player characters can be created through this.
|
||||||
characters can be created through this.
|
|
||||||
|
|
||||||
You should have some familiarity with how Django sets up its Model Template View framework. You need
|
You should have some familiarity with how Django sets up its Model Template View framework. You need to understand what is happening in the basic [Web Character View tutorial](./Web-Character-View-Tutorial.md). If you don’t understand the listed tutorial or have a grasp of Django basics, please look at the [Django tutorial](https://docs.djangoproject.com/en/4.1/intro/) to get a taste of what Django does, before throwing Evennia into the mix (Evennia shares its API and attributes with the website interface). This guide will outline the format of the models, views, urls, and html templates needed.
|
||||||
to understand what is happening in the basic [Web Character View tutorial](./Web-Character-View-Tutorial.md).
|
|
||||||
If you don’t understand the listed tutorial or have a grasp of Django basics, please look at the
|
|
||||||
[Django tutorial](https://docs.djangoproject.com/en/4.1/intro/) to get a taste of what Django does,
|
|
||||||
before throwing Evennia into the mix (Evennia shares its API and attributes with the website
|
|
||||||
interface). This guide will outline the format of the models, views, urls, and html templates
|
|
||||||
needed.
|
|
||||||
|
|
||||||
## Pictures
|
## Pictures
|
||||||
|
|
||||||
|
|
@ -31,22 +19,19 @@ Index page, with no character application yet done:
|
||||||

|

|
||||||
***
|
***
|
||||||
|
|
||||||
Having clicked the "create" link you get to create your character (here we will only have name and
|
Having clicked the "create" link you get to create your character (here we will only have name and background, you can add whatever is needed to fit your game):
|
||||||
background, you can add whatever is needed to fit your game):
|
|
||||||
|
|
||||||
***
|
***
|
||||||

|

|
||||||
***
|
***
|
||||||
|
|
||||||
Back to the index page. Having entered our character application (we called our character "TestApp")
|
Back to the index page. Having entered our character application (we called our character "TestApp") you see it listed:
|
||||||
you see it listed:
|
|
||||||
|
|
||||||
***
|
***
|
||||||

|

|
||||||
***
|
***
|
||||||
|
|
||||||
We can also view an already written character application by clicking on it - this brings us to the
|
We can also view an already written character application by clicking on it - this brings us to the *detail* page:
|
||||||
*detail* page:
|
|
||||||
|
|
||||||
***
|
***
|
||||||

|

|
||||||
|
|
@ -56,21 +41,17 @@ We can also view an already written character application by clicking on it - th
|
||||||
|
|
||||||
Assuming your game is named "mygame", navigate to your `mygame/` directory, and type:
|
Assuming your game is named "mygame", navigate to your `mygame/` directory, and type:
|
||||||
|
|
||||||
|
cd web
|
||||||
evennia startapp chargen
|
evennia startapp chargen
|
||||||
|
|
||||||
This will initialize a new Django app we choose to call "chargen". It is directory containing some
|
This will initialize a new Django app we choose to call "chargen" in `mygame/web/`. We put it under `web/` to keep all web stuff together, but you can organize however you like. It is directory containing some basic starting things Django needs.
|
||||||
basic starting things Django needs. You will need to move this directory: for the time being, it is
|
|
||||||
in your `mygame` directory. Better to move it in your `mygame/web` directory, so you have
|
|
||||||
`mygame/web/chargen` in the end.
|
|
||||||
|
|
||||||
Next, navigate to `mygame/server/conf/settings.py` and add or edit the following line to make
|
Next, navigate to `mygame/server/conf/settings.py` and add or edit the following line to make Evennia (and Django) aware of our new app:
|
||||||
Evennia (and Django) aware of our new app:
|
|
||||||
|
|
||||||
INSTALLED_APPS += ('web.chargen',)
|
INSTALLED_APPS += ('web.chargen',)
|
||||||
|
|
||||||
After this, we will get into defining our *models* (the description of the database storage),
|
After this, we will get into defining our *models* (the description of the database storage),
|
||||||
*views* (the server-side website content generators), *urls* (how the web browser finds the pages)
|
*views* (the server-side website content generators), *urls* (how the web browser finds the pages) and *templates* (how the web page should be structured).
|
||||||
and *templates* (how the web page should be structured).
|
|
||||||
|
|
||||||
### Installing - Checkpoint:
|
### Installing - Checkpoint:
|
||||||
|
|
||||||
|
|
@ -82,12 +63,9 @@ and *templates* (how the web page should be structured).
|
||||||
Models are created in `mygame/web/chargen/models.py`.
|
Models are created in `mygame/web/chargen/models.py`.
|
||||||
|
|
||||||
A [Django database model](../Concepts/Models.md) is a Python class that describes the database storage of the
|
A [Django database model](../Concepts/Models.md) is a Python class that describes the database storage of the
|
||||||
data you want to manage. Any data you choose to store is stored in the same database as the game and
|
data you want to manage. Any data you choose to store is stored in the same database as the game and you have access to all the game's objects here.
|
||||||
you have access to all the game's objects here.
|
|
||||||
|
|
||||||
We need to define what a character application actually is. This will differ from game to game so
|
|
||||||
for this tutorial we will define a simple character sheet with the following database fields:
|
|
||||||
|
|
||||||
|
We need to define what a character application actually is. This will differ from game to game so for this tutorial we will define a simple character sheet with the following database fields:
|
||||||
|
|
||||||
* `app_id` (AutoField): Primary key for this character application sheet.
|
* `app_id` (AutoField): Primary key for this character application sheet.
|
||||||
* `char_name` (CharField): The new character's name.
|
* `char_name` (CharField): The new character's name.
|
||||||
|
|
@ -97,8 +75,7 @@ for this tutorial we will define a simple character sheet with the following dat
|
||||||
AccountID from the AccountDB object.
|
AccountID from the AccountDB object.
|
||||||
* `submitted` (BooleanField): `True`/`False` depending on if the application has been submitted yet.
|
* `submitted` (BooleanField): `True`/`False` depending on if the application has been submitted yet.
|
||||||
|
|
||||||
> Note: In a full-fledged game, you’d likely want them to be able to select races, skills,
|
> Note: In a full-fledged game, you’d likely want them to be able to select races, skills, attributes and so on.
|
||||||
attributes and so on.
|
|
||||||
|
|
||||||
Our `models.py` file should look something like this:
|
Our `models.py` file should look something like this:
|
||||||
|
|
||||||
|
|
@ -116,28 +93,20 @@ class CharApp(models.Model):
|
||||||
submitted = models.BooleanField(default=False)
|
submitted = models.BooleanField(default=False)
|
||||||
```
|
```
|
||||||
|
|
||||||
You should consider how you are going to link your application to your account. For this tutorial,
|
You should consider how you are going to link your application to your account. For this tutorial, we are using the account_id attribute on our character application model in order to keep track of which characters are owned by which accounts. Since the account id is a primary key in Evennia, it is a good candidate, as you will never have two of the same IDs in Evennia. You can feel free to use anything else, but for the purposes of this guide, we are going to use account ID to join the character applications with the proper account.
|
||||||
we are using the account_id attribute on our character application model in order to keep track of
|
|
||||||
which characters are owned by which accounts. Since the account id is a primary key in Evennia, it
|
|
||||||
is a good candidate, as you will never have two of the same IDs in Evennia. You can feel free to use
|
|
||||||
anything else, but for the purposes of this guide, we are going to use account ID to join the
|
|
||||||
character applications with the proper account.
|
|
||||||
|
|
||||||
### Model - Checkpoint:
|
### Model - Checkpoint:
|
||||||
|
|
||||||
* you should have filled out `mygame/web/chargen/models.py` with the model class shown above
|
* you should have filled out `mygame/web/chargen/models.py` with the model class shown above (eventually adding fields matching what you need for your game).
|
||||||
(eventually adding fields matching what you need for your game).
|
|
||||||
|
|
||||||
## Create Views
|
## Create Views
|
||||||
|
|
||||||
*Views* are server-side constructs that make dynamic data available to a web page. We are going to
|
*Views* are server-side constructs that make dynamic data available to a web page. We are going to add them to `mygame/web/chargen.views.py`. Each view in our example represents the backbone of a
|
||||||
add them to `mygame/web/chargen.views.py`. Each view in our example represents the backbone of a
|
|
||||||
specific web page. We will use three views and three pages here:
|
specific web page. We will use three views and three pages here:
|
||||||
|
|
||||||
* The index (managing `index.html`). This is what you see when you navigate to
|
* The index (managing `index.html`). This is what you see when you navigate to
|
||||||
`http://yoursite.com/chargen`.
|
`http://yoursite.com/chargen`.
|
||||||
* The detail display sheet (manages `detail.html`). A page that passively displays the stats of a
|
* The detail display sheet (manages `detail.html`). A page that passively displays the stats of a given Character.
|
||||||
given Character.
|
|
||||||
* Character creation sheet (manages `create.html`). This is the main form with fields to fill in.
|
* Character creation sheet (manages `create.html`). This is the main form with fields to fill in.
|
||||||
|
|
||||||
### *Index* view
|
### *Index* view
|
||||||
|
|
@ -163,14 +132,12 @@ def index(request):
|
||||||
|
|
||||||
### *Detail* view
|
### *Detail* view
|
||||||
|
|
||||||
Our detail page will have pertinent character application information our users can see. Since this
|
Our detail page will have pertinent character application information our users can see. Since this is a basic demonstration, our detail page will only show two fields:
|
||||||
is a basic demonstration, our detail page will only show two fields:
|
|
||||||
|
|
||||||
* Character name
|
* Character name
|
||||||
* Character background
|
* Character background
|
||||||
|
|
||||||
We will use the account ID again just to double-check that whoever tries to check our character page
|
We will use the account ID again just to double-check that whoever tries to check our character page is actually the account who owns the application.
|
||||||
is actually the account who owns the application.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# file mygame/web/chargen.views.py
|
# file mygame/web/chargen.views.py
|
||||||
|
|
@ -188,12 +155,9 @@ def detail(request, app_id):
|
||||||
|
|
||||||
## *Creating* view
|
## *Creating* view
|
||||||
|
|
||||||
Predictably, our *create* function will be the most complicated of the views, as it needs to accept
|
Predictably, our *create* function will be the most complicated of the views, as it needs to accept information from the user, validate the information, and send the information to the server. Once the form content is validated will actually create a playable Character.
|
||||||
information from the user, validate the information, and send the information to the server. Once
|
|
||||||
the form content is validated will actually create a playable Character.
|
|
||||||
|
|
||||||
The form itself we will define first. In our simple example we are just looking for the Character's
|
The form itself we will define first. In our simple example we are just looking for the Character's name and background. This form we create in `mygame/web/chargen/forms.py`:
|
||||||
name and background. This form we create in `mygame/web/chargen/forms.py`:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# file mygame/web/chargen/forms.py
|
# file mygame/web/chargen/forms.py
|
||||||
|
|
@ -258,10 +222,7 @@ def creating(request):
|
||||||
return render(request, 'chargen/create.html', {'form': form})
|
return render(request, 'chargen/create.html', {'form': form})
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note also that we basically create the character using the Evennia API, and we grab the proper
|
> Note also that we basically create the character using the Evennia API, and we grab the proper permissions from the `AccountDB` object and copy them to the character object. We take the user permissions attribute and turn that list of strings into a string object in order for the create_object function to properly process the permissions.
|
||||||
permissions from the `AccountDB` object and copy them to the character object. We take the user
|
|
||||||
permissions attribute and turn that list of strings into a string object in order for the
|
|
||||||
create_object function to properly process the permissions.
|
|
||||||
|
|
||||||
Most importantly, the following attributes must be set on the created character object:
|
Most importantly, the following attributes must be set on the created character object:
|
||||||
|
|
||||||
|
|
@ -271,10 +232,7 @@ Most importantly, the following attributes must be set on the created character
|
||||||
* Character name (key)
|
* Character name (key)
|
||||||
* The Character's home room location (`#2` by default)
|
* The Character's home room location (`#2` by default)
|
||||||
|
|
||||||
Other attributes are strictly speaking optional, such as the `background` attribute on our
|
Other attributes are strictly speaking optional, such as the `background` attribute on our character. It may be a good idea to decompose this function and create a separate _create_character function in order to set up your character object the account owns. But with the Evennia API, setting custom attributes is as easy as doing it in the meat of your Evennia game directory.
|
||||||
character. It may be a good idea to decompose this function and create a separate _create_character
|
|
||||||
function in order to set up your character object the account owns. But with the Evennia API,
|
|
||||||
setting custom attributes is as easy as doing it in the meat of your Evennia game directory.
|
|
||||||
|
|
||||||
After all of this, our `views.py` file should look like something like this:
|
After all of this, our `views.py` file should look like something like this:
|
||||||
|
|
||||||
|
|
@ -351,14 +309,12 @@ def creating(request):
|
||||||
### Create Views - Checkpoint:
|
### Create Views - Checkpoint:
|
||||||
|
|
||||||
* you’ve defined a `views.py` that has an index, detail, and creating functions.
|
* you’ve defined a `views.py` that has an index, detail, and creating functions.
|
||||||
* you’ve defined a forms.py with the `AppForm` class needed by the `creating` function of
|
* you’ve defined a forms.py with the `AppForm` class needed by the `creating` function of `views.py`.
|
||||||
`views.py`.
|
|
||||||
* your `mygame/web/chargen` directory should now have a `views.py` and `forms.py` file
|
* your `mygame/web/chargen` directory should now have a `views.py` and `forms.py` file
|
||||||
|
|
||||||
## Create URLs
|
## Create URLs
|
||||||
|
|
||||||
URL patterns helps redirect requests from the web browser to the right views. These patterns are
|
URL patterns helps redirect requests from the web browser to the right views. These patterns are created in `mygame/web/chargen/urls.py`.
|
||||||
created in `mygame/web/chargen/urls.py`.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# file mygame/web/chargen/urls.py
|
# file mygame/web/chargen/urls.py
|
||||||
|
|
@ -376,13 +332,9 @@ urlpatterns = [
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
You could change the format as you desire. To make it more secure, you could remove app_id from the
|
You could change the format as you desire. To make it more secure, you could remove app_id from the "detail" url, and instead just fetch the account’s applications using a unifying field like account_id to find all the character application objects to display.
|
||||||
"detail" url, and instead just fetch the account’s applications using a unifying field like
|
|
||||||
account_id to find all the character application objects to display.
|
|
||||||
|
|
||||||
To add this to our website, we must also update the main `mygame/website/urls.py` file; this
|
To add this to our website, we must also update the main `mygame/website/urls.py` file; this will help tying our new chargen app in with the rest of the website. `urlpatterns` variable, and change it to include:
|
||||||
will help tying our new chargen app in with the rest of the website. `urlpatterns` variable, and
|
|
||||||
change it to include:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# in file mygame/website/urls.py
|
# in file mygame/website/urls.py
|
||||||
|
|
@ -403,21 +355,15 @@ urlpatterns = [
|
||||||
|
|
||||||
## HTML Templates
|
## HTML Templates
|
||||||
|
|
||||||
So we have our url patterns, views, and models defined. Now we must define our HTML templates that
|
So we have our url patterns, views, and models defined. Now we must define our HTML templates that the actual user will see and interact with. For this tutorial we us the basic *prosimii* template that comes with Evennia.
|
||||||
the actual user will see and interact with. For this tutorial we us the basic *prosimii* template
|
|
||||||
that comes with Evennia.
|
|
||||||
|
|
||||||
Take note that we use `user.is_authenticated` to make sure that the user cannot create a character
|
Take note that we use `user.is_authenticated` to make sure that the user cannot create a character without logging in.
|
||||||
without logging in.
|
|
||||||
|
|
||||||
These files will all go into the `/mygame/web/chargen/templates/chargen/` directory.
|
These files will all go into the `/mygame/web/chargen/templates/chargen/` directory.
|
||||||
|
|
||||||
### index.html
|
### index.html
|
||||||
|
|
||||||
This HTML template should hold a list of all the applications the account currently has active. For
|
This HTML template should hold a list of all the applications the account currently has active. For this demonstration, we will only list the applications that the account has submitted. You could easily adjust this to include saved applications, or other types of applications if you have different kinds.
|
||||||
this demonstration, we will only list the applications that the account has submitted. You could
|
|
||||||
easily adjust this to include saved applications, or other types of applications if you have
|
|
||||||
different kinds.
|
|
||||||
|
|
||||||
Please refer back to `views.py` to see where we define the variables these templates make use of.
|
Please refer back to `views.py` to see where we define the variables these templates make use of.
|
||||||
|
|
||||||
|
|
@ -445,10 +391,7 @@ Please refer back to `views.py` to see where we define the variables these templ
|
||||||
|
|
||||||
### detail.html
|
### detail.html
|
||||||
|
|
||||||
This page should show a detailed character sheet of their application. This will only show their
|
This page should show a detailed character sheet of their application. This will only show their name and character background. You will likely want to extend this to show many more fields for your game. In a full-fledged character generation, you may want to extend the boolean attribute of submitted to allow accounts to save character applications and submit them later.
|
||||||
name and character background. You will likely want to extend this to show many more fields for your
|
|
||||||
game. In a full-fledged character generation, you may want to extend the boolean attribute of
|
|
||||||
submitted to allow accounts to save character applications and submit them later.
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- file mygame/web/chargen/templates/chargen/detail.html-->
|
<!-- file mygame/web/chargen/templates/chargen/detail.html-->
|
||||||
|
|
@ -473,11 +416,7 @@ submitted to allow accounts to save character applications and submit them later
|
||||||
|
|
||||||
### create.html
|
### create.html
|
||||||
|
|
||||||
Our create HTML template will use the Django form we defined back in views.py/forms.py to drive the
|
Our create HTML template will use the Django form we defined back in views.py/forms.py to drive the majority of the application process. There will be a form input for every field we defined in forms.py, which is handy. We have used POST as our method because we are sending information to the server that will update the database. As an alternative, GET would be much less secure. You can read up on documentation elsewhere on the web for GET vs. POST.
|
||||||
majority of the application process. There will be a form input for every field we defined in
|
|
||||||
forms.py, which is handy. We have used POST as our method because we are sending information to the
|
|
||||||
server that will update the database. As an alternative, GET would be much less secure. You can read
|
|
||||||
up on documentation elsewhere on the web for GET vs. POST.
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- file mygame/web/chargen/templates/chargen/create.html-->
|
<!-- file mygame/web/chargen/templates/chargen/create.html-->
|
||||||
|
|
@ -499,8 +438,7 @@ up on documentation elsewhere on the web for GET vs. POST.
|
||||||
|
|
||||||
### Templates - Checkpoint:
|
### Templates - Checkpoint:
|
||||||
|
|
||||||
* Create a `index.html`, `detail.html` and `create.html` template in your
|
* Create a `index.html`, `detail.html` and `create.html` template in your `mygame/web/chargen/templates/chargen` directory
|
||||||
`mygame/web/chargen/templates/chargen` directory
|
|
||||||
|
|
||||||
## Activating your new character generation
|
## Activating your new character generation
|
||||||
|
|
||||||
|
|
@ -523,49 +461,30 @@ evennia makemigrations
|
||||||
evennia migrate
|
evennia migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
This will create and update the models. If you see any errors at this stage, read the traceback
|
This will create and update the models. If you see any errors at this stage, read the traceback carefully, it should be relatively easy to figure out where the error is.
|
||||||
carefully, it should be relatively easy to figure out where the error is.
|
|
||||||
|
|
||||||
Login to the website (you need to have previously registered an Player account with the game to do
|
Login to the website (you need to have previously registered an Player account with the game to do this). Next you navigate to `http://yourwebsite.com/chargen` (if you are running locally this will be something like `http://localhost:4001/chargen` and you will see your new app in action.
|
||||||
this). Next you navigate to `http://yourwebsite.com/chargen` (if you are running locally this will
|
|
||||||
be something like `http://localhost:4001/chargen` and you will see your new app in action.
|
|
||||||
|
|
||||||
This should hopefully give you a good starting point in figuring out how you’d like to approach your
|
This should hopefully give you a good starting point in figuring out how you’d like to approach your own web generation. The main difficulties are in setting the appropriate settings on your newly created character object. Thankfully, the Evennia API makes this easy.
|
||||||
own web generation. The main difficulties are in setting the appropriate settings on your newly
|
|
||||||
created character object. Thankfully, the Evennia API makes this easy.
|
|
||||||
|
|
||||||
## Adding a no CAPCHA reCAPCHA on your character generation
|
## Adding a no CAPCHA reCAPCHA on your character generation
|
||||||
|
|
||||||
As sad as it is, if your server is open to the web, bots might come to visit and take advantage of
|
As sad as it is, if your server is open to the web, bots might come to visit and take advantage of your open form to create hundreds, thousands, millions of characters if you give them the opportunity. This section shows you how to use the [No CAPCHA
|
||||||
your open form to create hundreds, thousands, millions of characters if you give them the
|
reCAPCHA](https://www.google.com/recaptcha/intro/invisible.html) designed by Google. Not only is it easy to use, it is user-friendly... for humans. A simple checkbox to check, except if Google has some suspicion, in which case you will have a more difficult test with an image and the usual text inside. It's worth pointing out that, as long as Google doesn't suspect you of being a robot, this is quite useful, not only for common users, but to screen-reader users, to which reading inside of an image is pretty difficult, if not impossible. And to top it all, it will be so easy to add in your website.
|
||||||
opportunity. This section shows you how to use the [No CAPCHA
|
|
||||||
reCAPCHA](https://www.google.com/recaptcha/intro/invisible.html) designed by Google. Not only is it
|
|
||||||
easy to use, it is user-friendly... for humans. A simple checkbox to check, except if Google has
|
|
||||||
some suspicion, in which case you will have a more difficult test with an image and the usual text
|
|
||||||
inside. It's worth pointing out that, as long as Google doesn't suspect you of being a robot, this
|
|
||||||
is quite useful, not only for common users, but to screen-reader users, to which reading inside of
|
|
||||||
an image is pretty difficult, if not impossible. And to top it all, it will be so easy to add in
|
|
||||||
your website.
|
|
||||||
|
|
||||||
### Step 1: Obtain a SiteKey and secret from Google
|
### Step 1: Obtain a SiteKey and secret from Google
|
||||||
|
|
||||||
The first thing is to ask Google for a way to safely authenticate your website to their service. To
|
The first thing is to ask Google for a way to safely authenticate your website to their service. To do it, we need to create a site key and a secret. Go to [https://www.google.com/recaptcha/admin](https://www.google.com/recaptcha/admin) to create such a site key. It's quite easy when you have a Google account.
|
||||||
do it, we need to create a site key and a secret. Go to
|
|
||||||
[https://www.google.com/recaptcha/admin](https://www.google.com/recaptcha/admin) to create such a
|
|
||||||
site key. It's quite easy when you have a Google account.
|
|
||||||
|
|
||||||
When you have created your site key, save it safely. Also copy your secret key as well. You should
|
When you have created your site key, save it safely. Also copy your secret key as well. You should find both information on the web page. Both would contain a lot of letters and figures.
|
||||||
find both information on the web page. Both would contain a lot of letters and figures.
|
|
||||||
|
|
||||||
### Step 2: installing and configuring the dedicated Django app
|
### Step 2: installing and configuring the dedicated Django app
|
||||||
|
|
||||||
Since Evennia runs on Django, the easiest way to add our CAPCHA and perform the proper check is to
|
Since Evennia runs on Django, the easiest way to add our CAPCHA and perform the proper check is to install the dedicated Django app. Quite easy:
|
||||||
install the dedicated Django app. Quite easy:
|
|
||||||
|
|
||||||
pip install django-nocaptcha-recaptcha
|
pip install django-nocaptcha-recaptcha
|
||||||
|
|
||||||
And add it to the installed apps in your settings. In your `mygame/server/conf/settings.py`, you
|
And add it to the installed apps in your settings. In your `mygame/server/conf/settings.py`, you might have something like this:
|
||||||
might have something like this:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# ...
|
# ...
|
||||||
|
|
@ -575,8 +494,7 @@ INSTALLED_APPS += (
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Don't close the setting file just yet. We have to add in the site key and secret key. You can add
|
Don't close the setting file just yet. We have to add in the site key and secret key. You can add them below:
|
||||||
them below:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# NoReCAPCHA site key
|
# NoReCAPCHA site key
|
||||||
|
|
@ -587,9 +505,7 @@ NORECAPTCHA_SECRET_KEY = "PUT YOUR SECRET KEY HERE"
|
||||||
|
|
||||||
### Step 3: Adding the CAPCHA to our form
|
### Step 3: Adding the CAPCHA to our form
|
||||||
|
|
||||||
Finally we have to add the CAPCHA to our form. It will be pretty easy too. First, open your
|
Finally we have to add the CAPCHA to our form. It will be pretty easy too. First, open your `web/chargen/forms.py` file. We're going to add a new field, but hopefully, all the hard work has been done for us. Update at your convenience, You might end up with something like this:
|
||||||
`web/chargen/forms.py` file. We're going to add a new field, but hopefully, all the hard work has
|
|
||||||
been done for us. Update at your convenience, You might end up with something like this:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
@ -610,9 +526,7 @@ And lastly, we need to update our HTML file to add in the Google library. You c
|
||||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
And you should put it at the bottom of the page. Just before the closing body would be good, but
|
And you should put it at the bottom of the page. Just before the closing body would be good, but for the time being, the base page doesn't provide a footer block, so we'll put it in the content block. Note that it's not the best place, but it will work. In the end, your
|
||||||
for the time being, the base page doesn't provide a footer block, so we'll put it in the content
|
|
||||||
block. Note that it's not the best place, but it will work. In the end, your
|
|
||||||
`web/chargen/templates/chargen/create.html` file should look like this:
|
`web/chargen/templates/chargen/create.html` file should look like this:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
|
@ -632,6 +546,4 @@ block. Note that it's not the best place, but it will work. In the end, your
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
```
|
```
|
||||||
|
|
||||||
Reload and open [http://localhost:4001/chargen/create](http://localhost:4001/chargen/create/) and
|
Reload and open [http://localhost:4001/chargen/create](http://localhost:4001/chargen/create/) and you should see your beautiful CAPCHA just before the "submit" button. Try not to check the checkbox to see what happens. And do the same while checking the checkbox!
|
||||||
you should see your beautiful CAPCHA just before the "submit" button. Try not to check the checkbox
|
|
||||||
to see what happens. And do the same while checking the checkbox!
|
|
||||||
|
|
@ -1,40 +1,27 @@
|
||||||
# Web Character View Tutorial
|
# Web Character View Tutorial
|
||||||
|
|
||||||
|
|
||||||
**Before doing this tutorial you will probably want to read the intro in [Basic Web tutorial](Web- Tutorial).**
|
**Before doing this tutorial you will probably want to read the intro in [Changing The Web Page tutorial](./Web-Changing-Webpage.md).**
|
||||||
|
|
||||||
In this tutorial we will create a web page that displays the stats of a game character. For this,
|
In this tutorial we will create a web page that displays the stats of a game character. For this, and all other pages we want to make specific to our game, we'll need to create our own Django "app". We'll call our app `character`, since it will be dealing with character information. From your game dir, run
|
||||||
and all other pages we want to make specific to our game, we'll need to create our own Django "app"
|
|
||||||
|
|
||||||
We'll call our app `character`, since it will be dealing with character information. From your game
|
|
||||||
dir, run
|
|
||||||
|
|
||||||
|
cd web
|
||||||
evennia startapp character
|
evennia startapp character
|
||||||
|
|
||||||
This will create a directory named `character` in the root of your game dir. It contains all basic
|
This will create a new directory named `character` inside `mygame/web/`. We put it in `web/` to keep things tidy, but you could place it wherever you like. It contains all basic files that a Django app needs.
|
||||||
files that a Django app needs. To keep `mygame` well ordered, move it to your `mygame/web/`
|
|
||||||
directory instead:
|
|
||||||
|
|
||||||
mv character web/
|
Note that we will not edit all files in this new directory, many of the generated files are outside the scope of this tutorial.
|
||||||
|
|
||||||
Note that we will not edit all files in this new directory, many of the generated files are outside
|
In order for Django to find our new web app, we'll need to add it to the `INSTALLED_APPS` setting. Evennia's default installed apps are already set, so in `server/conf/settings.py`, we'll just extend them:
|
||||||
the scope of this tutorial.
|
|
||||||
|
|
||||||
In order for Django to find our new web app, we'll need to add it to the `INSTALLED_APPS` setting.
|
|
||||||
Evennia's default installed apps are already set, so in `server/conf/settings.py`, we'll just extend
|
|
||||||
them:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
INSTALLED_APPS += ('web.character',)
|
INSTALLED_APPS += ('web.character',)
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note: That end comma is important. It makes sure that Python interprets the addition as a tuple
|
> Note: That end comma is important. It makes sure that Python interprets the addition as a tuple instead of a string.
|
||||||
instead of a string.
|
|
||||||
|
|
||||||
The first thing we need to do is to create a *view* and an *URL pattern* to point to it. A view is a
|
The first thing we need to do is to create a *view* and an *URL pattern* to point to it. A view is a
|
||||||
function that generates the web page that a visitor wants to see, while the URL pattern lets Django
|
function that generates the web page that a visitor wants to see, while the URL pattern lets Django know what URL should trigger the view. The pattern may also provide some information of its own as we shall see.
|
||||||
know what URL should trigger the view. The pattern may also provide some information of its own as
|
|
||||||
we shall see.
|
|
||||||
|
|
||||||
Here is our `character/urls.py` file (**Note**: you may have to create this file if a blank one
|
Here is our `character/urls.py` file (**Note**: you may have to create this file if a blank one
|
||||||
wasn't generated for you):
|
wasn't generated for you):
|
||||||
|
|
@ -52,32 +39,15 @@ urlpatterns = [
|
||||||
|
|
||||||
This file contains all of the URL patterns for the application. The `url` function in the
|
This file contains all of the URL patterns for the application. The `url` function in the
|
||||||
`urlpatterns` list are given three arguments. The first argument is a pattern-string used to
|
`urlpatterns` list are given three arguments. The first argument is a pattern-string used to
|
||||||
identify which URLs are valid. Patterns are specified as *regular expressions*. Regular expressions
|
identify which URLs are valid. Patterns are specified as *regular expressions*. Regular expressions are used to match strings and are written in a special, very compact, syntax. A detailed description of regular expressions is beyond this tutorial but you can learn more about them [here](https://docs.python.org/2/howto/regex.html). For now, just accept that this regular expression requires that the visitor's URL looks something like this:
|
||||||
are used to match strings and are written in a special, very compact, syntax. A detailed description
|
|
||||||
of regular expressions is beyond this tutorial but you can learn more about them
|
|
||||||
[here](https://docs.python.org/2/howto/regex.html). For now, just accept that this regular
|
|
||||||
expression requires that the visitor's URL looks something like this:
|
|
||||||
|
|
||||||
````
|
````
|
||||||
sheet/123/
|
sheet/123/
|
||||||
````
|
````
|
||||||
|
|
||||||
That is, `sheet/` followed by a number, rather than some other possible URL pattern. We will
|
That is, `sheet/` followed by a number, rather than some other possible URL pattern. We will interpret this number as object ID. Thanks to how the regular expression is formulated, the pattern recognizer stores the number in a variable called `object_id`. This will be passed to the view (see below). We add the imported view function (`sheet`) in the second argument. We also add the `name` keyword to identify the URL pattern itself. You should always name your URL patterns, this makes them easy to refer to in html templates using the `{% url %}` tag (but we won't get more into that in this tutorial).
|
||||||
interpret this number as object ID. Thanks to how the regular expression is formulated, the pattern
|
|
||||||
recognizer stores the number in a variable called `object_id`. This will be passed to the view (see
|
|
||||||
below). We add the imported view function (`sheet`) in the second argument. We also add the `name`
|
|
||||||
keyword to identify the URL pattern itself. You should always name your URL patterns, this makes
|
|
||||||
them easy to refer to in html templates using the `{% url %}` tag (but we won't get more into that
|
|
||||||
in this tutorial).
|
|
||||||
|
|
||||||
> Security Note: Normally, users do not have the ability to see object IDs within the game (it's
|
> Security Note: Normally, users do not have the ability to see object IDs within the game (it's restricted to superusers only). Exposing the game's object IDs to the public like this enables griefers to perform what is known as an [account enumeration attack](http://www.sans.edu/research/security-laboratory/article/attacks-browsing) in the efforts of hijacking your superuser account. Consider this: in every Evennia installation, there are two objects that we can *always* expect to exist and have the same object IDs-- Limbo (#2) and the superuser you create in the beginning (#1). Thus, the griefer can get 50% of the information they need to hijack the admin account (the admin's username) just by navigating to `sheet/1`!
|
||||||
restricted to superusers only). Exposing the game's object IDs to the public like this enables
|
|
||||||
griefers to perform what is known as an [account enumeration
|
|
||||||
attack](http://www.sans.edu/research/security-laboratory/article/attacks-browsing) in the efforts of
|
|
||||||
hijacking your superuser account. Consider this: in every Evennia installation, there are two
|
|
||||||
objects that we can *always* expect to exist and have the same object IDs-- Limbo (#2) and the
|
|
||||||
superuser you create in the beginning (#1). Thus, the griefer can get 50% of the information they
|
|
||||||
need to hijack the admin account (the admin's username) just by navigating to `sheet/1`!
|
|
||||||
|
|
||||||
Next we create `views.py`, the view file that `urls.py` refers to.
|
Next we create `views.py`, the view file that `urls.py` refers to.
|
||||||
|
|
||||||
|
|
@ -103,20 +73,12 @@ def sheet(request, object_id):
|
||||||
return render(request, 'character/sheet.html', {'character': character})
|
return render(request, 'character/sheet.html', {'character': character})
|
||||||
```
|
```
|
||||||
|
|
||||||
As explained earlier, the URL pattern parser in `urls.py` parses the URL and passes `object_id` to
|
As explained earlier, the URL pattern parser in `urls.py` parses the URL and passes `object_id` to our view function `sheet`. We do a database search for the object using this number. We also make sure such an object exists and that it is actually a Character. The view function is also handed a `request` object. This gives us information about the request, such as if a logged-in user viewed it - we won't use that information here but it is good to keep in mind.
|
||||||
our view function `sheet`. We do a database search for the object using this number. We also make
|
|
||||||
sure such an object exists and that it is actually a Character. The view function is also handed a
|
|
||||||
`request` object. This gives us information about the request, such as if a logged-in user viewed it
|
|
||||||
- we won't use that information here but it is good to keep in mind.
|
|
||||||
|
|
||||||
On the last line, we call the `render` function. Apart from the `request` object, the `render`
|
On the last line, we call the `render` function. Apart from the `request` object, the `render`
|
||||||
function takes a path to an html template and a dictionary with extra data you want to pass into
|
function takes a path to an html template and a dictionary with extra data you want to pass into said template. As extra data we pass the Character object we just found. In the template it will be available as the variable "character".
|
||||||
said template. As extra data we pass the Character object we just found. In the template it will be
|
|
||||||
available as the variable "character".
|
|
||||||
|
|
||||||
The html template is created as `templates/character/sheet.html` under your `character` app folder.
|
The html template is created as `templates/character/sheet.html` under your `character` app folder. You may have to manually create both `template` and its subfolder `character`. Here's the template to create:
|
||||||
You may have to manually create both `template` and its subfolder `character`. Here's the template
|
|
||||||
to create:
|
|
||||||
|
|
||||||
````html
|
````html
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
@ -167,32 +129,19 @@ to create:
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
````
|
````
|
||||||
|
|
||||||
In Django templates, `{% ... %}` denotes special in-template "functions" that Django understands.
|
In Django templates, `{% ... %}` denotes special in-template "functions" that Django understands. The `{{ ... }}` blocks work as "slots". They are replaced with whatever value the code inside the block returns.
|
||||||
The `{{ ... }}` blocks work as "slots". They are replaced with whatever value the code inside the
|
|
||||||
block returns.
|
|
||||||
|
|
||||||
The first line, `{% extends "base.html" %}`, tells Django that this template extends the base
|
The first line, `{% extends "base.html" %}`, tells Django that this template extends the base template that Evennia is using. The base template is provided by the theme. Evennia comes with the open-source third-party theme `prosimii`. You can find it and its `base.html` in
|
||||||
template that Evennia is using. The base template is provided by the theme. Evennia comes with the
|
|
||||||
open-source third-party theme `prosimii`. You can find it and its `base.html` in
|
|
||||||
`evennia/web/templates/prosimii`. Like other templates, these can be overwritten.
|
`evennia/web/templates/prosimii`. Like other templates, these can be overwritten.
|
||||||
|
|
||||||
The next line is `{% block content %}`. The `base.html` file has `block`s, which are placeholders
|
The next line is `{% block content %}`. The `base.html` file has `block`s, which are placeholders
|
||||||
that templates can extend. The main block, and the one we use, is named `content`.
|
that templates can extend. The main block, and the one we use, is named `content`.
|
||||||
|
|
||||||
We can access the `character` variable anywhere in the template because we passed it in the `render`
|
We can access the `character` variable anywhere in the template because we passed it in the `render` call at the end of `view.py`. That means we also have access to the Character's `db` attributes, much like you would in normal Python code. You don't have the ability to call functions with arguments in the template-- in fact, if you need to do any complicated logic, you should do it in `view.py` and pass the results as more variables to the template. But you still have a great deal of flexibility in how you display the data.
|
||||||
call at the end of `view.py`. That means we also have access to the Character's `db` attributes,
|
|
||||||
much like you would in normal Python code. You don't have the ability to call functions with
|
|
||||||
arguments in the template-- in fact, if you need to do any complicated logic, you should do it in
|
|
||||||
`view.py` and pass the results as more variables to the template. But you still have a great deal of
|
|
||||||
flexibility in how you display the data.
|
|
||||||
|
|
||||||
We can do a little bit of logic here as well. We use the `{% for %} ... {% endfor %}` and `{% if %}
|
We can do a little bit of logic here as well. We use the `{% for %} ... {% endfor %}` and `{% if %} ... {% else %} ... {% endif %}` structures to change how the template renders depending on how many skills the user has, or if the user is approved (assuming your game has an approval system).
|
||||||
... {% else %} ... {% endif %}` structures to change how the template renders depending on how many
|
|
||||||
skills the user has, or if the user is approved (assuming your game has an approval system).
|
|
||||||
|
|
||||||
The last file we need to edit is the master URLs file. This is needed in order to smoothly integrate
|
The last file we need to edit is the master URLs file. This is needed in order to smoothly integrate the URLs from your new `character` app with the URLs from Evennia's existing pages. Find the file `web/website/urls.py` and update its `patterns` list as follows:
|
||||||
the URLs from your new `character` app with the URLs from Evennia's existing pages. Find the file
|
|
||||||
`web/website/urls.py` and update its `patterns` list as follows:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# web/website/urls.py
|
# web/website/urls.py
|
||||||
|
|
@ -207,11 +156,9 @@ Now reload the server with `evennia reload` and visit the page in your browser.
|
||||||
changed your defaults, you should be able to find the sheet for character `#1` at
|
changed your defaults, you should be able to find the sheet for character `#1` at
|
||||||
`http://localhost:4001/character/sheet/1/`
|
`http://localhost:4001/character/sheet/1/`
|
||||||
|
|
||||||
Try updating the stats in-game and refresh the page in your browser. The results should show
|
Try updating the stats in-game and refresh the page in your browser. The results should show immediately.
|
||||||
immediately.
|
|
||||||
|
|
||||||
As an optional final step, you can also change your character typeclass to have a method called
|
As an optional final step, you can also change your character typeclass to have a method called 'get_absolute_url'.
|
||||||
'get_absolute_url'.
|
|
||||||
```python
|
```python
|
||||||
# typeclasses/characters.py
|
# typeclasses/characters.py
|
||||||
|
|
||||||
|
|
@ -221,9 +168,7 @@ As an optional final step, you can also change your character typeclass to have
|
||||||
return reverse('character:sheet', kwargs={'object_id':self.id})
|
return reverse('character:sheet', kwargs={'object_id':self.id})
|
||||||
```
|
```
|
||||||
Doing so will give you a 'view on site' button in the top right of the Django Admin Objects
|
Doing so will give you a 'view on site' button in the top right of the Django Admin Objects
|
||||||
changepage that links to your new character sheet, and allow you to get the link to a character's
|
changepage that links to your new character sheet, and allow you to get the link to a character's page by using `{{ object.get_absolute_url }}` in any template where you have a given object.
|
||||||
page by using `{{ object.get_absolute_url }}` in any template where you have a given object.
|
|
||||||
|
|
||||||
*Now that you've made a basic page and app with Django, you may want to read the full Django
|
*Now that you've made a basic page and app with Django, you may want to read the full Django tutorial to get a better idea of what it can do. [You can find Django's tutorial
|
||||||
tutorial to get a better idea of what it can do. [You can find Django's tutorial
|
|
||||||
here](https://docs.djangoproject.com/en/4.1/intro/tutorial01/).*
|
here](https://docs.djangoproject.com/en/4.1/intro/tutorial01/).*
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
# Help System Tutorial
|
# Web Help System Tutorial
|
||||||
|
|
||||||
|
|
||||||
**Before doing this tutorial you will probably want to read the intro in [Basic Web tutorial](Web- Tutorial).** Reading the three first parts of the [Django tutorial](https://docs.djangoproject.com/en/4.0/intro/tutorial01/) might help as well.
|
**Before doing this tutorial you will probably want to read the intro in the [Changing the Web page tutorial](./Web-Changing-Webpage.md).** Reading the three first parts of the [Django tutorial](https://docs.djangoproject.com/en/4.0/intro/tutorial01/) might help as well.
|
||||||
|
|
||||||
This tutorial will show you how to access the help system through your website. Both help commands
|
This tutorial will show you how to access the help system through your website. Both help commands and regular help entries will be visible, depending on the logged-in user or an anonymous character.
|
||||||
and regular help entries will be visible, depending on the logged-in user or an anonymous character.
|
|
||||||
|
|
||||||
This tutorial will show you how to:
|
This tutorial will show you how to:
|
||||||
|
|
||||||
|
|
@ -15,23 +14,16 @@ This tutorial will show you how to:
|
||||||
|
|
||||||
## Creating our app
|
## Creating our app
|
||||||
|
|
||||||
The first step is to create our new Django *app*. An app in Django can contain pages and
|
The first step is to create our new Django *app*. An app in Django can contain pages and mechanisms: your website may contain different apps. Actually, the website provided out-of-the-box by Evennia has already three apps: a "webclient" app, to handle the entire webclient, a "website" app to contain your basic pages, and a third app provided by Django to create a simple admin interface. So we'll create another app in parallel, giving it a clear name to represent our help system.
|
||||||
mechanisms: your website may contain different apps. Actually, the website provided out-of-the-box
|
|
||||||
by Evennia has already three apps: a "webclient" app, to handle the entire webclient, a "website"
|
|
||||||
app to contain your basic pages, and a third app provided by Django to create a simple admin
|
|
||||||
interface. So we'll create another app in parallel, giving it a clear name to represent our help
|
|
||||||
system.
|
|
||||||
|
|
||||||
From your game directory, use the following command:
|
From your game directory, use the following commands:
|
||||||
|
|
||||||
|
cd web
|
||||||
evennia startapp help_system
|
evennia startapp help_system
|
||||||
|
|
||||||
> Note: calling the app "help" would have been more explicit, but this name is already used by
|
> Note: calling the app "help" would have been more explicit, but this name is already used by Django.
|
||||||
Django.
|
|
||||||
|
|
||||||
This will create a directory named `help_system` at the root of your game directory. It's a good
|
This will create a directory named `help_system` under `mygame/web/`. We put it there to keep all web-related things together, but you can organize however you like. Here's how the structure looks:
|
||||||
idea to keep things organized and move this directory in the "web" directory of your game. Your
|
|
||||||
game directory should look like:
|
|
||||||
|
|
||||||
mygame/
|
mygame/
|
||||||
...
|
...
|
||||||
|
|
@ -39,13 +31,9 @@ game directory should look like:
|
||||||
help_system/
|
help_system/
|
||||||
...
|
...
|
||||||
|
|
||||||
The "web/help_system" directory contains files created by Django. We'll use some of them, but if
|
The "web/help_system" directory contains files created by Django. We'll use some of them, but if you want to learn more about them all, you should read [the Django tutorial](https://docs.djangoproject.com/en/4.1/intro/tutorial01/).
|
||||||
you want to learn more about them all, you should read [the Django
|
|
||||||
tutorial](https://docs.djangoproject.com/en/4.1/intro/tutorial01/).
|
|
||||||
|
|
||||||
There is a last thing to be done: your folder has been added, but Django doesn't know about it, it
|
There is a last thing to be done: your folder has been added, but Django doesn't know about it, it doesn't know it's a new app. We need to tell it, and we do so by editing a simple setting. Open your "server/conf/settings.py" file and add, or edit, these lines:
|
||||||
doesn't know it's a new app. We need to tell it, and we do so by editing a simple setting. Open
|
|
||||||
your "server/conf/settings.py" file and add, or edit, these lines:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# Web configuration
|
# Web configuration
|
||||||
|
|
@ -54,30 +42,24 @@ INSTALLED_APPS += (
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
You can start Evennia if you want, and go to your website, probably at
|
You can start Evennia if you want, and go to your website, probably at [http://localhost:4001](http://localhost:4001) . You won't see anything different though: we added the app but it's fairly empty.
|
||||||
[http://localhost:4001](http://localhost:4001) . You won't see anything different though: we added
|
|
||||||
the app but it's fairly empty.
|
|
||||||
|
|
||||||
## Our new page
|
## Our new page
|
||||||
|
|
||||||
At this point, our new *app* contains mostly empty files that you can explore. In order to create
|
At this point, our new *app* contains mostly empty files that you can explore. In order to create a page for our help system, we need to add:
|
||||||
a page for our help system, we need to add:
|
|
||||||
|
|
||||||
- A *view*, dealing with the logic of our page.
|
- A *view*, dealing with the logic of our page.
|
||||||
- A *template* to display our new page.
|
- A *template* to display our new page.
|
||||||
- A new *URL* pointing to our page.
|
- A new *URL* pointing to our page.
|
||||||
|
|
||||||
> We could get away by creating just a view and a new URL, but that's not a recommended way to work
|
> We could get away by creating just a view and a new URL, but that's not a recommended way to work with your website. Building on templates is so much more convenient.
|
||||||
with your website. Building on templates is so much more convenient.
|
|
||||||
|
|
||||||
### Create a view
|
### Create a view
|
||||||
|
|
||||||
A *view* in Django is a simple Python function placed in the "views.py" file in your app. It will
|
A *view* in Django is a simple Python function placed in the "views.py" file in your app. It will
|
||||||
handle the behavior that is triggered when a user asks for this information by entering a *URL* (the
|
handle the behavior that is triggered when a user asks for this information by entering a *URL* (the connection between *views* and *URLs* will be discussed later).
|
||||||
connection between *views* and *URLs* will be discussed later).
|
|
||||||
|
|
||||||
So let's create our view. You can open the "web/help_system/views.py" file and paste the following
|
So let's create our view. You can open the "web/help_system/views.py" file and paste the following lines:
|
||||||
lines:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|
@ -87,16 +69,11 @@ def index(request):
|
||||||
return render(request, "help_system/index.html")
|
return render(request, "help_system/index.html")
|
||||||
```
|
```
|
||||||
|
|
||||||
Our view handles all code logic. This time, there's not much: when this function is called, it will
|
Our view handles all code logic. This time, there's not much: when this function is called, it will render the template we will now create. But that's where we will do most of our work afterward.
|
||||||
render the template we will now create. But that's where we will do most of our work afterward.
|
|
||||||
|
|
||||||
### Create a template
|
### Create a template
|
||||||
|
|
||||||
The `render` function called into our *view* asks the *template* `help_system/index.html`. The
|
The `render` function called into our *view* asks the *template* `help_system/index.html`. The *templates* of our apps are stored in the app directory, "templates" sub-directory. Django may have created the "templates" folder already. If not, create it yourself. In it, create another folder "help_system", and inside of this folder, create a file named "index.html". Wow, that's some hierarchy. Your directory structure (starting from `web`) should look like this:
|
||||||
*templates* of our apps are stored in the app directory, "templates" sub-directory. Django may have
|
|
||||||
created the "templates" folder already. If not, create it yourself. In it, create another folder
|
|
||||||
"help_system", and inside of this folder, create a file named "index.html". Wow, that's some
|
|
||||||
hierarchy. Your directory structure (starting from `web`) should look like this:
|
|
||||||
|
|
||||||
web/
|
web/
|
||||||
help_system/
|
help_system/
|
||||||
|
|
@ -117,24 +94,17 @@ Open the "index.html" file and paste in the following lines:
|
||||||
|
|
||||||
Here's a little explanation line by line of what this template does:
|
Here's a little explanation line by line of what this template does:
|
||||||
|
|
||||||
1. It loads the "base.html" *template*. This describes the basic structure of all your pages, with
|
1. It loads the "base.html" *template*. This describes the basic structure of all your pages, with a menu at the top and a footer, and perhaps other information like images and things to be present on each page. You can create templates that do not inherit from "base.html", but you should have a good reason for doing so.
|
||||||
a menu at the top and a footer, and perhaps other information like images and things to be present
|
2. The "base.html" *template* defines all the structure of the page. What is left is to override some sections of our pages. These sections are called *blocks*. On line 2, we override the block named "blocktitle", which contains the title of our page.
|
||||||
on each page. You can create templates that do not inherit from "base.html", but you should have a
|
3. Same thing here, we override the *block* named "content", which contains the main content of our web page. This block is bigger, so we define it on several lines.
|
||||||
good reason for doing so.
|
|
||||||
2. The "base.html" *template* defines all the structure of the page. What is left is to override
|
|
||||||
some sections of our pages. These sections are called *blocks*. On line 2, we override the block
|
|
||||||
named "blocktitle", which contains the title of our page.
|
|
||||||
3. Same thing here, we override the *block* named "content", which contains the main content of our
|
|
||||||
web page. This block is bigger, so we define it on several lines.
|
|
||||||
4. This is perfectly normal HTML code to display a level-2 heading.
|
4. This is perfectly normal HTML code to display a level-2 heading.
|
||||||
5. And finally we close the *block* named "content".
|
5. And finally we close the *block* named "content".
|
||||||
|
|
||||||
### Create a new URL
|
### Create a new URL
|
||||||
|
|
||||||
Last step to add our page: we need to add a *URL* leading to it... otherwise users won't be able to
|
Last step to add our page: we need to add a *URL* leading to it... otherwise users won't be able to access it. The URLs of our apps are stored in the app's directory "urls.py" file.
|
||||||
access it. The URLs of our apps are stored in the app's directory "urls.py" file.
|
|
||||||
|
|
||||||
Open the `web/help_system/urls.py` file (you might have to create it) and make it look like this.
|
Open the `web/help_system/urls.py` file (you might have to create it) and make it look like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# URL patterns for the help_system app
|
# URL patterns for the help_system app
|
||||||
|
|
@ -147,13 +117,9 @@ urlpatterns = [
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
The `urlpatterns` variable is what Django/Evennia looks for to figure out how to
|
The `urlpatterns` variable is what Django/Evennia looks for to figure out how to direct a user entering an URL in their browser to the view-code you have written.
|
||||||
direct a user entering an URL in their browser to the view-code you have
|
|
||||||
written.
|
|
||||||
|
|
||||||
Last we need to tie this into the main namespace for your game. Edit the file
|
Last we need to tie this into the main namespace for your game. Edit the file `mygame/web/urls.py`. In it you will find the `urlpatterns` list again. Add a new `path` to the end of the list.
|
||||||
`mygame/web/urls.py`. In it you will find the `urlpatterns` list again.
|
|
||||||
Add a new `path` to the end of the list.
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# mygame/web/urls.py
|
# mygame/web/urls.py
|
||||||
|
|
@ -177,21 +143,14 @@ urlpatterns = [
|
||||||
|
|
||||||
When a user will ask for a specific *URL* on your site, Django will:
|
When a user will ask for a specific *URL* on your site, Django will:
|
||||||
|
|
||||||
1. Read the list of custom patterns defined in "web/urls.py". There's one pattern here, which
|
1. Read the list of custom patterns defined in "web/urls.py". There's one pattern here, which describes to Django that all URLs beginning by 'help/' should be sent to the 'help_system' app. The 'help/' part is removed.
|
||||||
describes to Django that all URLs beginning by 'help/' should be sent to the 'help_system' app. The
|
2. Then Django will check the "web.help_system/urls.py" file. It contains only one URL, which is empty (`^$`).
|
||||||
'help/' part is removed.
|
|
||||||
2. Then Django will check the "web.help_system/urls.py" file. It contains only one URL, which is
|
|
||||||
empty (`^$`).
|
|
||||||
|
|
||||||
In other words, if the URL is '/help/', then Django will execute our defined view.
|
In other words, if the URL is '/help/', then Django will execute our defined view.
|
||||||
|
|
||||||
### Let's see it work
|
### Let's see it work
|
||||||
|
|
||||||
You can now reload or start Evennia. Open a tab in your browser and go to
|
You can now reload or start Evennia. Open a tab in your browser and go to [http://localhost:4001/help/](http://localhost:4001/help/) . If everything goes well, you should see your new page... which isn't empty since Evennia uses our "base.html" *template*. In the content of our page, there's only a heading that reads "help index". Notice that the title of our page is "mygame - Help index" ("mygame" is replaced by the name of your game).
|
||||||
[http://localhost:4001/help/](http://localhost:4001/help/) . If everything goes well, you should
|
|
||||||
see your new page... which isn't empty since Evennia uses our "base.html" *template*. In the
|
|
||||||
content of our page, there's only a heading that reads "help index". Notice that the title of our
|
|
||||||
page is "mygame - Help index" ("mygame" is replaced by the name of your game).
|
|
||||||
|
|
||||||
From now on, it will be easier to move forward and add features.
|
From now on, it will be easier to move forward and add features.
|
||||||
|
|
||||||
|
|
@ -211,30 +170,17 @@ The first one would link to the second.
|
||||||
|
|
||||||
> Should we create two URLs?
|
> Should we create two URLs?
|
||||||
|
|
||||||
The answer is... maybe. It depends on what you want to do. We have our help index accessible
|
The answer is... maybe. It depends on what you want to do. We have our help index accessible through the "/help/" URL. We could have the detail of a help entry accessible through "/help/desc" (to see the detail of the "desc" command). The problem is that our commands or help topics may contain special characters that aren't to be present in URLs. There are different ways around this problem. I have decided to use a *GET variable* here, which would create URLs like this:
|
||||||
through the "/help/" URL. We could have the detail of a help entry accessible through "/help/desc"
|
|
||||||
(to see the detail of the "desc" command). The problem is that our commands or help topics may
|
|
||||||
contain special characters that aren't to be present in URLs. There are different ways around this
|
|
||||||
problem. I have decided to use a *GET variable* here, which would create URLs like this:
|
|
||||||
|
|
||||||
/help?name=desc
|
/help?name=desc
|
||||||
|
|
||||||
If you use this system, you don't have to add a new URL: GET and POST variables are accessible
|
If you use this system, you don't have to add a new URL: GET and POST variables are accessible through our requests and we'll see how soon enough.
|
||||||
through our requests and we'll see how soon enough.
|
|
||||||
|
|
||||||
## Handling logged-in users
|
## Handling logged-in users
|
||||||
|
|
||||||
One of our requirements is to have a help system tailored to our accounts. If an account with admin
|
One of our requirements is to have a help system tailored to our accounts. If an account with admin access logs in, the page should display a lot of commands that aren't accessible to common users. And perhaps even some additional help topics.
|
||||||
access logs in, the page should display a lot of commands that aren't accessible to common users.
|
|
||||||
And perhaps even some additional help topics.
|
|
||||||
|
|
||||||
Fortunately, it's fairly easy to get the logged in account in our view (remember that we'll do most
|
Fortunately, it's fairly easy to get the logged in account in our view (remember that we'll do most of our coding there). The *request* object, passed to our function, contains a `user` attribute. This attribute will always be there: we cannot test whether it's `None` or not, for instance. But when the request comes from a user that isn't logged in, the `user` attribute will contain an anonymous Django user. We then can use the `is_anonymous` method to see whether the user is logged-in or not. Last gift by Evennia, if the user is logged in, `request.user` contains a reference to an account object, which will help us a lot in coupling the game and online system.
|
||||||
of our coding there). The *request* object, passed to our function, contains a `user` attribute.
|
|
||||||
This attribute will always be there: we cannot test whether it's `None` or not, for instance. But
|
|
||||||
when the request comes from a user that isn't logged in, the `user` attribute will contain an
|
|
||||||
anonymous Django user. We then can use the `is_anonymous` method to see whether the user is logged-
|
|
||||||
in or not. Last gift by Evennia, if the user is logged in, `request.user` contains a reference to
|
|
||||||
an account object, which will help us a lot in coupling the game and online system.
|
|
||||||
|
|
||||||
So we might end up with something like:
|
So we might end up with something like:
|
||||||
|
|
||||||
|
|
@ -246,8 +192,7 @@ def index(request):
|
||||||
character = user.character
|
character = user.character
|
||||||
```
|
```
|
||||||
|
|
||||||
> Note: this code works when your MULTISESSION_MODE is set to 0 or 1. When it's above, you would
|
> Note: this code works when your MULTISESSION_MODE is set to 0 or 1. When it's above, you would have something like:
|
||||||
have something like:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def index(request):
|
def index(request):
|
||||||
|
|
@ -259,16 +204,14 @@ def index(request):
|
||||||
|
|
||||||
In this second case, it will select the first character of the account.
|
In this second case, it will select the first character of the account.
|
||||||
|
|
||||||
But what if the user's not logged in? Again, we have different solutions. One of the most simple
|
But what if the user's not logged in? Again, we have different solutions. One of the most simple is to create a character that will behave as our default character for the help system. You can create it through your game: connect to it and enter:
|
||||||
is to create a character that will behave as our default character for the help system. You can
|
|
||||||
create it through your game: connect to it and enter:
|
|
||||||
|
|
||||||
@charcreate anonymous
|
@charcreate anonymous
|
||||||
|
|
||||||
The system should answer:
|
The system should answer:
|
||||||
|
|
||||||
Created new character anonymous. Use @ic anonymous to enter the game as this character.
|
Created new character anonymous. Use @ic anonymous to enter the game as this character.
|
||||||
|
|
||||||
So in our view, we could have something like this:
|
So in our view, we could have something like this:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
@ -283,16 +226,13 @@ def index(request):
|
||||||
character = Character.objects.get(db_key="anonymous")
|
character = Character.objects.get(db_key="anonymous")
|
||||||
```
|
```
|
||||||
|
|
||||||
This time, we have a valid character no matter what: remember to adapt this code if you're running
|
This time, we have a valid character no matter what: remember to adapt this code if you're running in multisession mode above 1.
|
||||||
in multisession mode above 1.
|
|
||||||
|
|
||||||
## The full system
|
## The full system
|
||||||
|
|
||||||
What we're going to do is to browse through all commands and help entries, and list all the commands
|
What we're going to do is to browse through all commands and help entries, and list all the commands that can be seen by this character (either our 'anonymous' character, or our logged-in character).
|
||||||
that can be seen by this character (either our 'anonymous' character, or our logged-in character).
|
|
||||||
|
|
||||||
The code is longer, but it presents the entire concept in our view. Edit the
|
The code is longer, but it presents the entire concept in our view. Edit the "web/help_system/views.py" file and paste into it:
|
||||||
"web/help_system/views.py" file and paste into it:
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
|
@ -386,26 +326,17 @@ def _get_topics(character):
|
||||||
That's a bit more complicated here, but all in all, it can be divided in small chunks:
|
That's a bit more complicated here, but all in all, it can be divided in small chunks:
|
||||||
|
|
||||||
- The `index` function is our view:
|
- The `index` function is our view:
|
||||||
- It begins by getting the character as we saw in the previous section.
|
- It begins by getting the character as we saw in the previous section.
|
||||||
- It gets the help topics (commands and help entries) accessible to this character. It's another
|
- It gets the help topics (commands and help entries) accessible to this character. It's another function that handles that part.
|
||||||
function that handles that part.
|
- If there's a *GET variable* "name" in our URL (like "/help?name=drop"), it will retrieve it. If it's not a valid topic's name, it returns a *404*. Otherwise, it renders the template called "detail.html", to display the detail of our topic.
|
||||||
- If there's a *GET variable* "name" in our URL (like "/help?name=drop"), it will retrieve it. If
|
- If there's no *GET variable* "name", render "index.html", to display the list of topics.
|
||||||
it's not a valid topic's name, it returns a *404*. Otherwise, it renders the template called
|
- The `_get_topics` is a private function. Its sole mission is to retrieve the commands a character can execute, and the help entries this same character can see. This code is more Evennia-specific than Django-specific, it will not be detailed in this tutorial. Just notice that all help topics are stored in a dictionary. This is to simplify our job when displaying them in our templates.
|
||||||
"detail.html", to display the detail of our topic.
|
|
||||||
- If there's no *GET variable* "name", render "index.html", to display the list of topics.
|
|
||||||
- The `_get_topics` is a private function. Its sole mission is to retrieve the commands a character
|
|
||||||
can execute, and the help entries this same character can see. This code is more Evennia-specific
|
|
||||||
than Django-specific, it will not be detailed in this tutorial. Just notice that all help topics
|
|
||||||
are stored in a dictionary. This is to simplify our job when displaying them in our templates.
|
|
||||||
|
|
||||||
Notice that, in both cases when we asked to render a *template*, we passed to `render` a third
|
Notice that, in both cases when we asked to render a *template*, we passed to `render` a third argument which is the dictionary of variables used in our templates. We can pass variables this way, and we will use them in our templates.
|
||||||
argument which is the dictionary of variables used in our templates. We can pass variables this
|
|
||||||
way, and we will use them in our templates.
|
|
||||||
|
|
||||||
### The index template
|
### The index template
|
||||||
|
|
||||||
Let's look at our full "index" *template*. You can open the
|
Let's look at our full "index" *template*. You can open the "web/help_system/templates/help_sstem/index.html" file and paste the following into it:
|
||||||
"web/help_system/templates/help_sstem/index.html" file and paste the following into it:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
@ -436,17 +367,12 @@ This template is definitely more detailed. What it does is:
|
||||||
|
|
||||||
1. Browse through all categories.
|
1. Browse through all categories.
|
||||||
2. For all categories, display a level-2 heading with the name of the category.
|
2. For all categories, display a level-2 heading with the name of the category.
|
||||||
3. All topics in a category (remember, they can be either commands or help entries) are displayed in
|
3. All topics in a category (remember, they can be either commands or help entries) are displayed in a table. The trickier part may be that, when the loop is above 5, it will create a new line. The table will have 5 columns at the most per row.
|
||||||
a table. The trickier part may be that, when the loop is above 5, it will create a new line. The
|
4. For every cell in the table, we create a link redirecting to the detail page (see below). The URL would look something like "help?name=say". We use `urlencode` to ensure special characters are properly escaped.
|
||||||
table will have 5 columns at the most per row.
|
|
||||||
4. For every cell in the table, we create a link redirecting to the detail page (see below). The
|
|
||||||
URL would look something like "help?name=say". We use `urlencode` to ensure special characters are
|
|
||||||
properly escaped.
|
|
||||||
|
|
||||||
### The detail template
|
### The detail template
|
||||||
|
|
||||||
It's now time to show the detail of a topic (command or help entry). You can create the file
|
It's now time to show the detail of a topic (command or help entry). You can create the file "web/help_system/templates/help_system/detail.html". You can paste into it the following code:
|
||||||
"web/help_system/templates/help_system/detail.html". You can paste into it the following code:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
@ -458,26 +384,17 @@ It's now time to show the detail of a topic (command or help entry). You can cr
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
```
|
```
|
||||||
|
|
||||||
This template is much easier to read. Some *filters* might be unknown to you, but they are just
|
This template is much easier to read. Some *filters* might be unknown to you, but they are just used to format here.
|
||||||
used to format here.
|
|
||||||
|
|
||||||
### Put it all together
|
### Put it all together
|
||||||
|
|
||||||
Remember to reload or start Evennia, and then go to
|
Remember to reload or start Evennia, and then go to [http://localhost:4001/help](http://localhost:4001/help/). You should see the list of commands and topics accessible by all characters. Try to login (click the "login" link in the menu of your website) and go to the same page again. You should now see a more detailed list of commands and help entries. Click on one to see its detail.
|
||||||
[http://localhost:4001/help](http://localhost:4001/help/). You should see the list of commands and
|
|
||||||
topics accessible by all characters. Try to login (click the "login" link in the menu of your
|
|
||||||
website) and go to the same page again. You should now see a more detailed list of commands and
|
|
||||||
help entries. Click on one to see its detail.
|
|
||||||
|
|
||||||
## To improve this feature
|
## To improve this feature
|
||||||
|
|
||||||
As always, a tutorial is here to help you feel comfortable adding new features and code by yourself.
|
As always, a tutorial is here to help you feel comfortable adding new features and code by yourself. Here are some ideas of things to improve this little feature:
|
||||||
Here are some ideas of things to improve this little feature:
|
|
||||||
|
|
||||||
- Links at the bottom of the detail template to go back to the index might be useful.
|
- Links at the bottom of the detail template to go back to the index might be useful.
|
||||||
- A link in the main menu to link to this page would be great... for the time being you have to
|
- A link in the main menu to link to this page would be great... for the time being you have to enter the URL, users won't guess it's there.
|
||||||
enter the URL, users won't guess it's there.
|
|
||||||
- Colors aren't handled at this point, which isn't exactly surprising. You could add it though.
|
- Colors aren't handled at this point, which isn't exactly surprising. You could add it though.
|
||||||
- Linking help entries between one another won't be simple, but it would be great. For instance, if
|
- Linking help entries between one another won't be simple, but it would be great. For instance, if you see a help entry about how to use several commands, it would be great if these commands were themselves links to display their details.
|
||||||
you see a help entry about how to use several commands, it would be great if these commands were
|
|
||||||
themselves links to display their details.
|
|
||||||
|
|
|
||||||
|
|
@ -192,11 +192,12 @@ WEBSOCKET_CLIENT_URL = "wss://fqdn:4002"
|
||||||
|
|
||||||
Also, on Freenode visit the #letsencrypt channel for assistance from the community. For an additional resource, Let's Encrypt has a very active [community forum](https://community.letsencrypt.org/).
|
Also, on Freenode visit the #letsencrypt channel for assistance from the community. For an additional resource, Let's Encrypt has a very active [community forum](https://community.letsencrypt.org/).
|
||||||
|
|
||||||
[A blog where someone sets up Let's Encrypt](https://www.digitalocean.com/community/tutorials/how- to-secure-apache-with-let-s-encrypt-on-ubuntu-16-04)
|
[A blog where someone sets up Let's Encrypt](https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-ubuntu-16-04)
|
||||||
|
|
||||||
The only process missing from all of the above documentation is how to pass verification. This is how Let's Encrypt verifies that you have control over your domain (not necessarily ownership, it's Domain Validation (DV)). This can be done either with configuring a certain path on your web server or through a TXT record in your DNS. Which one you will want to do is a personal preference, but can also be based on your hosting choice. In a controlled/cPanel environment, you will most likely have to use DNS verification.
|
The only process missing from all of the above documentation is how to pass verification. This is how Let's Encrypt verifies that you have control over your domain (not necessarily ownership, it's Domain Validation (DV)). This can be done either with configuring a certain path on your web server or through a TXT record in your DNS. Which one you will want to do is a personal preference, but can also be based on your hosting choice. In a controlled/cPanel environment, you will most likely have to use DNS verification.
|
||||||
|
|
||||||
### Relevant SSL Proxy Setup Information
|
### Relevant SSL Proxy Setup Information
|
||||||
|
|
||||||
- [Apache webserver configuration](./Config-Apache-Proxy.md) (optional)
|
- [Apache webserver configuration](./Config-Apache-Proxy.md) (optional)
|
||||||
- [HAProxy Config](./Config-HAProxy.md)
|
- [HAProxy Config](./Config-HAProxy.md)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue