Customize api and add redoc autodocs
This commit is contained in:
parent
43d678d8ca
commit
87e0796f05
14 changed files with 243 additions and 36 deletions
|
|
@ -204,7 +204,59 @@ 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/).
|
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/).
|
||||||
|
|
||||||
#### Change front page functionality
|
### Change webpage colors and styling
|
||||||
|
|
||||||
|
You can tweak the [CSS](https://en.wikipedia.org/wiki/Cascading_Style_Sheets) of the entire
|
||||||
|
website. If you investigate the `evennia/web/templates/website/base.html` file you'll see that we
|
||||||
|
use the [Bootstrap
|
||||||
|
4](https://getbootstrap.com/docs/4.6/getting-started/introduction/) toolkit.
|
||||||
|
|
||||||
|
Much structural HTML functionality is actually coming from bootstrap, so you
|
||||||
|
will often be able to just add bootstrap CSS classes to elements in the HTML
|
||||||
|
file to get various effects like text-centering or similar.
|
||||||
|
|
||||||
|
The website's custom CSS is found in
|
||||||
|
`evennia/web/static/website/css/website.css` but we also look for a (currently
|
||||||
|
empty) `custom.css` in the same location. You can override either, but it may
|
||||||
|
be easier to revert your changes if you only add things to `custom.css`.
|
||||||
|
|
||||||
|
Copy the CSS file you want to modify to the corresponding location in `mygame/web`.
|
||||||
|
Modify it and reload the server to see your changes.
|
||||||
|
|
||||||
|
You can also apply static files without reloading, but running this in the
|
||||||
|
terminal:
|
||||||
|
|
||||||
|
evennia collectstatic --no-input
|
||||||
|
|
||||||
|
(this is run automatically when reloading the server).
|
||||||
|
|
||||||
|
> Note that before you see new CSS files applied you may need to refresh your
|
||||||
|
> browser without cache (Ctrl-F5 in Firefox, for example).
|
||||||
|
|
||||||
|
As an example, add/copy `custom.css` to `mygame/web/static/website/css/` and
|
||||||
|
add the following:
|
||||||
|
|
||||||
|
|
||||||
|
```css
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background-color: #7a3d54;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: #7a3d54;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Reload and your website now has a red theme!
|
||||||
|
|
||||||
|
> Hint: Learn to use your web browser's [Developer tools](https://torquemag.io/2020/06/browser-developer-tools-tutorial/).
|
||||||
|
> These allow you to tweak CSS 'live' to find a look you like and copy it into
|
||||||
|
> the .css file only when you want to make the changes permanent.
|
||||||
|
|
||||||
|
|
||||||
|
### Change front page functionality
|
||||||
|
|
||||||
The logic is all in the view. To find where the index-page view is found, we
|
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:
|
look in `evennia/web/website/urls.py`. Here we find the following line:
|
||||||
|
|
|
||||||
|
|
@ -880,7 +880,7 @@ STATIC_URL = "/static/"
|
||||||
# served by webserver.
|
# served by webserver.
|
||||||
STATIC_ROOT = os.path.join(GAME_DIR, "server", ".static")
|
STATIC_ROOT = os.path.join(GAME_DIR, "server", ".static")
|
||||||
# Location of static data to overload the defaults from
|
# Location of static data to overload the defaults from
|
||||||
# evennia/web/webclient and evennia/web/website's static/ dirs.
|
# evennia/web/static.
|
||||||
STATICFILES_DIRS = [os.path.join(GAME_DIR, "web", "static")]
|
STATICFILES_DIRS = [os.path.join(GAME_DIR, "web", "static")]
|
||||||
# Patterns of files in the static directories. Used here to make sure that
|
# Patterns of files in the static directories. Used here to make sure that
|
||||||
# its readme file is preserved but unused.
|
# its readme file is preserved but unused.
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ permissions. Individual objects will check lockstrings to determine if the
|
||||||
user has permission to perform retrieve/update/delete actions upon them.
|
user has permission to perform retrieve/update/delete actions upon them.
|
||||||
To start with, you can view a synopsis of endpoints by making a GET request
|
To start with, you can view a synopsis of endpoints by making a GET request
|
||||||
to the `yourgame/api/` endpoint by using the excellent [requests library][requests]:
|
to the `yourgame/api/` endpoint by using the excellent [requests library][requests]:
|
||||||
|
|
||||||
```pythonstub
|
```pythonstub
|
||||||
>>> import requests
|
>>> import requests
|
||||||
>>> r = requests.get("https://www.mygame.com/api", auth=("user", "pw"))
|
>>> r = requests.get("https://www.mygame.com/api", auth=("user", "pw"))
|
||||||
|
|
@ -66,6 +67,7 @@ to the `yourgame/api/` endpoint by using the excellent [requests library][reques
|
||||||
```
|
```
|
||||||
|
|
||||||
To view an object, you might make a request like this:
|
To view an object, you might make a request like this:
|
||||||
|
|
||||||
```pythonstub
|
```pythonstub
|
||||||
>>> import requests
|
>>> import requests
|
||||||
>>> response = requests.get("https://www.mygame.com/api/objects/57",
|
>>> response = requests.get("https://www.mygame.com/api/objects/57",
|
||||||
|
|
@ -77,6 +79,7 @@ The above example makes a GET request to the /objects/ endpoint to retrieve the
|
||||||
object with an ID of 57, retrieving basic data for it.
|
object with an ID of 57, retrieving basic data for it.
|
||||||
|
|
||||||
For listing a number of objects, you might do this:
|
For listing a number of objects, you might do this:
|
||||||
|
|
||||||
```pythonstub
|
```pythonstub
|
||||||
>>> response = requests.get("https://www.mygame.com/api/objects",
|
>>> response = requests.get("https://www.mygame.com/api/objects",
|
||||||
auth=("Myusername", "password123"))
|
auth=("Myusername", "password123"))
|
||||||
|
|
@ -87,11 +90,16 @@ For listing a number of objects, you might do this:
|
||||||
"previous": null,
|
"previous": null,
|
||||||
"results" : [{"db_key": "A rusty longsword", "id": 57, "db_location": 213, ...}]}
|
"results" : [{"db_key": "A rusty longsword", "id": 57, "db_location": 213, ...}]}
|
||||||
```
|
```
|
||||||
In the above example, it now displays the objects inside the "results" array, while it has a "count" value
|
|
||||||
for the number of total objects, and "next" and "previous" links for the next and previous page, if any.
|
In the above example, it now displays the objects inside the "results" array,
|
||||||
This is called [pagination][pagination], and the link displays "limit" and "offset" as query parameters that
|
while it has a "count" value for the number of total objects, and "next" and
|
||||||
can be added to the url to control the output. Other query parameters can be defined as [filters][filters] which
|
"previous" links for the next and previous page, if any. This is called
|
||||||
allow you to further narrow the results. For example, to only get accounts with developer permissions:
|
[pagination][pagination], and the link displays "limit" and "offset" as query
|
||||||
|
parameters that can be added to the url to control the output. Other query
|
||||||
|
parameters can be defined as [filters][filters] which allow you to further
|
||||||
|
narrow the results. For example, to only get accounts with developer
|
||||||
|
permissions:
|
||||||
|
|
||||||
```pythonstub
|
```pythonstub
|
||||||
>>> response = requests.get("https://www.mygame.com/api/accounts/?permission=developer",
|
>>> response = requests.get("https://www.mygame.com/api/accounts/?permission=developer",
|
||||||
auth=("user", "pw"))
|
auth=("user", "pw"))
|
||||||
|
|
@ -104,6 +112,7 @@ allow you to further narrow the results. For example, to only get accounts with
|
||||||
|
|
||||||
Now suppose that you want
|
Now suppose that you want
|
||||||
to use the API to create an object:
|
to use the API to create an object:
|
||||||
|
|
||||||
```pythonstub
|
```pythonstub
|
||||||
>>> data = {"db_key": "A shiny sword"}
|
>>> data = {"db_key": "A shiny sword"}
|
||||||
>>> response = requests.post("https://www.mygame.com/api/objects",
|
>>> response = requests.post("https://www.mygame.com/api/objects",
|
||||||
|
|
@ -111,16 +120,19 @@ to use the API to create an object:
|
||||||
>>> response.json()
|
>>> response.json()
|
||||||
{"db_key": "A shiny sword", "id": 214, "db_location": None, ...}
|
{"db_key": "A shiny sword", "id": 214, "db_location": None, ...}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the above example, you make a POST request to the /objects/ endpoint with
|
In the above example, you make a POST request to the /objects/ endpoint with
|
||||||
the name of the object you wish to create passed along as data. Now suppose you
|
the name of the object you wish to create passed along as data. Now suppose you
|
||||||
decided you didn't like the name, and wanted to change it for the newly created
|
decided you didn't like the name, and wanted to change it for the newly created
|
||||||
object:
|
object:
|
||||||
|
|
||||||
```pythonstub
|
```pythonstub
|
||||||
>>> data = {"db_key": "An even SHINIER sword", "db_location": 50}
|
>>> data = {"db_key": "An even SHINIER sword", "db_location": 50}
|
||||||
>>> response = requests.put("https://www.mygame.com/api/objects/214",
|
>>> response = requests.put("https://www.mygame.com/api/objects/214",
|
||||||
data=data, auth=("Alsoauser", "Badpassword"))
|
data=data, auth=("Alsoauser", "Badpassword"))
|
||||||
>>> response.json()
|
>>> response.json()
|
||||||
{"db_key": "An even SHINIER sword", "id": 214, "db_location": 50, ...}
|
{"db_key": "An even SHINIER sword", "id": 214, "db_location": 50, ...}
|
||||||
|
|
||||||
```
|
```
|
||||||
By making a PUT request to the endpoint that includes the object ID, it becomes
|
By making a PUT request to the endpoint that includes the object ID, it becomes
|
||||||
a request to update the object with the specified data you pass along.
|
a request to update the object with the specified data you pass along.
|
||||||
|
|
|
||||||
17
evennia/web/api/root.py
Normal file
17
evennia/web/api/root.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
"""
|
||||||
|
Set a more useful description on the Api root.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from rest_framework import routers
|
||||||
|
|
||||||
|
|
||||||
|
class EvenniaAPIRoot(routers.APIRootView):
|
||||||
|
"""
|
||||||
|
Root of the Evennia API tree.
|
||||||
|
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class APIRootRouter(routers.DefaultRouter):
|
||||||
|
APIRootView = EvenniaAPIRoot
|
||||||
|
|
@ -14,9 +14,14 @@ retrieve object: action: GET, url: /objects/<:pk>, view name: object-detail
|
||||||
update object: action: POST, url: /objects/<:pk>, view name: object-detail
|
update object: action: POST, url: /objects/<:pk>, view name: object-detail
|
||||||
delete object: action: DELETE, url: /objects/<:pk>, view name: object-detail
|
delete object: action: DELETE, url: /objects/<:pk>, view name: object-detail
|
||||||
set attribute: action: POST, url: /objects/<:pk>/set-attribute, view name: object-set-attribute
|
set attribute: action: POST, url: /objects/<:pk>/set-attribute, view name: object-set-attribute
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from rest_framework import routers
|
from django.urls import path
|
||||||
|
from django.views.generic import TemplateView
|
||||||
|
from rest_framework.schemas import get_schema_view
|
||||||
|
|
||||||
|
from evennia.web.api.root import APIRootRouter
|
||||||
from evennia.web.api.views import (
|
from evennia.web.api.views import (
|
||||||
ObjectDBViewSet,
|
ObjectDBViewSet,
|
||||||
AccountDBViewSet,
|
AccountDBViewSet,
|
||||||
|
|
@ -28,7 +33,7 @@ from evennia.web.api.views import (
|
||||||
|
|
||||||
app_name = "api"
|
app_name = "api"
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = APIRootRouter()
|
||||||
router.trailing_slash = "/?"
|
router.trailing_slash = "/?"
|
||||||
router.register(r"accounts", AccountDBViewSet, basename="account")
|
router.register(r"accounts", AccountDBViewSet, basename="account")
|
||||||
router.register(r"objects", ObjectDBViewSet, basename="object")
|
router.register(r"objects", ObjectDBViewSet, basename="object")
|
||||||
|
|
@ -38,3 +43,21 @@ router.register(r"rooms", RoomViewSet, basename="room")
|
||||||
router.register(r"scripts", ScriptDBViewSet, basename="script")
|
router.register(r"scripts", ScriptDBViewSet, basename="script")
|
||||||
|
|
||||||
urlpatterns = router.urls
|
urlpatterns = router.urls
|
||||||
|
|
||||||
|
urlpatterns += [
|
||||||
|
# openapi schema
|
||||||
|
path('openapi',
|
||||||
|
get_schema_view(
|
||||||
|
title="Evennia API",
|
||||||
|
description="Evennia OpenAPI Schema",
|
||||||
|
version="1.0"),
|
||||||
|
name='openapi',
|
||||||
|
),
|
||||||
|
# redoc auto-doc (based on openapi schema)
|
||||||
|
path('redoc/',
|
||||||
|
TemplateView.as_view(
|
||||||
|
template_name="rest_framework/redoc.html" ,
|
||||||
|
extra_context={'schema_url': 'api:openapi'}),
|
||||||
|
name='redoc'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
"""
|
"""
|
||||||
Views are the functions that are called by different url endpoints.
|
Views are the functions that are called by different url endpoints. The Django
|
||||||
The Django Rest Framework provides collections called 'ViewSets', which
|
Rest Framework provides collections called 'ViewSets', which can generate a
|
||||||
can generate a number of views for the common CRUD operations.
|
number of views for the common CRUD operations.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
|
|
@ -24,10 +25,19 @@ from evennia.web.api.filters import ObjectDBFilterSet, AccountDBFilterSet, Scrip
|
||||||
from evennia.web.api.permissions import EvenniaPermission
|
from evennia.web.api.permissions import EvenniaPermission
|
||||||
|
|
||||||
|
|
||||||
class TypeclassViewSetMixin(object):
|
class TypeclassViewSetMixin:
|
||||||
"""
|
"""
|
||||||
This mixin adds some shared functionality to each viewset of a typeclass. They all use the same
|
This mixin adds some shared functionality to each viewset of a typeclass. They all use the same
|
||||||
permission classes and filter backend. You can override any of these in your own viewsets.
|
permission classes and filter backend. You can override any of these in your own viewsets.
|
||||||
|
|
||||||
|
The `set_atribute` action is an example of a custom action added to a
|
||||||
|
viewset. Based on the name of the method, it will create a default url_name
|
||||||
|
(used for reversing) and url_path. The 'pk' argument is automatically
|
||||||
|
passed to this action because it has a url path of the format <object
|
||||||
|
type>/:pk/set-attribute. The get_object method is automatically set in the
|
||||||
|
expected viewset classes that will inherit this, using the pk that's passed
|
||||||
|
along to retrieve the object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# permission classes determine who is authorized to call the view
|
# permission classes determine who is authorized to call the view
|
||||||
|
|
@ -39,15 +49,9 @@ class TypeclassViewSetMixin(object):
|
||||||
@action(detail=True, methods=["put", "post"])
|
@action(detail=True, methods=["put", "post"])
|
||||||
def set_attribute(self, request, pk=None):
|
def set_attribute(self, request, pk=None):
|
||||||
"""
|
"""
|
||||||
This is an example of a custom action added to a viewset. Based on the name of the
|
This action will set an attribute if the db_value is defined, or remove
|
||||||
method, it will create a default url_name (used for reversing) and url_path.
|
it if no db_value is provided.
|
||||||
The 'pk' argument is automatically passed to this action because it has a url path
|
|
||||||
of the format <object type>/:pk/set-attribute. The get_object method is automatically
|
|
||||||
set in the expected viewset classes that will inherit this, using the pk that's
|
|
||||||
passed along to retrieve the object.
|
|
||||||
|
|
||||||
This action will set an attribute if the db_value is defined, or remove it
|
|
||||||
if no db_value is provided.
|
|
||||||
"""
|
"""
|
||||||
attr = AttributeSerializer(data=request.data)
|
attr = AttributeSerializer(data=request.data)
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
|
|
@ -73,11 +77,14 @@ class TypeclassViewSetMixin(object):
|
||||||
|
|
||||||
class ObjectDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
class ObjectDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
"""
|
"""
|
||||||
An example of a basic viewset for all ObjectDB instances. It declares the
|
The Object is the parent for all in-game entities that have a location
|
||||||
serializer to use for both retrieving and changing/creating/deleting
|
(rooms, exits, characters etc).
|
||||||
instances. Serializers are similar to django forms, used for the
|
|
||||||
transmitting of data (typically json).
|
|
||||||
"""
|
"""
|
||||||
|
# An example of a basic viewset for all ObjectDB instances. It declares the
|
||||||
|
# serializer to use for both retrieving and changing/creating/deleting
|
||||||
|
# instances. Serializers are similar to django forms, used for the
|
||||||
|
# transmitting of data (typically json).
|
||||||
|
|
||||||
serializer_class = ObjectDBSerializer
|
serializer_class = ObjectDBSerializer
|
||||||
queryset = ObjectDB.objects.all()
|
queryset = ObjectDB.objects.all()
|
||||||
|
|
@ -86,8 +93,8 @@ class ObjectDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
|
|
||||||
class CharacterViewSet(ObjectDBViewSet):
|
class CharacterViewSet(ObjectDBViewSet):
|
||||||
"""
|
"""
|
||||||
This overrides the queryset to only retrieve Character objects
|
Characters are a type of Object commonly used as player avatars in-game.
|
||||||
based on your DefaultCharacter typeclass path.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
queryset = DefaultCharacter.objects.typeclass_search(
|
queryset = DefaultCharacter.objects.typeclass_search(
|
||||||
|
|
@ -96,19 +103,29 @@ class CharacterViewSet(ObjectDBViewSet):
|
||||||
|
|
||||||
|
|
||||||
class RoomViewSet(ObjectDBViewSet):
|
class RoomViewSet(ObjectDBViewSet):
|
||||||
"""Viewset for Room objects"""
|
"""
|
||||||
|
Rooms indicate discrete locations in-game.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
queryset = DefaultRoom.objects.typeclass_search(DefaultRoom.path, include_children=True)
|
queryset = DefaultRoom.objects.typeclass_search(DefaultRoom.path, include_children=True)
|
||||||
|
|
||||||
|
|
||||||
class ExitViewSet(ObjectDBViewSet):
|
class ExitViewSet(ObjectDBViewSet):
|
||||||
"""Viewset for Exit objects"""
|
"""
|
||||||
|
Exits are objects with a destination and allows for traversing from one
|
||||||
|
location to another.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
queryset = DefaultExit.objects.typeclass_search(DefaultExit.path, include_children=True)
|
queryset = DefaultExit.objects.typeclass_search(DefaultExit.path, include_children=True)
|
||||||
|
|
||||||
|
|
||||||
class AccountDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
class AccountDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
"""Viewset for Account objects"""
|
"""
|
||||||
|
Accounts represent the players connected to the game
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
serializer_class = AccountSerializer
|
serializer_class = AccountSerializer
|
||||||
queryset = AccountDB.objects.all()
|
queryset = AccountDB.objects.all()
|
||||||
|
|
@ -116,7 +133,11 @@ class AccountDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class ScriptDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
class ScriptDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
"""Viewset for Script objects"""
|
"""
|
||||||
|
Scripts are meta-objects for storing system data, running timers etc. They
|
||||||
|
have no in-game existence.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
serializer_class = ScriptDBSerializer
|
serializer_class = ScriptDBSerializer
|
||||||
queryset = ScriptDB.objects.all()
|
queryset = ScriptDB.objects.all()
|
||||||
|
|
|
||||||
23
evennia/web/static/rest_framework/css/api.css
Normal file
23
evennia/web/static/rest_framework/css/api.css
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
/* CSS overrides for Evennia rest-api page */
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background-color: #3d5c7a;
|
||||||
|
border-top: 0px;
|
||||||
|
padding-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.breadcrumb {
|
||||||
|
/* margin: 70px 0 0 0; */
|
||||||
|
margin: 85px 0 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.str, .atv {
|
||||||
|
color: #114edd;
|
||||||
|
}
|
||||||
|
|
||||||
|
body a {
|
||||||
|
color: #adb5ce;
|
||||||
|
}
|
||||||
|
body a:hover {
|
||||||
|
color: #fff
|
||||||
|
}
|
||||||
BIN
evennia/web/static/rest_framework/images/favicon.ico
Normal file
BIN
evennia/web/static/rest_framework/images/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
35
evennia/web/templates/rest_framework/api.html
Normal file
35
evennia/web/templates/rest_framework/api.html
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Extend and customize the Django REST Framework api look.
|
||||||
|
|
||||||
|
-->
|
||||||
|
{% extends "rest_framework/base.html" %}
|
||||||
|
{% load static sekizai_tags %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
Evennia API
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<!-- Plug in custom Evennia CSS -->
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="icon" type="image/x-icon" href="{% static "website/images/evennia_logo.png" %}" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "rest_framework/css/api.css" %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
{% block branding %}
|
||||||
|
<h3>Evennia REST API</h3>
|
||||||
|
Access game database from external services (requires login and proper access)
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<!-- Sidebar links -->
|
||||||
|
{% block userlinks %}
|
||||||
|
{{ block.super }}
|
||||||
|
<li><a href="/api">Root</a></li>
|
||||||
|
<li><a href="{% url 'api:openapi' %}">Schema</a></li>
|
||||||
|
<li><a href="{% url 'api:redoc' %}">Autodoc</a></li>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
20
evennia/web/templates/rest_framework/redoc.html
Normal file
20
evennia/web/templates/rest_framework/redoc.html
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!--ReDoc is used for generating documentation from OpenAPI schemas -- >
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
{% extends "rest_framework/api.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<redoc spec-url='{% url schema_url %}'></redoc>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -30,20 +30,21 @@ folder and edit it to add/remove links to the menu.
|
||||||
<li><a class="nav-link" href="{% url 'channels' %}">Channels</a></li>
|
<li><a class="nav-link" href="{% url 'channels' %}">Channels</a></li>
|
||||||
<li><a class="nav-link" href="{% url 'help' %}">Help</a></li>
|
<li><a class="nav-link" href="{% url 'help' %}">Help</a></li>
|
||||||
<!-- end game views -->
|
<!-- end game views -->
|
||||||
|
|
||||||
{% if webclient_enabled %}
|
{% if webclient_enabled %}
|
||||||
<li><a class="nav-link" href="{% url 'webclient:index' %}">Play Online</a></li>
|
<li><a class="nav-link" href="{% url 'webclient:index' %}">Play Online</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if user.is_staff %}
|
{% if user.is_staff %}
|
||||||
<li><a class="nav-link" href="{% url 'admin:index' %}">Admin</a></li>
|
<li><a class="nav-link" href="{% url 'admin:index' %}">Admin</a></li>
|
||||||
|
<li><a class="nav-link" href="/api">API</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav ml-auto w-120 justify-content-end">
|
<ul class="nav navbar-nav ml-auto w-120 justify-content-end">
|
||||||
{% block navbar_right %}
|
{% block navbar_right %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block navbar_user %}
|
{% block navbar_user %}
|
||||||
{% if account %}
|
{% if account %}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
|
|
@ -60,7 +61,7 @@ folder and edit it to add/remove links to the menu.
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
{% for character in account.characters|slice:"10" %}
|
{% for character in account.characters|slice:"10" %}
|
||||||
<a class="dropdown-item" href="{{ character.web_get_puppet_url }}?next={{ request.path }}">{{ character }}</a>
|
<a class="dropdown-item" href="{{ character.web_get_puppet_url }}?next={{ request.path }}">{{ character }}</a>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<a class="dropdown-item" href="#">No characters found!</a>
|
<a class="dropdown-item" href="#">No characters found!</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
<link rel="icon" type="image/x-icon" href="/static/website/images/evennia_logo.png" />
|
<link rel="icon" type="image/x-icon" href="{% static "website/images/evennia_logo.png" %}" />
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
<!-- Bootstrap CSS -->
|
||||||
<!--link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.6.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"-->
|
<!--link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.6.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"-->
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ urlpatterns = [
|
||||||
path("webclient/", include("evennia.web.webclient.urls")),
|
path("webclient/", include("evennia.web.webclient.urls")),
|
||||||
# admin
|
# admin
|
||||||
path("admin/", include("evennia.web.admin.urls")),
|
path("admin/", include("evennia.web.admin.urls")),
|
||||||
|
# api
|
||||||
|
path("api/", include("evennia.web.api.urls")),
|
||||||
# favicon
|
# favicon
|
||||||
path("favicon.ico", RedirectView.as_view(url="/media/images/favicon.ico", permanent=False)),
|
path("favicon.ico", RedirectView.as_view(url="/media/images/favicon.ico", permanent=False)),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ django >= 3.2, < 3.3
|
||||||
twisted >= 20.3.0, < 22.0.0
|
twisted >= 20.3.0, < 22.0.0
|
||||||
pytz
|
pytz
|
||||||
djangorestframework >= 3.10.3, < 3.12
|
djangorestframework >= 3.10.3, < 3.12
|
||||||
|
pyyaml
|
||||||
django-filter == 2.4
|
django-filter == 2.4
|
||||||
django-sekizai == 2.0
|
django-sekizai == 2.0
|
||||||
inflect >= 5.2.0
|
inflect >= 5.2.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue