Merge remote-tracking branch 'origin/main' into default-save-quirk
This commit is contained in:
commit
fa1d6e6c3a
11 changed files with 156 additions and 130 deletions
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
- [Feature][issue3273]: Allow passing `text_kwargs` kwarg to `EvMore.msg` in order to expand
|
- [Feature][issue3273]: Allow passing `text_kwargs` kwarg to `EvMore.msg` in order to expand
|
||||||
the outputfunc used for every evmore page.
|
the outputfunc used for every evmore page.
|
||||||
|
- [Feature][pull3278]: Refactor home page into multiple sub-parts for easier
|
||||||
|
overriding and composition (johnnyvoruz)
|
||||||
- [Fix][pull3267]: Missing recache step in ObjectSessionHandler (InspectorCaracal)
|
- [Fix][pull3267]: Missing recache step in ObjectSessionHandler (InspectorCaracal)
|
||||||
- [Fix][pull3270]: Evennia is its own MSSP family now, so we should return that
|
- [Fix][pull3270]: Evennia is its own MSSP family now, so we should return that
|
||||||
instead of 'Custom' (InspectorCaracal)
|
instead of 'Custom' (InspectorCaracal)
|
||||||
|
|
@ -17,6 +19,7 @@
|
||||||
[pull3267]: https://github.com/evennia/evennia/pull/3267
|
[pull3267]: https://github.com/evennia/evennia/pull/3267
|
||||||
[pull3270]: https://github.com/evennia/evennia/pull/3270
|
[pull3270]: https://github.com/evennia/evennia/pull/3270
|
||||||
[pull3274]: https://github.com/evennia/evennia/pull/3274
|
[pull3274]: https://github.com/evennia/evennia/pull/3274
|
||||||
|
[pull3278]: https://github.com/evennia/evennia/pull/3278
|
||||||
[issue3272]: https://github.com/evennia/evennia/issues/3272
|
[issue3272]: https://github.com/evennia/evennia/issues/3272
|
||||||
[issue3273]: https://github.com/evennia/evennia/issues/3273
|
[issue3273]: https://github.com/evennia/evennia/issues/3273
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import re
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
from evennia.comms.managers import ChannelManager
|
from evennia.comms.managers import ChannelManager
|
||||||
from evennia.comms.models import ChannelDB
|
from evennia.comms.models import ChannelDB
|
||||||
from evennia.typeclasses.models import TypeclassBase
|
from evennia.typeclasses.models import TypeclassBase
|
||||||
|
|
@ -188,7 +187,7 @@ class DefaultChannel(ChannelDB, metaclass=TypeclassBase):
|
||||||
# display listening subscribers in bold
|
# display listening subscribers in bold
|
||||||
string = ", ".join(
|
string = ", ".join(
|
||||||
[
|
[
|
||||||
account.key if account not in listening else "|w%s|n" % account.key
|
account.key if account not in listening else f"|w{account.key}|n"
|
||||||
for account in subs
|
for account in subs
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ necessary to easily be able to delete connections on the fly).
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from evennia.comms import managers
|
from evennia.comms import managers
|
||||||
from evennia.locks.lockhandler import LockHandler
|
from evennia.locks.lockhandler import LockHandler
|
||||||
from evennia.typeclasses.models import TypedObject
|
from evennia.typeclasses.models import TypedObject
|
||||||
|
|
@ -104,8 +103,10 @@ class Msg(SharedMemoryModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
db_index=True,
|
db_index=True,
|
||||||
help_text="Identifier for single external sender, for use with senders "
|
help_text=(
|
||||||
"not represented by a regular database model.",
|
"Identifier for single external sender, for use with senders "
|
||||||
|
"not represented by a regular database model."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
db_receivers_accounts = models.ManyToManyField(
|
db_receivers_accounts = models.ManyToManyField(
|
||||||
|
|
@ -137,8 +138,10 @@ class Msg(SharedMemoryModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
db_index=True,
|
db_index=True,
|
||||||
help_text="Identifier for single external receiver, for use with recievers "
|
help_text=(
|
||||||
"not represented by a regular database model.",
|
"Identifier for single external receiver, for use with recievers "
|
||||||
|
"not represented by a regular database model."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# header could be used for meta-info about the message if your system needs
|
# header could be used for meta-info about the message if your system needs
|
||||||
|
|
@ -167,8 +170,10 @@ class Msg(SharedMemoryModel):
|
||||||
db_tags = models.ManyToManyField(
|
db_tags = models.ManyToManyField(
|
||||||
Tag,
|
Tag,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="tags on this message. Tags are simple string markers to "
|
help_text=(
|
||||||
"identify, group and alias messages.",
|
"tags on this message. Tags are simple string markers to "
|
||||||
|
"identify, group and alias messages."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Database manager
|
# Database manager
|
||||||
|
|
@ -518,7 +523,7 @@ class TempMsg:
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
class SubscriptionHandler(object):
|
class SubscriptionHandler:
|
||||||
"""
|
"""
|
||||||
This handler manages subscriptions to the
|
This handler manages subscriptions to the
|
||||||
channel and hides away which type of entity is
|
channel and hides away which type of entity is
|
||||||
|
|
@ -540,13 +545,13 @@ class SubscriptionHandler(object):
|
||||||
def _recache(self):
|
def _recache(self):
|
||||||
self._cache = {
|
self._cache = {
|
||||||
account: True
|
account: True
|
||||||
for account in self.obj.db_account_subscriptions.all()
|
for account in self.obj.db_account_subscriptions.all().order_by("pk")
|
||||||
if hasattr(account, "pk") and account.pk
|
if hasattr(account, "pk") and account.pk
|
||||||
}
|
}
|
||||||
self._cache.update(
|
self._cache.update(
|
||||||
{
|
{
|
||||||
obj: True
|
obj: True
|
||||||
for obj in self.obj.db_object_subscriptions.all()
|
for obj in self.obj.db_object_subscriptions.all().order_by("pk")
|
||||||
if hasattr(obj, "pk") and obj.pk
|
if hasattr(obj, "pk") and obj.pk
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ The custom manager for Scripts.
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from evennia.server import signals
|
from evennia.server import signals
|
||||||
from evennia.typeclasses.managers import TypeclassManager, TypedObjectManager
|
from evennia.typeclasses.managers import TypeclassManager, TypedObjectManager
|
||||||
from evennia.utils.utils import class_from_module, dbid_to_obj, make_iter
|
from evennia.utils.utils import class_from_module, dbid_to_obj, make_iter
|
||||||
|
|
|
||||||
|
|
@ -78,26 +78,28 @@ class TestScriptHandler(BaseEvenniaTest):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.obj, self.errors = DefaultObject.create("test_object")
|
self.obj, self.errors = DefaultObject.create("test_object")
|
||||||
|
self.obj.scripts.add(TestingListIntervalScript)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.obj.delete()
|
self.obj.delete()
|
||||||
|
|
||||||
def test_start(self):
|
def test_start(self):
|
||||||
"Check that ScriptHandler start function works correctly"
|
"Check that ScriptHandler start function works correctly"
|
||||||
self.obj.scripts.add(TestingListIntervalScript)
|
|
||||||
self.num = self.obj.scripts.start(self.obj.scripts.all()[0].key)
|
self.num = self.obj.scripts.start(self.obj.scripts.all()[0].key)
|
||||||
self.assertTrue(self.num == 1)
|
self.assertEqual(self.num, 1)
|
||||||
|
|
||||||
def test_list_script_intervals(self):
|
def test_list_script_intervals(self):
|
||||||
"Checks that Scripthandler __str__ function lists script intervals correctly"
|
"Checks that Scripthandler __str__ function lists script intervals correctly"
|
||||||
self.obj.scripts.add(TestingListIntervalScript)
|
|
||||||
self.str = str(self.obj.scripts)
|
self.str = str(self.obj.scripts)
|
||||||
self.assertTrue("None/1" in self.str)
|
self.assertTrue("None/1" in self.str)
|
||||||
self.assertTrue("1 repeats" in self.str)
|
self.assertTrue("1 repeats" in self.str)
|
||||||
|
|
||||||
|
def test_get_all_scripts(self):
|
||||||
|
"Checks that Scripthandler get_all returns correct number of scripts"
|
||||||
|
self.assertEqual([script.key for script in self.obj.scripts.all()], ["interval_test"])
|
||||||
|
|
||||||
def test_get_script(self):
|
def test_get_script(self):
|
||||||
"Checks that Scripthandler get function returns correct script"
|
"Checks that Scripthandler get function returns correct script"
|
||||||
self.obj.scripts.add(TestingListIntervalScript)
|
|
||||||
script = self.obj.scripts.get("interval_test")
|
script = self.obj.scripts.get("interval_test")
|
||||||
self.assertTrue(bool(script))
|
self.assertTrue(bool(script))
|
||||||
|
|
||||||
|
|
|
||||||
14
evennia/web/templates/website/homepage/accounts-widget.html
Normal file
14
evennia/web/templates/website/homepage/accounts-widget.html
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<div class="card">
|
||||||
|
<h4 class="card-header text-center">Accounts</h4>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<p>
|
||||||
|
There's currently <strong>{{num_accounts_connected}}</strong> connected out of a total of <strong>{{num_accounts_registered}}</strong>
|
||||||
|
account{{num_accounts_registered|pluralize}} registered.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Of these, <strong>{{num_accounts_registered_recent}}</strong> were created this week, and <strong>{{num_accounts_connected_recent}}</strong>
|
||||||
|
have connected within the last seven days.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div class="card">
|
||||||
|
<h4 class="card-header text-center">Database Stats</h4>
|
||||||
|
|
||||||
|
<div class="card-body py-0 px-0">
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item">{{num_accounts_registered}} account{{num_accounts_registered|pluralize}} (+
|
||||||
|
{{num_characters}} character{{num_characters|pluralize}})
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">{{num_rooms}} room{{num_rooms|pluralize}} (+ {{num_exits}} exits)</li>
|
||||||
|
<li class="list-group-item">{{num_others}} other objects</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
12
evennia/web/templates/website/homepage/evennia-widget.html
Normal file
12
evennia/web/templates/website/homepage/evennia-widget.html
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<div class="card text-center">
|
||||||
|
<h4 class="card-header text-center">Evennia</h4>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<p><a href="https://evennia.com">Evennia</a> is an open-source MUD/MU*-creation framework built in
|
||||||
|
<a href="http://python.org">Python</a>, using
|
||||||
|
<a href="http://twistedmatrix.com">Twisted</a> and
|
||||||
|
<a href="http://djangoproject.com">Django</a>.<br>
|
||||||
|
Create the text-based multiplayer-game of your dreams - as
|
||||||
|
simple or as complex as you like.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
62
evennia/web/templates/website/homepage/main-content.html
Normal file
62
evennia/web/templates/website/homepage/main-content.html
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
<h1 class="card-title">Welcome to Evennia!</h1>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<p class="lead">The Python MUD/MU* creation system.</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You are looking at the start of your game's website, generated out of
|
||||||
|
the box by Evennia.<br>It can be expanded into a full-fledged home for your game.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% if webclient_enabled %}
|
||||||
|
|
||||||
|
<p><a href="{% url 'webclient:index' %}" class="playbutton">Play in the browser!</a></p>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if telnet_enabled %}
|
||||||
|
<p>
|
||||||
|
Telnet: <strong>{{ server_hostname }}</strong>, port
|
||||||
|
{% for port in telnet_ports %}
|
||||||
|
{% if not forloop.first and forloop.last %} or
|
||||||
|
{% elif forloop.counter != 1 %},
|
||||||
|
{% endif %}
|
||||||
|
<strong>{{ port }}</strong>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if telnet_ssl_enabled %}
|
||||||
|
<p>
|
||||||
|
Telnet (SSL): <strong>{{ server_hostname }}</strong>, port
|
||||||
|
{% for port in telnet_ssl_ports %}
|
||||||
|
{% if not forloop.first and forloop.last %} or
|
||||||
|
{% elif forloop.counter != 1 %},
|
||||||
|
{% endif %}
|
||||||
|
<strong>{{ port }}</strong>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if ssh_enabled %}
|
||||||
|
<p>
|
||||||
|
SSH: <strong>{{ server_hostname }}</strong>, port
|
||||||
|
{% for port in ssh_ports %}
|
||||||
|
{% if not forloop.first and forloop.last %} or
|
||||||
|
{% elif forloop.counter != 1 %},
|
||||||
|
{% endif %}
|
||||||
|
<strong>{{ port }}</strong>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
<p>
|
||||||
|
For more info, see the <a href="https://www.evennia.com">Evennia homepage</a> or check
|
||||||
|
out our extensive <a href="https://evennia.com/docs/latest">online documentation</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Don't hesitate asking questions to the Evennia community!<br>Drop a message
|
||||||
|
in the <a href="https://github.com/evennia/evennia/discussions">Evennia forums</a>
|
||||||
|
or come say hi in the <a href="https://discord.gg/AJJpcRUhtF">Discord support channel</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Evennia is Open source and can be found on <a href="https://github.com/evennia/evennia">GitHub</a>.
|
||||||
|
If you find bugs, please report them to the <a href="https://github.com/evennia/evennia/issues">Issue tracker</a>.
|
||||||
|
</p>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<div class="card">
|
||||||
|
<h4 class="card-header text-center">Recently Connected</h4>
|
||||||
|
|
||||||
|
<div class="card-body px-0 py-0">
|
||||||
|
<ul class="list-group">
|
||||||
|
{% for account in accounts_connected_recent %}
|
||||||
|
<li class="list-group-item">{{account.username}}—<em>{{account.last_login|timesince}} ago</em></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -11,134 +11,40 @@
|
||||||
<div class="card text-center">
|
<div class="card text-center">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<h1 class="card-title">Welcome to Evennia!</h1>
|
{% include "website/homepage/main-content.html" %}
|
||||||
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<p class="lead">
|
|
||||||
The Python MUD/MU* creation system.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
You are looking at the start of your game's website, generated out of
|
|
||||||
the box by Evennia.<br>It can be expanded into a full-fledged home for your game.
|
|
||||||
</p>
|
|
||||||
{% if webclient_enabled %}
|
|
||||||
<p>
|
|
||||||
<a href="{% url 'webclient:index' %}" class="playbutton">Play in the browser!</a>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% if telnet_enabled %}
|
|
||||||
<p>
|
|
||||||
Telnet: <strong>{{ server_hostname }}</strong>, port
|
|
||||||
{% for port in telnet_ports %}
|
|
||||||
{% if not forloop.first and forloop.last %} or
|
|
||||||
{% elif forloop.counter != 1 %},
|
|
||||||
{% endif %}
|
|
||||||
<strong>{{ port }}</strong>
|
|
||||||
{% endfor %}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% if telnet_ssl_enabled %}
|
|
||||||
<p>
|
|
||||||
Telnet (SSL): <strong>{{ server_hostname }}</strong>, port
|
|
||||||
{% for port in telnet_ssl_ports %}
|
|
||||||
{% if not forloop.first and forloop.last %} or
|
|
||||||
{% elif forloop.counter != 1 %},
|
|
||||||
{% endif %}
|
|
||||||
<strong>{{ port }}</strong>
|
|
||||||
{% endfor %}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% if ssh_enabled %}
|
|
||||||
<p>
|
|
||||||
SSH: <strong>{{ server_hostname }}</strong>, port
|
|
||||||
{% for port in ssh_ports %}
|
|
||||||
{% if not forloop.first and forloop.last %} or
|
|
||||||
{% elif forloop.counter != 1 %},
|
|
||||||
{% endif %}
|
|
||||||
<strong>{{ port }}</strong>
|
|
||||||
{% endfor %}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
<p>
|
|
||||||
For more info, see the <a href="https://www.evennia.com">Evennia homepage</a> or check
|
|
||||||
out our extensive <a href="https://evennia.com/docs/latest">online documentation</a>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Don't hesitate asking questions to the Evennia community!<br>Drop a message
|
|
||||||
in the <a href="https://github.com/evennia/evennia/discussions">Evennia forums</a>
|
|
||||||
or come say hi in the <a href="https://discord.gg/AJJpcRUhtF">Discord support channel</a>.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Evennia is Open source and can be found on <a href="https://github.com/evennia/evennia">GitHub</a>.
|
|
||||||
If you find bugs, please report them to the <a href="https://github.com/evennia/evennia/issues">Issue tracker</a>.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr/>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-4 mb-3">
|
<div class="col-12 col-md-4 mb-3">
|
||||||
<div class="card">
|
|
||||||
<h4 class="card-header text-center">Accounts</h4>
|
|
||||||
|
|
||||||
<div class="card-body">
|
{% include "website/homepage/accounts-widget.html" %}
|
||||||
<p>
|
|
||||||
There's currently <strong>{{num_accounts_connected}}</strong> connected out of a total of <strong>{{num_accounts_registered}}</strong> account{{num_accounts_registered|pluralize}} registered.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Of these, <strong>{{num_accounts_registered_recent}}</strong> were created this week, and <strong>{{num_accounts_connected_recent}}</strong> have connected within the last seven days.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-md-4 mb-3">
|
<div class="col-12 col-md-4 mb-3">
|
||||||
<div class="card">
|
|
||||||
<h4 class="card-header text-center">Recently Connected</h4>
|
|
||||||
|
|
||||||
<div class="card-body px-0 py-0">
|
{% include "website/homepage/recently-connected-widget.html" %}
|
||||||
<ul class="list-group">
|
|
||||||
{% for account in accounts_connected_recent %}
|
|
||||||
<li class="list-group-item">{{account.username}}—<em>{{account.last_login|timesince}} ago</em></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-md-4 mb-3">
|
<div class="col-12 col-md-4 mb-3">
|
||||||
<div class="card">
|
|
||||||
<h4 class="card-header text-center">Database Stats</h4>
|
|
||||||
|
|
||||||
<div class="card-body py-0 px-0">
|
{% include "website/homepage/database-stats-widget.html" %}
|
||||||
<ul class="list-group">
|
|
||||||
<li class="list-group-item">{{num_accounts_registered}} account{{num_accounts_registered|pluralize}} (+ {{num_characters}} character{{num_characters|pluralize}})</li>
|
|
||||||
<li class="list-group-item">{{num_rooms}} room{{num_rooms|pluralize}} (+ {{num_exits}} exits)</li>
|
|
||||||
<li class="list-group-item">{{num_others}} other objects</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card text-center">
|
|
||||||
<h4 class="card-header text-center">Evennia</h4>
|
|
||||||
|
|
||||||
<div class="card-body">
|
{% include "website/homepage/evennia-widget.html" %}
|
||||||
<p><a href="https://evennia.com">Evennia</a> is an open-source MUD/MU*-creation framework built in
|
|
||||||
<a href="http://python.org">Python</a>, using
|
|
||||||
<a href="http://twistedmatrix.com">Twisted</a> and
|
|
||||||
<a href="http://djangoproject.com">Django</a>.<br>
|
|
||||||
Create the text-based multiplayer-game of your dreams - as
|
|
||||||
simple or as complex as you like.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue