Upgrade evennia dependencies and RUN MIGRATIONS. Update requirement to Django 5.3, which has some backwards-incompatible index changes
This commit is contained in:
parent
4269745d3e
commit
0f28eb1ac3
12 changed files with 701 additions and 588 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
## Main branch
|
## Main branch
|
||||||
|
|
||||||
Updated dependencies: Twisted >24 (<25). Python 3.10, 3.11, 3.12, 3.13. Will
|
Updated dependencies: Django >5.1 (<5,2), Twisted >24 (<25).
|
||||||
drop 3.10 support as part of next major release.
|
Python versions: 3.11, 3.12, 3.13.
|
||||||
|
|
||||||
|
- Feat (backwards incompatible): RUN MIGRATIONS (`evennia migrate`): Now requiring Django 5.1 (Griatch)
|
||||||
|
- Feat (backwards incompatible): Drop support and testing for Python 3.10 (Griatch)
|
||||||
- [Feat][pull3719]: Support Python 3.13. (0xDEADFED5)
|
- [Feat][pull3719]: Support Python 3.13. (0xDEADFED5)
|
||||||
- [Feat][pull3633]: Default object's default descs are now taken from a `default_description`
|
- [Feat][pull3633]: Default object's default descs are now taken from a `default_description`
|
||||||
class variable instead of the `desc` Attribute always being set (count-infinity)
|
class variable instead of the `desc` Attribute always being set (count-infinity)
|
||||||
|
|
@ -13,8 +15,7 @@ drop 3.10 support as part of next major release.
|
||||||
strings instead of `None` if no name is provided, also enforce string type (InspectorCaracal)
|
strings instead of `None` if no name is provided, also enforce string type (InspectorCaracal)
|
||||||
- [Fix][pull3682]: Allow in-game help searching for commands natively starting
|
- [Fix][pull3682]: Allow in-game help searching for commands natively starting
|
||||||
with `*` (which is the Lunr search wildcard) (count-infinity)
|
with `*` (which is the Lunr search wildcard) (count-infinity)
|
||||||
- [Fix][pull3684]: Web client stopped auto-focusing the input box after opening
|
- [Fix][pull3684]: Web client stopped auto-focusing the input box after opening settings (count-infinity)
|
||||||
settings (count-infinity)
|
|
||||||
- [Fix][pull3689]: Partial matching fix in default search, makes sure e.g. `b sw` uniquely
|
- [Fix][pull3689]: Partial matching fix in default search, makes sure e.g. `b sw` uniquely
|
||||||
finds `big sword` even if another type of sword is around (InspectorCaracal)
|
finds `big sword` even if another type of sword is around (InspectorCaracal)
|
||||||
- [Fix][pull3690]: In searches, allow special 'here' and 'me' keywords only be valid queries
|
- [Fix][pull3690]: In searches, allow special 'here' and 'me' keywords only be valid queries
|
||||||
|
|
@ -37,7 +38,6 @@ drop 3.10 support as part of next major release.
|
||||||
used as the task's category (Griatch)
|
used as the task's category (Griatch)
|
||||||
- [Docs]: Fixes from InspectorCaracal, Griatch, ChrisLR
|
- [Docs]: Fixes from InspectorCaracal, Griatch, ChrisLR
|
||||||
|
|
||||||
|
|
||||||
[pull3633]: https://github.com/evennia/evennia/pull/3633
|
[pull3633]: https://github.com/evennia/evennia/pull/3633
|
||||||
[pull3677]: https://github.com/evennia/evennia/pull/3677
|
[pull3677]: https://github.com/evennia/evennia/pull/3677
|
||||||
[pull3682]: https://github.com/evennia/evennia/pull/3682
|
[pull3682]: https://github.com/evennia/evennia/pull/3682
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
# when people upgrade outside regular channels). This file only supports lines of
|
# when people upgrade outside regular channels). This file only supports lines of
|
||||||
# `value = number` and only specific names supported by the handler.
|
# `value = number` and only specific names supported by the handler.
|
||||||
|
|
||||||
PYTHON_MIN = 3.10
|
PYTHON_MIN = 3.11
|
||||||
PYTHON_MAX_TESTED = 3.13.100
|
PYTHON_MAX_TESTED = 3.13.100
|
||||||
TWISTED_MIN = 24.11
|
TWISTED_MIN = 24.11
|
||||||
DJANGO_MIN = 4.0.2
|
DJANGO_MIN = 4.0.2
|
||||||
DJANGO_MAX_TESTED = 4.2.100
|
DJANGO_MAX_TESTED = 5.1.100
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
from django.test import TestCase, override_settings
|
from django.test import TestCase, override_settings
|
||||||
from django.utils.timezone import is_aware, utc
|
from django.utils.timezone import is_aware
|
||||||
|
|
||||||
_SKIP = False
|
_SKIP = False
|
||||||
try:
|
try:
|
||||||
|
|
@ -533,7 +533,7 @@ class S3Boto3StorageTests(S3Boto3TestCase):
|
||||||
|
|
||||||
def _test_storage_mtime(self, use_tz):
|
def _test_storage_mtime(self, use_tz):
|
||||||
obj = self.storage.bucket.Object.return_value
|
obj = self.storage.bucket.Object.return_value
|
||||||
obj.last_modified = datetime.datetime.now(utc)
|
obj.last_modified = datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
|
||||||
name = "file.txt"
|
name = "file.txt"
|
||||||
self.assertFalse(
|
self.assertFalse(
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@ This directory contains Evennia's log files. The existence of this README.md fil
|
||||||
to correctly include the log directory in git (since log files are ignored by git and you can't
|
to correctly include the log directory in git (since log files are ignored by git and you can't
|
||||||
commit an empty directory).
|
commit an empty directory).
|
||||||
|
|
||||||
- `server.log` - log file from the game Server.
|
`server.log` - log file from the game Server.
|
||||||
- `portal.log` - log file from Portal proxy (internet facing)
|
`portal.log` - log file from Portal proxy (internet facing)
|
||||||
|
|
||||||
Usually these logs are viewed together with `evennia -l`. They are also rotated every week so as not
|
Usually these logs are viewed together with `evennia -l`. They are also rotated every week so as not
|
||||||
to be too big. Older log names will have a name appended by `_month_date`.
|
to be too big. Older log names will have a name appended by `_month_date`.
|
||||||
|
|
||||||
- `lockwarnings.log` - warnings from the lock system.
|
`lockwarnings.log` - warnings from the lock system.
|
||||||
- `http_requests.log` - this will generally be empty unless turning on debugging inside the server.
|
`http_requests.log` - this will generally be empty unless turning on debugging inside the server.
|
||||||
|
|
||||||
- `channel_<channelname>.log` - these are channel logs for the in-game channels They are also used
|
`channel_<channelname>.log` - these are channel logs for the in-game channels They are also used
|
||||||
by the `/history` flag in-game to get the latest message history.
|
by the `/history` flag in-game to get the latest message history.
|
||||||
|
|
|
||||||
|
|
@ -1484,69 +1484,78 @@ def create_superuser():
|
||||||
|
|
||||||
def check_database(always_return=False):
|
def check_database(always_return=False):
|
||||||
"""
|
"""
|
||||||
Check so the database exists.
|
Check if the database exists and has basic tables. This is only run by the launcher.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
always_return (bool, optional): If set, will always return True/False
|
always_return (bool, optional): If True, will not raise exceptions on errors.
|
||||||
also on critical errors. No output will be printed.
|
|
||||||
Returns:
|
Returns:
|
||||||
exists (bool): `True` if the database exists, otherwise `False`.
|
exists (bool): `True` if database exists and seems set up, `False` otherwise.
|
||||||
|
If `always_return` is `False`, this will raise exceptions instead of
|
||||||
|
returning `False`.
|
||||||
"""
|
"""
|
||||||
# Check so a database exists and is accessible
|
# Check if database exists
|
||||||
|
from django.conf import settings
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
tables = connection.introspection.get_table_list(connection.cursor())
|
tables_to_check = [
|
||||||
if not tables or not isinstance(tables[0], str): # django 1.8+
|
"accounts_accountdb", # base account table
|
||||||
tables = [tableinfo.name for tableinfo in tables]
|
"objects_objectdb", # base object table
|
||||||
if tables and "accounts_accountdb" in tables:
|
"scripts_scriptdb", # base script table
|
||||||
# database exists and seems set up. Initialize evennia.
|
"typeclasses_tag", # base tag table
|
||||||
evennia._init()
|
]
|
||||||
# Try to get Account#1
|
|
||||||
from evennia.accounts.models import AccountDB
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
AccountDB.objects.get(id=1)
|
with connection.cursor() as cursor:
|
||||||
except (django.db.utils.OperationalError, ProgrammingError) as e:
|
# Get all table names in the database
|
||||||
if always_return:
|
if connection.vendor == "postgresql":
|
||||||
return False
|
cursor.execute(
|
||||||
print(ERROR_DATABASE.format(traceback=e))
|
"""
|
||||||
sys.exit()
|
SELECT tablename FROM pg_tables
|
||||||
except AccountDB.DoesNotExist:
|
WHERE schemaname = 'public'
|
||||||
# no superuser yet. We need to create it.
|
"""
|
||||||
|
)
|
||||||
|
elif connection.vendor == "mysql":
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT table_name FROM information_schema.tables
|
||||||
|
WHERE table_schema = %s
|
||||||
|
""",
|
||||||
|
[settings.DATABASES["default"]["NAME"]],
|
||||||
|
)
|
||||||
|
elif connection.vendor == "sqlite":
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
SELECT name FROM sqlite_master
|
||||||
|
WHERE type='table' AND name NOT LIKE 'sqlite_%'
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if not always_return:
|
||||||
|
raise Exception(f"Unsupported database vendor: {connection.vendor}")
|
||||||
|
return False
|
||||||
|
|
||||||
other_superuser = AccountDB.objects.filter(is_superuser=True)
|
existing_tables = {row[0].lower() for row in cursor.fetchall()}
|
||||||
if other_superuser:
|
|
||||||
# Another superuser was found, but not with id=1. This may
|
|
||||||
# happen if using flush (the auto-id starts at a higher
|
|
||||||
# value). Wwe copy this superuser into id=1. To do
|
|
||||||
# this we must deepcopy it, delete it then save the copy
|
|
||||||
# with the new id. This allows us to avoid the UNIQUE
|
|
||||||
# constraint on usernames.
|
|
||||||
other = other_superuser[0]
|
|
||||||
other_id = other.id
|
|
||||||
other_key = other.username
|
|
||||||
print(WARNING_MOVING_SUPERUSER.format(other_key=other_key, other_id=other_id))
|
|
||||||
res = ""
|
|
||||||
while res.upper() != "Y":
|
|
||||||
# ask for permission
|
|
||||||
res = eval(input("Continue [Y]/N: "))
|
|
||||||
if res.upper() == "N":
|
|
||||||
sys.exit()
|
|
||||||
elif not res:
|
|
||||||
break
|
|
||||||
# continue with the
|
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
new = deepcopy(other)
|
# Check if essential tables exist
|
||||||
other.delete()
|
missing_tables = [table for table in tables_to_check if table not in existing_tables]
|
||||||
new.id = 1
|
|
||||||
new.save()
|
if missing_tables:
|
||||||
else:
|
if always_return:
|
||||||
create_superuser()
|
return False
|
||||||
check_database(always_return=always_return)
|
raise Exception(
|
||||||
return True
|
f"Database tables missing: {', '.join(missing_tables)}. "
|
||||||
|
"Did you remember to run migrations?"
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as exc:
|
||||||
|
if not always_return:
|
||||||
|
raise
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def getenv():
|
def getenv():
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Generated by Django 5.1.6 on 2025-03-01 21:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("typeclasses", "0016_alter_attribute_id_alter_tag_id"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
# First create the index with the old name that Django expects
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="tag",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["db_key", "db_category", "db_tagtype", "db_model"],
|
||||||
|
name="typeclasses_tag_db_key_db_category_db_tagtype_db_model_idx",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
# Then rename it to the new name
|
||||||
|
migrations.RenameIndex(
|
||||||
|
model_name="tag",
|
||||||
|
new_name="typeclasses_db_key_be0c81_idx",
|
||||||
|
old_fields=("db_key", "db_category", "db_tagtype", "db_model"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -14,7 +14,6 @@ from collections import defaultdict
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from evennia.locks.lockfuncs import perm as perm_lockfunc
|
from evennia.locks.lockfuncs import perm as perm_lockfunc
|
||||||
from evennia.utils.utils import make_iter, to_str
|
from evennia.utils.utils import make_iter, to_str
|
||||||
|
|
||||||
|
|
@ -79,9 +78,10 @@ class Tag(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
|
|
||||||
verbose_name = "Tag"
|
verbose_name = "Tag"
|
||||||
unique_together = (("db_key", "db_category", "db_tagtype", "db_model"),)
|
unique_together = (("db_key", "db_category", "db_tagtype", "db_model"),)
|
||||||
index_together = (("db_key", "db_category", "db_tagtype", "db_model"),)
|
indexes = [models.Index(fields=["db_key", "db_category", "db_tagtype", "db_model"])]
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
return str(self) < str(other)
|
return str(self) < str(other)
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ objects already existing in the database.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.utils.functional import SimpleLazyObject
|
||||||
from django.db.utils import OperationalError, ProgrammingError
|
|
||||||
|
|
||||||
# limit symbol import from API
|
# limit symbol import from API
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
|
@ -29,232 +28,254 @@ __all__ = (
|
||||||
|
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
|
|
||||||
# import objects this way to avoid circular import problems
|
|
||||||
try:
|
|
||||||
ObjectDB = ContentType.objects.get(app_label="objects", model="objectdb").model_class()
|
|
||||||
ScriptDB = ContentType.objects.get(app_label="scripts", model="scriptdb").model_class()
|
|
||||||
AccountDB = ContentType.objects.get(app_label="accounts", model="accountdb").model_class()
|
|
||||||
Msg = ContentType.objects.get(app_label="comms", model="msg").model_class()
|
|
||||||
ChannelDB = ContentType.objects.get(app_label="comms", model="channeldb").model_class()
|
|
||||||
HelpEntry = ContentType.objects.get(app_label="help", model="helpentry").model_class()
|
|
||||||
Tag = ContentType.objects.get(app_label="typeclasses", model="tag").model_class()
|
|
||||||
except (OperationalError, ProgrammingError):
|
|
||||||
# this is a fallback used during tests/doc building
|
|
||||||
print("Database not available yet - using temporary fallback for create managers.")
|
|
||||||
from evennia.accounts.models import AccountDB
|
|
||||||
from evennia.comms.models import ChannelDB, Msg
|
|
||||||
from evennia.help.models import HelpEntry
|
|
||||||
from evennia.objects.models import ObjectDB
|
|
||||||
from evennia.scripts.models import ScriptDB
|
|
||||||
from evennia.typeclasses.tags import Tag # noqa
|
|
||||||
|
|
||||||
#
|
# Lazy-loaded model classes
|
||||||
# Game Object creation
|
def _get_objectdb():
|
||||||
#
|
from django.contrib.contenttypes.models import ContentType
|
||||||
# Create a new in-game object.
|
|
||||||
#
|
|
||||||
# Keyword Args:
|
|
||||||
# typeclass (class or str): Class or python path to a typeclass.
|
|
||||||
# key (str): Name of the new object. If not set, a name of
|
|
||||||
# `#dbref` will be set.
|
|
||||||
# location (Object or str): Obj or #dbref to use as the location of the new object.
|
|
||||||
# home (Object or str): Obj or #dbref to use as the object's home location.
|
|
||||||
# permissions (list): A list of permission strings or tuples (permstring, category).
|
|
||||||
# locks (str): one or more lockstrings, separated by semicolons.
|
|
||||||
# aliases (list): A list of alternative keys or tuples (aliasstring, category).
|
|
||||||
# tags (list): List of tag keys or tuples (tagkey, category) or (tagkey, category, data).
|
|
||||||
# destination (Object or str): Obj or #dbref to use as an Exit's target.
|
|
||||||
# report_to (Object): The object to return error messages to.
|
|
||||||
# nohome (bool): This allows the creation of objects without a
|
|
||||||
# default home location; only used when creating the default
|
|
||||||
# location itself or during unittests.
|
|
||||||
# attributes (list): Tuples on the form (key, value) or (key, value, category),
|
|
||||||
# (key, value, lockstring) or (key, value, lockstring, default_access).
|
|
||||||
# to set as Attributes on the new object.
|
|
||||||
# nattributes (list): Non-persistent tuples on the form (key, value). Note that
|
|
||||||
# adding this rarely makes sense since this data will not survive a reload.
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# object (Object): A newly created object of the given typeclass.
|
|
||||||
#
|
|
||||||
# Raises:
|
|
||||||
# ObjectDB.DoesNotExist: If trying to create an Object with
|
|
||||||
# `location` or `home` that can't be found.
|
|
||||||
#
|
|
||||||
|
|
||||||
create_object = ObjectDB.objects.create_object
|
return ContentType.objects.get(app_label="objects", model="objectdb").model_class()
|
||||||
# alias for create_object
|
|
||||||
|
|
||||||
|
def _get_scriptdb():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="scripts", model="scriptdb").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_accountdb():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="accounts", model="accountdb").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_msg():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="comms", model="msg").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_channeldb():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="comms", model="channeldb").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_helpentry():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="help", model="helpentry").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tag():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="typeclasses", model="tag").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
# Lazy model instances
|
||||||
|
ObjectDB = SimpleLazyObject(_get_objectdb)
|
||||||
|
ScriptDB = SimpleLazyObject(_get_scriptdb)
|
||||||
|
AccountDB = SimpleLazyObject(_get_accountdb)
|
||||||
|
Msg = SimpleLazyObject(_get_msg)
|
||||||
|
ChannelDB = SimpleLazyObject(_get_channeldb)
|
||||||
|
HelpEntry = SimpleLazyObject(_get_helpentry)
|
||||||
|
Tag = SimpleLazyObject(_get_tag)
|
||||||
|
|
||||||
|
|
||||||
|
def create_object(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a new in-game object.
|
||||||
|
|
||||||
|
Keyword Args:
|
||||||
|
typeclass (class or str): Class or python path to a typeclass.
|
||||||
|
key (str): Name of the new object. If not set, a name of
|
||||||
|
`#dbref` will be set.
|
||||||
|
location (Object or str): Obj or #dbref to use as the location of the new object.
|
||||||
|
home (Object or str): Obj or #dbref to use as the object's home location.
|
||||||
|
permissions (list): A list of permission strings or tuples (permstring, category).
|
||||||
|
locks (str): one or more lockstrings, separated by semicolons.
|
||||||
|
aliases (list): A list of alternative keys or tuples (aliasstring, category).
|
||||||
|
tags (list): List of tag keys or tuples (tagkey, category) or (tagkey, category, data).
|
||||||
|
destination (Object or str): Obj or #dbref to use as an Exit's target.
|
||||||
|
report_to (Object): The object to return error messages to.
|
||||||
|
nohome (bool): This allows the creation of objects without a
|
||||||
|
default home location; only used when creating the default
|
||||||
|
location itself or during unittests.
|
||||||
|
attributes (list): Tuples on the form (key, value) or (key, value, category),
|
||||||
|
(key, value, lockstring) or (key, value, lockstring, default_access).
|
||||||
|
to set as Attributes on the new object.
|
||||||
|
nattributes (list): Non-persistent tuples on the form (key, value). Note that
|
||||||
|
adding this rarely makes sense since this data will not survive a reload.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
object (Object): A newly created object of the given typeclass.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ObjectDB.DoesNotExist: If trying to create an Object with
|
||||||
|
`location` or `home` that can't be found.
|
||||||
|
"""
|
||||||
|
return ObjectDB.objects.create_object(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_script(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a new script. All scripts are a combination of a database
|
||||||
|
object that communicates with the database, and an typeclass that
|
||||||
|
'decorates' the database object into being different types of
|
||||||
|
scripts. It's behaviour is similar to the game objects except
|
||||||
|
scripts has a time component and are more limited in scope.
|
||||||
|
|
||||||
|
Keyword Args:
|
||||||
|
typeclass (class or str): Class or python path to a typeclass.
|
||||||
|
key (str): Name of the new object. If not set, a name of
|
||||||
|
#dbref will be set.
|
||||||
|
obj (Object): The entity on which this Script sits. If this
|
||||||
|
is `None`, we are creating a "global" script.
|
||||||
|
account (Account): The account on which this Script sits. It is
|
||||||
|
exclusiv to `obj`.
|
||||||
|
locks (str): one or more lockstrings, separated by semicolons.
|
||||||
|
interval (int): The triggering interval for this Script, in
|
||||||
|
seconds. If unset, the Script will not have a timing
|
||||||
|
component.
|
||||||
|
start_delay (bool): If `True`, will wait `interval` seconds
|
||||||
|
before triggering the first time.
|
||||||
|
repeats (int): The number of times to trigger before stopping.
|
||||||
|
If unset, will repeat indefinitely.
|
||||||
|
persistent (bool): If this Script survives a server shutdown
|
||||||
|
or not (all Scripts will survive a reload).
|
||||||
|
autostart (bool): If this Script will start immediately when
|
||||||
|
created or if the `start` method must be called explicitly.
|
||||||
|
report_to (Object): The object to return error messages to.
|
||||||
|
desc (str): Optional description of script
|
||||||
|
tags (list): List of tags or tuples (tag, category).
|
||||||
|
attributes (list): List if tuples (key, value) or (key, value, category)
|
||||||
|
(key, value, lockstring) or (key, value, lockstring, default_access).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
script (obj): An instance of the script created
|
||||||
|
|
||||||
|
See evennia.scripts.manager for methods to manipulate existing
|
||||||
|
scripts in the database.
|
||||||
|
"""
|
||||||
|
return ScriptDB.objects.create_script(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_help_entry(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a static help entry in the help database. Note that Command
|
||||||
|
help entries are dynamic and directly taken from the __doc__
|
||||||
|
entries of the command. The database-stored help entries are
|
||||||
|
intended for more general help on the game, more extensive info,
|
||||||
|
in-game setting information and so on.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str): The name of the help entry.
|
||||||
|
entrytext (str): The body of te help entry
|
||||||
|
category (str, optional): The help category of the entry.
|
||||||
|
locks (str, optional): A lockstring to restrict access.
|
||||||
|
aliases (list of str, optional): List of alternative (likely shorter) keynames.
|
||||||
|
tags (lst, optional): List of tags or tuples `(tag, category)`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
help (HelpEntry): A newly created help entry.
|
||||||
|
"""
|
||||||
|
return HelpEntry.objects.create_help(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_message(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a new communication Msg. Msgs represent a unit of
|
||||||
|
database-persistent communication between entites.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
senderobj (Object, Account, Script, str or list): The entity (or
|
||||||
|
entities) sending the Msg. If a `str`, this is the id-string
|
||||||
|
for an external sender type.
|
||||||
|
message (str): Text with the message. Eventual headers, titles
|
||||||
|
etc should all be included in this text string. Formatting
|
||||||
|
will be retained.
|
||||||
|
receivers (Object, Account, Script, str or list): An Account/Object to send
|
||||||
|
to, or a list of them. If a string, it's an identifier for an external
|
||||||
|
receiver.
|
||||||
|
locks (str): Lock definition string.
|
||||||
|
tags (list): A list of tags or tuples `(tag, category)`.
|
||||||
|
header (str): Mime-type or other optional information for the message
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The Comm system is created to be very open-ended, so it's fully
|
||||||
|
possible to let a message both go several receivers at the same time,
|
||||||
|
it's up to the command definitions to limit this as desired.
|
||||||
|
"""
|
||||||
|
return Msg.objects.create_message(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_channel(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Create A communication Channel. A Channel serves as a central hub
|
||||||
|
for distributing Msgs to groups of people without specifying the
|
||||||
|
receivers explicitly. Instead accounts may 'connect' to the channel
|
||||||
|
and follow the flow of messages. By default the channel allows
|
||||||
|
access to all old messages, but this can be turned off with the
|
||||||
|
keep_log switch.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str): This must be unique.
|
||||||
|
|
||||||
|
Keyword Args:
|
||||||
|
aliases (list of str): List of alternative (likely shorter) keynames.
|
||||||
|
desc (str): A description of the channel, for use in listings.
|
||||||
|
locks (str): Lockstring.
|
||||||
|
keep_log (bool): Log channel throughput.
|
||||||
|
typeclass (str or class): The typeclass of the Channel (not
|
||||||
|
often used).
|
||||||
|
tags (list): A list of tags or tuples `(tag[,category[,data]])`.
|
||||||
|
attrs (list): List of attributes on form `(name, value[,category[,lockstring]])`
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
channel (Channel): A newly created channel.
|
||||||
|
"""
|
||||||
|
return ChannelDB.objects.create_channel(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_account(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
This creates a new account.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str): The account's name. This should be unique.
|
||||||
|
email (str or None): Email on valid addr@addr.domain form. If
|
||||||
|
the empty string, will be set to None.
|
||||||
|
password (str): Password in cleartext.
|
||||||
|
|
||||||
|
Keyword Args:
|
||||||
|
typeclass (str): The typeclass to use for the account.
|
||||||
|
is_superuser (bool): Whether or not this account is to be a superuser
|
||||||
|
locks (str): Lockstring.
|
||||||
|
permission (list): List of permission strings.
|
||||||
|
tags (list): List of Tags on form `(key, category[, data])`
|
||||||
|
attributes (list): List of Attributes on form
|
||||||
|
`(key, value [, category, [,lockstring [, default_pass]]])`
|
||||||
|
report_to (Object): An object with a msg() method to report
|
||||||
|
errors to. If not given, errors will be logged.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Account: The newly created Account.
|
||||||
|
Raises:
|
||||||
|
ValueError: If `key` already exists in database.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
Usually only the server admin should need to be superuser, all
|
||||||
|
other access levels can be handled with more fine-grained
|
||||||
|
permissions or groups. A superuser bypasses all lock checking
|
||||||
|
operations and is thus not suitable for play-testing the game.
|
||||||
|
"""
|
||||||
|
return AccountDB.objects.create_account(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# Aliases for the creation functions
|
||||||
object = create_object
|
object = create_object
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Script creation
|
|
||||||
|
|
||||||
# Create a new script. All scripts are a combination of a database
|
|
||||||
# object that communicates with the database, and an typeclass that
|
|
||||||
# 'decorates' the database object into being different types of
|
|
||||||
# scripts. It's behaviour is similar to the game objects except
|
|
||||||
# scripts has a time component and are more limited in scope.
|
|
||||||
#
|
|
||||||
# Keyword Args:
|
|
||||||
# typeclass (class or str): Class or python path to a typeclass.
|
|
||||||
# key (str): Name of the new object. If not set, a name of
|
|
||||||
# #dbref will be set.
|
|
||||||
# obj (Object): The entity on which this Script sits. If this
|
|
||||||
# is `None`, we are creating a "global" script.
|
|
||||||
# account (Account): The account on which this Script sits. It is
|
|
||||||
# exclusiv to `obj`.
|
|
||||||
# locks (str): one or more lockstrings, separated by semicolons.
|
|
||||||
# interval (int): The triggering interval for this Script, in
|
|
||||||
# seconds. If unset, the Script will not have a timing
|
|
||||||
# component.
|
|
||||||
# start_delay (bool): If `True`, will wait `interval` seconds
|
|
||||||
# before triggering the first time.
|
|
||||||
# repeats (int): The number of times to trigger before stopping.
|
|
||||||
# If unset, will repeat indefinitely.
|
|
||||||
# persistent (bool): If this Script survives a server shutdown
|
|
||||||
# or not (all Scripts will survive a reload).
|
|
||||||
# autostart (bool): If this Script will start immediately when
|
|
||||||
# created or if the `start` method must be called explicitly.
|
|
||||||
# report_to (Object): The object to return error messages to.
|
|
||||||
# desc (str): Optional description of script
|
|
||||||
# tags (list): List of tags or tuples (tag, category).
|
|
||||||
# attributes (list): List if tuples (key, value) or (key, value, category)
|
|
||||||
# (key, value, lockstring) or (key, value, lockstring, default_access).
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# script (obj): An instance of the script created
|
|
||||||
#
|
|
||||||
# See evennia.scripts.manager for methods to manipulate existing
|
|
||||||
# scripts in the database.
|
|
||||||
|
|
||||||
create_script = ScriptDB.objects.create_script
|
|
||||||
# alias
|
|
||||||
script = create_script
|
script = create_script
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Help entry creation
|
|
||||||
#
|
|
||||||
|
|
||||||
# """
|
|
||||||
# Create a static help entry in the help database. Note that Command
|
|
||||||
# help entries are dynamic and directly taken from the __doc__
|
|
||||||
# entries of the command. The database-stored help entries are
|
|
||||||
# intended for more general help on the game, more extensive info,
|
|
||||||
# in-game setting information and so on.
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# key (str): The name of the help entry.
|
|
||||||
# entrytext (str): The body of te help entry
|
|
||||||
# category (str, optional): The help category of the entry.
|
|
||||||
# locks (str, optional): A lockstring to restrict access.
|
|
||||||
# aliases (list of str, optional): List of alternative (likely shorter) keynames.
|
|
||||||
# tags (lst, optional): List of tags or tuples `(tag, category)`.
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# help (HelpEntry): A newly created help entry.
|
|
||||||
#
|
|
||||||
|
|
||||||
create_help_entry = HelpEntry.objects.create_help
|
|
||||||
# alias
|
|
||||||
help_entry = create_help_entry
|
help_entry = create_help_entry
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Comm system methods
|
|
||||||
|
|
||||||
#
|
|
||||||
# Create a new communication Msg. Msgs represent a unit of
|
|
||||||
# database-persistent communication between entites.
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# senderobj (Object, Account, Script, str or list): The entity (or
|
|
||||||
# entities) sending the Msg. If a `str`, this is the id-string
|
|
||||||
# for an external sender type.
|
|
||||||
# message (str): Text with the message. Eventual headers, titles
|
|
||||||
# etc should all be included in this text string. Formatting
|
|
||||||
# will be retained.
|
|
||||||
# receivers (Object, Account, Script, str or list): An Account/Object to send
|
|
||||||
# to, or a list of them. If a string, it's an identifier for an external
|
|
||||||
# receiver.
|
|
||||||
# locks (str): Lock definition string.
|
|
||||||
# tags (list): A list of tags or tuples `(tag, category)`.
|
|
||||||
# header (str): Mime-type or other optional information for the message
|
|
||||||
#
|
|
||||||
# Notes:
|
|
||||||
# The Comm system is created to be very open-ended, so it's fully
|
|
||||||
# possible to let a message both go several receivers at the same time,
|
|
||||||
# it's up to the command definitions to limit this as desired.
|
|
||||||
#
|
|
||||||
|
|
||||||
create_message = Msg.objects.create_message
|
|
||||||
message = create_message
|
message = create_message
|
||||||
create_msg = create_message
|
|
||||||
|
|
||||||
|
|
||||||
# Create A communication Channel. A Channel serves as a central hub
|
|
||||||
# for distributing Msgs to groups of people without specifying the
|
|
||||||
# receivers explicitly. Instead accounts may 'connect' to the channel
|
|
||||||
# and follow the flow of messages. By default the channel allows
|
|
||||||
# access to all old messages, but this can be turned off with the
|
|
||||||
# keep_log switch.
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# key (str): This must be unique.
|
|
||||||
#
|
|
||||||
# Keyword Args:
|
|
||||||
# aliases (list of str): List of alternative (likely shorter) keynames.
|
|
||||||
# desc (str): A description of the channel, for use in listings.
|
|
||||||
# locks (str): Lockstring.
|
|
||||||
# keep_log (bool): Log channel throughput.
|
|
||||||
# typeclass (str or class): The typeclass of the Channel (not
|
|
||||||
# often used).
|
|
||||||
# tags (list): A list of tags or tuples `(tag, category)`.
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# channel (Channel): A newly created channel.
|
|
||||||
#
|
|
||||||
|
|
||||||
create_channel = ChannelDB.objects.create_channel
|
|
||||||
channel = create_channel
|
channel = create_channel
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Account creation methods
|
|
||||||
#
|
|
||||||
|
|
||||||
# This creates a new account.
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# key (str): The account's name. This should be unique.
|
|
||||||
# email (str or None): Email on valid addr@addr.domain form. If
|
|
||||||
# the empty string, will be set to None.
|
|
||||||
# password (str): Password in cleartext.
|
|
||||||
#
|
|
||||||
# Keyword Args:
|
|
||||||
# typeclass (str): The typeclass to use for the account.
|
|
||||||
# is_superuser (bool): Wether or not this account is to be a superuser
|
|
||||||
# locks (str): Lockstring.
|
|
||||||
# permission (list): List of permission strings.
|
|
||||||
# tags (list): List of Tags on form `(key, category[, data])`
|
|
||||||
# attributes (list): List of Attributes on form
|
|
||||||
# `(key, value [, category, [,lockstring [, default_pass]]])`
|
|
||||||
# report_to (Object): An object with a msg() method to report
|
|
||||||
# errors to. If not given, errors will be logged.
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# Account: The newly created Account.
|
|
||||||
# Raises:
|
|
||||||
# ValueError: If `key` already exists in database.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# Notes:
|
|
||||||
# Usually only the server admin should need to be superuser, all
|
|
||||||
# other access levels can be handled with more fine-grained
|
|
||||||
# permissions or groups. A superuser bypasses all lock checking
|
|
||||||
# operations and is thus not suitable for play-testing the game.
|
|
||||||
|
|
||||||
create_account = AccountDB.objects.create_account
|
|
||||||
# alias
|
|
||||||
account = create_account
|
account = create_account
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,9 @@ Example: To reach the search method 'get_object_with_account'
|
||||||
> from evennia.objects.models import ObjectDB
|
> from evennia.objects.models import ObjectDB
|
||||||
> match = Object.objects.get_object_with_account(...)
|
> match = Object.objects.get_object_with_account(...)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Import the manager methods to be wrapped
|
from django.utils.functional import SimpleLazyObject
|
||||||
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.db.utils import OperationalError, ProgrammingError
|
|
||||||
|
|
||||||
# limit symbol import from API
|
# limit symbol import from API
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
|
@ -45,180 +41,265 @@ __all__ = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# import objects this way to avoid circular import problems
|
# Lazy-loaded model classes
|
||||||
try:
|
def _get_objectdb():
|
||||||
ObjectDB = ContentType.objects.get(app_label="objects", model="objectdb").model_class()
|
from django.contrib.contenttypes.models import ContentType
|
||||||
AccountDB = ContentType.objects.get(app_label="accounts", model="accountdb").model_class()
|
|
||||||
ScriptDB = ContentType.objects.get(app_label="scripts", model="scriptdb").model_class()
|
return ContentType.objects.get(app_label="objects", model="objectdb").model_class()
|
||||||
Msg = ContentType.objects.get(app_label="comms", model="msg").model_class()
|
|
||||||
ChannelDB = ContentType.objects.get(app_label="comms", model="channeldb").model_class()
|
|
||||||
HelpEntry = ContentType.objects.get(app_label="help", model="helpentry").model_class()
|
def _get_accountdb():
|
||||||
Tag = ContentType.objects.get(app_label="typeclasses", model="tag").model_class()
|
from django.contrib.contenttypes.models import ContentType
|
||||||
except (OperationalError, ProgrammingError):
|
|
||||||
# this is a fallback used during tests/doc building
|
return ContentType.objects.get(app_label="accounts", model="accountdb").model_class()
|
||||||
print("Database not available yet - using temporary fallback for search managers.")
|
|
||||||
from evennia.accounts.models import AccountDB
|
|
||||||
from evennia.comms.models import ChannelDB, Msg
|
def _get_scriptdb():
|
||||||
from evennia.help.models import HelpEntry
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from evennia.objects.models import ObjectDB
|
|
||||||
from evennia.scripts.models import ScriptDB
|
return ContentType.objects.get(app_label="scripts", model="scriptdb").model_class()
|
||||||
from evennia.typeclasses.tags import Tag # noqa
|
|
||||||
|
|
||||||
|
def _get_msg():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="comms", model="msg").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_channeldb():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="comms", model="channeldb").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_helpentry():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="help", model="helpentry").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_tag():
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
return ContentType.objects.get(app_label="typeclasses", model="tag").model_class()
|
||||||
|
|
||||||
|
|
||||||
|
# Lazy model instances
|
||||||
|
ObjectDB = SimpleLazyObject(_get_objectdb)
|
||||||
|
AccountDB = SimpleLazyObject(_get_accountdb)
|
||||||
|
ScriptDB = SimpleLazyObject(_get_scriptdb)
|
||||||
|
Msg = SimpleLazyObject(_get_msg)
|
||||||
|
ChannelDB = SimpleLazyObject(_get_channeldb)
|
||||||
|
HelpEntry = SimpleLazyObject(_get_helpentry)
|
||||||
|
Tag = SimpleLazyObject(_get_tag)
|
||||||
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
# Search manager-wrappers
|
# Search manager-wrappers
|
||||||
# -------------------------------------------------------------------
|
# -------------------------------------------------------------------
|
||||||
|
|
||||||
#
|
|
||||||
# Search objects as a character
|
def search_object(*args, **kwargs):
|
||||||
#
|
"""
|
||||||
# NOTE: A more powerful wrapper of this method
|
Search for objects in the database.
|
||||||
# is reachable from within each command class
|
|
||||||
# by using self.caller.search()!
|
Args:
|
||||||
#
|
key (str or int): Object key or dbref to search for. This can also
|
||||||
# def object_search(self, ostring=None,
|
be a list of keys/dbrefs. `None` (default) returns all objects.
|
||||||
# attribute_name=None,
|
exact (bool): Only valid for string keys. If True, requires exact
|
||||||
# typeclass=None,
|
key match, otherwise also match key with case-insensitive and
|
||||||
# candidates=None,
|
partial matching. Default is True.
|
||||||
# exact=True):
|
candidates (list): Only search among these object candidates,
|
||||||
#
|
if given. Default is to search all objects.
|
||||||
# Search globally or in a list of candidates and return results.
|
attribute_name (str): If set, search by objects with this attribute_name
|
||||||
# The result is always a list of Objects (or the empty list)
|
defined on them, with the value specified by `attribute_value`.
|
||||||
#
|
attribute_value (any): What value the given attribute_name must have.
|
||||||
# Arguments:
|
location (Object): Filter by objects at this location.
|
||||||
# ostring: (str) The string to compare names against. By default (if
|
typeclass (str or TypeClass): Filter by objects having this typeclass.
|
||||||
# not attribute_name is set), this will search object.key
|
This can also be a list of typeclasses.
|
||||||
# and object.aliases in order. Can also be on the form #dbref,
|
tags (str or list): Filter by objects having one or more Tags.
|
||||||
# which will, if exact=True be matched against primary key.
|
This can be a single tag key, a list of tag keys, or a list of
|
||||||
# attribute_name: (str): Use this named ObjectAttribute to match ostring
|
tuples (tag_key, tag_category).
|
||||||
# against, instead of the defaults.
|
nofetch (bool): Don't fetch typeclass and perms data from db.
|
||||||
# typeclass (str or TypeClass): restrict matches to objects having
|
This is faster but gives less info.
|
||||||
# this typeclass. This will help speed up global searches.
|
|
||||||
# candidates (list obj ObjectDBs): If supplied, search will only be
|
Returns:
|
||||||
# performed among the candidates in this list. A common list
|
matches (list): List of Objects matching the search criteria.
|
||||||
# of candidates is the contents of the current location.
|
"""
|
||||||
# exact (bool): Match names/aliases exactly or partially. Partial
|
return ObjectDB.objects.search_object(*args, **kwargs)
|
||||||
# matching matches the beginning of words in the names/aliases,
|
|
||||||
# using a matching routine to separate multiple matches in
|
|
||||||
# names with multiple components (so "bi sw" will match
|
|
||||||
# "Big sword"). Since this is more expensive than exact
|
|
||||||
# matching, it is recommended to be used together with
|
|
||||||
# the objlist keyword to limit the number of possibilities.
|
|
||||||
# This keyword has no meaning if attribute_name is set.
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# A list of matching objects (or a list with one unique match)
|
|
||||||
# def object_search(self, ostring, caller=None,
|
|
||||||
# candidates=None,
|
|
||||||
# attribute_name=None):
|
|
||||||
#
|
|
||||||
search_object = ObjectDB.objects.search_object
|
|
||||||
search_objects = search_object
|
search_objects = search_object
|
||||||
object_search = search_object
|
object_search = search_object
|
||||||
objects = search_objects
|
objects = search_objects
|
||||||
|
|
||||||
#
|
|
||||||
# Search for accounts
|
|
||||||
#
|
|
||||||
# account_search(self, ostring)
|
|
||||||
|
|
||||||
# Searches for a particular account by name or
|
def search_account(*args, **kwargs):
|
||||||
# database id.
|
"""
|
||||||
#
|
Search for accounts in the database.
|
||||||
# ostring = a string or database id.
|
|
||||||
#
|
Args:
|
||||||
|
key (str or int): Account key or dbref to search for. This can also
|
||||||
|
be a list of keys/dbrefs. `None` (default) returns all accounts.
|
||||||
|
exact (bool): Only valid for string keys. If True, requires exact
|
||||||
|
key match, otherwise also match key with case-insensitive and
|
||||||
|
partial matching. Default is True.
|
||||||
|
candidates (list): Only search among these account candidates,
|
||||||
|
if given. Default is to search all accounts.
|
||||||
|
attribute_name (str): If set, search by accounts with this attribute_name
|
||||||
|
defined on them, with the value specified by `attribute_value`.
|
||||||
|
attribute_value (any): What value the given attribute_name must have.
|
||||||
|
tags (str or list): Filter by accounts having one or more Tags.
|
||||||
|
This can be a single tag key, a list of tag keys, or a list of
|
||||||
|
tuples (tag_key, tag_category).
|
||||||
|
nofetch (bool): Don't fetch typeclass and perms data from db.
|
||||||
|
This is faster but gives less info.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matches (list): List of Accounts matching the search criteria.
|
||||||
|
"""
|
||||||
|
return AccountDB.objects.search_account(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
search_account = AccountDB.objects.search_account
|
|
||||||
search_accounts = search_account
|
search_accounts = search_account
|
||||||
account_search = search_account
|
account_search = search_account
|
||||||
accounts = search_accounts
|
accounts = search_accounts
|
||||||
|
|
||||||
#
|
|
||||||
# Searching for scripts
|
|
||||||
#
|
|
||||||
# script_search(self, ostring, obj=None, only_timed=False)
|
|
||||||
#
|
|
||||||
# Search for a particular script.
|
|
||||||
#
|
|
||||||
# ostring - search criterion - a script ID or key
|
|
||||||
# obj - limit search to scripts defined on this object
|
|
||||||
# only_timed - limit search only to scripts that run
|
|
||||||
# on a timer.
|
|
||||||
#
|
|
||||||
|
|
||||||
search_script = ScriptDB.objects.search_script
|
def search_script(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Search for scripts in the database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str or int): Script key or dbref to search for. This can also
|
||||||
|
be a list of keys/dbrefs. `None` (default) returns all scripts.
|
||||||
|
exact (bool): Only valid for string keys. If True, requires exact
|
||||||
|
key match, otherwise also match key with case-insensitive and
|
||||||
|
partial matching. Default is True.
|
||||||
|
candidates (list): Only search among these script candidates,
|
||||||
|
if given. Default is to search all scripts.
|
||||||
|
attribute_name (str): If set, search by scripts with this attribute_name
|
||||||
|
defined on them, with the value specified by `attribute_value`.
|
||||||
|
attribute_value (any): What value the given attribute_name must have.
|
||||||
|
obj (Object): Filter by scripts defined on this object.
|
||||||
|
account (Account): Filter by scripts defined on this account.
|
||||||
|
typeclass (str or TypeClass): Filter by scripts having this typeclass.
|
||||||
|
This can also be a list of typeclasses.
|
||||||
|
tags (str or list): Filter by scripts having one or more Tags.
|
||||||
|
This can be a single tag key, a list of tag keys, or a list of
|
||||||
|
tuples (tag_key, tag_category).
|
||||||
|
nofetch (bool): Don't fetch typeclass and perms data from db.
|
||||||
|
This is faster but gives less info.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matches (list): List of Scripts matching the search criteria.
|
||||||
|
"""
|
||||||
|
return ScriptDB.objects.search_script(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
search_scripts = search_script
|
search_scripts = search_script
|
||||||
script_search = search_script
|
script_search = search_script
|
||||||
scripts = search_scripts
|
scripts = search_scripts
|
||||||
#
|
|
||||||
# Searching for communication messages
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# message_search(self, sender=None, receiver=None, channel=None, freetext=None)
|
|
||||||
#
|
|
||||||
# Search the message database for particular messages. At least one
|
|
||||||
# of the arguments must be given to do a search.
|
|
||||||
#
|
|
||||||
# sender - get messages sent by a particular account
|
|
||||||
# receiver - get messages received by a certain account
|
|
||||||
# channel - get messages sent to a particular channel
|
|
||||||
# freetext - Search for a text string in a message.
|
|
||||||
# NOTE: This can potentially be slow, so make sure to supply
|
|
||||||
# one of the other arguments to limit the search.
|
|
||||||
#
|
|
||||||
|
|
||||||
search_message = Msg.objects.search_message
|
|
||||||
|
def search_message(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Search for messages in the database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
sender (Object, Account or str): Filter by messages sent by this entity.
|
||||||
|
If a string, this is an external sender name.
|
||||||
|
receiver (Object, Account or str): Filter by messages received by this entity.
|
||||||
|
If a string, this is an external receiver name.
|
||||||
|
channel (Channel): Filter by messages sent to this channel.
|
||||||
|
date (datetime): Filter by messages sent on this date.
|
||||||
|
type (str): Filter by messages of this type.
|
||||||
|
tags (str or list): Filter by messages having one or more Tags.
|
||||||
|
This can be a single tag key, a list of tag keys, or a list of
|
||||||
|
tuples (tag_key, tag_category).
|
||||||
|
exclude_tags (str or list): Exclude messages with these tags.
|
||||||
|
search_text (str): Search for text in message content.
|
||||||
|
exact (bool): If True, require exact text match. Default False.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matches (list): List of Messages matching the search criteria.
|
||||||
|
"""
|
||||||
|
return Msg.objects.search_message(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
search_messages = search_message
|
search_messages = search_message
|
||||||
message_search = search_message
|
message_search = search_message
|
||||||
messages = search_messages
|
messages = search_messages
|
||||||
|
|
||||||
#
|
|
||||||
# Search for Communication Channels
|
|
||||||
#
|
|
||||||
# channel_search(self, ostring)
|
|
||||||
#
|
|
||||||
# Search the channel database for a particular channel.
|
|
||||||
#
|
|
||||||
# ostring - the key or database id of the channel.
|
|
||||||
# exact - requires an exact ostring match (not case sensitive)
|
|
||||||
#
|
|
||||||
|
|
||||||
search_channel = ChannelDB.objects.search_channel
|
def search_channel(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Search for channels in the database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str or int): Channel key or dbref to search for. This can also
|
||||||
|
be a list of keys/dbrefs. `None` (default) returns all channels.
|
||||||
|
exact (bool): Only valid for string keys. If True, requires exact
|
||||||
|
key match, otherwise also match key with case-insensitive and
|
||||||
|
partial matching. Default is True.
|
||||||
|
candidates (list): Only search among these channel candidates,
|
||||||
|
if given. Default is to search all channels.
|
||||||
|
attribute_name (str): If set, search by channels with this attribute_name
|
||||||
|
defined on them, with the value specified by `attribute_value`.
|
||||||
|
attribute_value (any): What value the given attribute_name must have.
|
||||||
|
typeclass (str or TypeClass): Filter by channels having this typeclass.
|
||||||
|
This can also be a list of typeclasses.
|
||||||
|
tags (str or list): Filter by channels having one or more Tags.
|
||||||
|
This can be a single tag key, a list of tag keys, or a list of
|
||||||
|
tuples (tag_key, tag_category).
|
||||||
|
nofetch (bool): Don't fetch typeclass and perms data from db.
|
||||||
|
This is faster but gives less info.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matches (list): List of Channels matching the search criteria.
|
||||||
|
"""
|
||||||
|
return ChannelDB.objects.search_channel(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
search_channels = search_channel
|
search_channels = search_channel
|
||||||
channel_search = search_channel
|
channel_search = search_channel
|
||||||
channels = search_channels
|
channels = search_channels
|
||||||
|
|
||||||
#
|
|
||||||
# Find help entry objects.
|
|
||||||
#
|
|
||||||
# search_help(self, ostring, help_category=None)
|
|
||||||
#
|
|
||||||
# Retrieve a search entry object.
|
|
||||||
#
|
|
||||||
# ostring - the help topic to look for
|
|
||||||
# category - limit the search to a particular help topic
|
|
||||||
#
|
|
||||||
|
|
||||||
search_help = HelpEntry.objects.search_help
|
def search_help(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Search for help entries in the database.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key (str or int): Help entry key or dbref to search for. This can also
|
||||||
|
be a list of keys/dbrefs. `None` (default) returns all help entries.
|
||||||
|
exact (bool): Only valid for string keys. If True, requires exact
|
||||||
|
key match, otherwise also match key with case-insensitive and
|
||||||
|
partial matching. Default is True.
|
||||||
|
category (str): Filter by help entries in this category.
|
||||||
|
tags (str or list): Filter by help entries having one or more Tags.
|
||||||
|
This can be a single tag key, a list of tag keys, or a list of
|
||||||
|
tuples (tag_key, tag_category).
|
||||||
|
locks (str): Filter by help entries with these locks.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
matches (list): List of HelpEntries matching the search criteria.
|
||||||
|
"""
|
||||||
|
return HelpEntry.objects.search_help(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
search_help_entry = search_help
|
search_help_entry = search_help
|
||||||
search_help_entries = search_help
|
search_help_entries = search_help
|
||||||
help_entry_search = search_help
|
help_entry_search = search_help
|
||||||
help_entries = search_help
|
help_entries = search_help
|
||||||
|
|
||||||
|
|
||||||
# Locate Attributes
|
|
||||||
|
|
||||||
# search_object_attribute(key, category, value, strvalue) (also search_attribute works)
|
|
||||||
# search_account_attribute(key, category, value, strvalue) (also search_attribute works)
|
|
||||||
# search_script_attribute(key, category, value, strvalue) (also search_attribute works)
|
|
||||||
# search_channel_attribute(key, category, value, strvalue) (also search_attribute works)
|
|
||||||
|
|
||||||
# Note that these return the object attached to the Attribute,
|
|
||||||
# not the attribute object itself (this is usually what you want)
|
|
||||||
|
|
||||||
|
|
||||||
def search_object_attribute(
|
def search_object_attribute(
|
||||||
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
|
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Search for objects by their attributes.
|
||||||
|
"""
|
||||||
return ObjectDB.objects.get_by_attribute(
|
return ObjectDB.objects.get_by_attribute(
|
||||||
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
|
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
|
||||||
)
|
)
|
||||||
|
|
@ -227,6 +308,9 @@ def search_object_attribute(
|
||||||
def search_account_attribute(
|
def search_account_attribute(
|
||||||
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
|
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Search for accounts by their attributes.
|
||||||
|
"""
|
||||||
return AccountDB.objects.get_by_attribute(
|
return AccountDB.objects.get_by_attribute(
|
||||||
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
|
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
|
||||||
)
|
)
|
||||||
|
|
@ -235,6 +319,9 @@ def search_account_attribute(
|
||||||
def search_script_attribute(
|
def search_script_attribute(
|
||||||
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
|
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Search for scripts by their attributes.
|
||||||
|
"""
|
||||||
return ScriptDB.objects.get_by_attribute(
|
return ScriptDB.objects.get_by_attribute(
|
||||||
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
|
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
|
||||||
)
|
)
|
||||||
|
|
@ -243,23 +330,20 @@ def search_script_attribute(
|
||||||
def search_channel_attribute(
|
def search_channel_attribute(
|
||||||
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
|
key=None, category=None, value=None, strvalue=None, attrtype=None, **kwargs
|
||||||
):
|
):
|
||||||
|
"""
|
||||||
|
Search for channels by their attributes.
|
||||||
|
"""
|
||||||
return ChannelDB.objects.get_by_attribute(
|
return ChannelDB.objects.get_by_attribute(
|
||||||
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
|
key=key, category=category, value=value, strvalue=strvalue, attrtype=attrtype, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# search for attribute objects
|
# Replace direct assignments with functions
|
||||||
search_attribute_object = ObjectDB.objects.get_attribute
|
def search_attribute_object(*args, **kwargs):
|
||||||
|
"""
|
||||||
# Locate Tags
|
Search for attribute objects.
|
||||||
|
"""
|
||||||
# search_object_tag(key=None, category=None) (also search_tag works)
|
return ObjectDB.objects.get_attribute(*args, **kwargs)
|
||||||
# search_account_tag(key=None, category=None)
|
|
||||||
# search_script_tag(key=None, category=None)
|
|
||||||
# search_channel_tag(key=None, category=None)
|
|
||||||
|
|
||||||
# Note that this returns the object attached to the tag, not the tag
|
|
||||||
# object itself (this is usually what you want)
|
|
||||||
|
|
||||||
|
|
||||||
def search_object_by_tag(key=None, category=None, tagtype=None, **kwargs):
|
def search_object_by_tag(key=None, category=None, tagtype=None, **kwargs):
|
||||||
|
|
@ -281,7 +365,6 @@ def search_object_by_tag(key=None, category=None, tagtype=None, **kwargs):
|
||||||
matches (list): List of Objects with tags matching
|
matches (list): List of Objects with tags matching
|
||||||
the search criteria, or an empty list if no
|
the search criteria, or an empty list if no
|
||||||
matches were found.
|
matches were found.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return ObjectDB.objects.get_by_tag(key=key, category=category, tagtype=tagtype, **kwargs)
|
return ObjectDB.objects.get_by_tag(key=key, category=category, tagtype=tagtype, **kwargs)
|
||||||
|
|
||||||
|
|
@ -292,23 +375,6 @@ search_tag = search_object_by_tag # this is the most common case
|
||||||
def search_account_tag(key=None, category=None, tagtype=None, **kwargs):
|
def search_account_tag(key=None, category=None, tagtype=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Find account based on tag or category.
|
Find account based on tag or category.
|
||||||
|
|
||||||
Args:
|
|
||||||
key (str, optional): The tag key to search for.
|
|
||||||
category (str, optional): The category of tag
|
|
||||||
to search for. If not set, uncategorized
|
|
||||||
tags will be searched.
|
|
||||||
tagtype (str, optional): 'type' of Tag, by default
|
|
||||||
this is either `None` (a normal Tag), `alias` or
|
|
||||||
`permission`. This always apply to all queried tags.
|
|
||||||
kwargs (any): Other optional parameter that may be supported
|
|
||||||
by the manager method.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
matches (list): List of Accounts with tags matching
|
|
||||||
the search criteria, or an empty list if no
|
|
||||||
matches were found.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return AccountDB.objects.get_by_tag(key=key, category=category, tagtype=tagtype, **kwargs)
|
return AccountDB.objects.get_by_tag(key=key, category=category, tagtype=tagtype, **kwargs)
|
||||||
|
|
||||||
|
|
@ -316,23 +382,6 @@ def search_account_tag(key=None, category=None, tagtype=None, **kwargs):
|
||||||
def search_script_tag(key=None, category=None, tagtype=None, **kwargs):
|
def search_script_tag(key=None, category=None, tagtype=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Find script based on tag or category.
|
Find script based on tag or category.
|
||||||
|
|
||||||
Args:
|
|
||||||
key (str, optional): The tag key to search for.
|
|
||||||
category (str, optional): The category of tag
|
|
||||||
to search for. If not set, uncategorized
|
|
||||||
tags will be searched.
|
|
||||||
tagtype (str, optional): 'type' of Tag, by default
|
|
||||||
this is either `None` (a normal Tag), `alias` or
|
|
||||||
`permission`. This always apply to all queried tags.
|
|
||||||
kwargs (any): Other optional parameter that may be supported
|
|
||||||
by the manager method.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
matches (list): List of Scripts with tags matching
|
|
||||||
the search criteria, or an empty list if no
|
|
||||||
matches were found.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return ScriptDB.objects.get_by_tag(key=key, category=category, tagtype=tagtype, **kwargs)
|
return ScriptDB.objects.get_by_tag(key=key, category=category, tagtype=tagtype, **kwargs)
|
||||||
|
|
||||||
|
|
@ -340,35 +389,16 @@ def search_script_tag(key=None, category=None, tagtype=None, **kwargs):
|
||||||
def search_channel_tag(key=None, category=None, tagtype=None, **kwargs):
|
def search_channel_tag(key=None, category=None, tagtype=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Find channel based on tag or category.
|
Find channel based on tag or category.
|
||||||
|
|
||||||
Args:
|
|
||||||
key (str, optional): The tag key to search for.
|
|
||||||
category (str, optional): The category of tag
|
|
||||||
to search for. If not set, uncategorized
|
|
||||||
tags will be searched.
|
|
||||||
tagtype (str, optional): 'type' of Tag, by default
|
|
||||||
this is either `None` (a normal Tag), `alias` or
|
|
||||||
`permission`. This always apply to all queried tags.
|
|
||||||
kwargs (any): Other optional parameter that may be supported
|
|
||||||
by the manager method.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
matches (list): List of Channels with tags matching
|
|
||||||
the search criteria, or an empty list if no
|
|
||||||
matches were found.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return ChannelDB.objects.get_by_tag(key=key, category=category, tagtype=tagtype, **kwargs)
|
return ChannelDB.objects.get_by_tag(key=key, category=category, tagtype=tagtype, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# search for tag objects (not the objects they are attached to
|
# Replace direct assignment with function
|
||||||
search_tag_object = ObjectDB.objects.get_tag
|
def search_tag_object(*args, **kwargs):
|
||||||
|
"""
|
||||||
|
Search for tag objects.
|
||||||
# Locate Objects by Typeclass
|
"""
|
||||||
|
return ObjectDB.objects.get_tag(*args, **kwargs)
|
||||||
# search_objects_by_typeclass(typeclass="", include_children=False, include_parents=False) (also search_typeclass works)
|
|
||||||
# This returns the objects of the given typeclass
|
|
||||||
|
|
||||||
|
|
||||||
def search_objects_by_typeclass(typeclass, include_children=False, include_parents=False):
|
def search_objects_by_typeclass(typeclass, include_children=False, include_parents=False):
|
||||||
|
|
|
||||||
|
|
@ -5,86 +5,98 @@ folder and edit it to add/remove links to the menu.
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<nav class="navbar navbar-dark font-weight-bold navbar-expand-md">
|
<nav class="navbar navbar-dark font-weight-bold navbar-expand-md">
|
||||||
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#menu-content" aria-controls="menu-content" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#menu-content"
|
||||||
<span class="navbar-toggler-icon"></span>
|
aria-controls="menu-content" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
</button>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<a class="navbar-brand" href="/">
|
<a class="navbar-brand" href="/">
|
||||||
<div class="media">
|
<div class="media">
|
||||||
<img class="d-flex navbar-brand-logo mx-3" src="{% static "website/images/evennia_logo.png" %}" alt="{{game_name}} logo" />
|
<img class="d-flex navbar-brand-logo mx-3" src="{% static "website/images/evennia_logo.png" %}"
|
||||||
<div class="media-body">
|
alt="{{game_name}} logo" />
|
||||||
{{ game_name }}<br />
|
<div class="media-body">
|
||||||
<small>{{game_slogan}}</small>
|
{{ game_name }}<br />
|
||||||
</div>
|
<small>{{game_slogan}}</small>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="menu-content">
|
|
||||||
<ul class="navbar-nav">
|
|
||||||
{% block navbar_left %}
|
|
||||||
<li>
|
|
||||||
<a class="nav-link" href="{% url 'index' %}">Home</a>
|
|
||||||
</li>
|
|
||||||
<!-- game views -->
|
|
||||||
<li><a class="nav-link" href="{% url 'characters' %}">Characters</a></li>
|
|
||||||
<li><a class="nav-link" href="{% url 'channels' %}">Channels</a></li>
|
|
||||||
<li><a class="nav-link" href="{% url 'help' %}">Help</a></li>
|
|
||||||
<!-- end game views -->
|
|
||||||
|
|
||||||
{% if webclient_enabled %}
|
|
||||||
<li><a class="nav-link" href="{% url 'webclient:index' %}">Play Online</a></li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if user.is_staff %}
|
|
||||||
<li><a class="nav-link" href="{% url 'admin:index' %}">Admin</a></li>
|
|
||||||
{% if rest_api_enabled %}
|
|
||||||
<li><a class="nav-link" href="/api">API</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
</ul>
|
|
||||||
<ul class="nav navbar-nav ml-auto w-120 justify-content-end">
|
|
||||||
{% block navbar_right %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block navbar_user %}
|
|
||||||
{% if account %}
|
|
||||||
<li class="nav-item dropdown">
|
|
||||||
<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" id="user_options" aria-expanded="false">
|
|
||||||
{% if puppet %}
|
|
||||||
Welcome, {{ puppet }}! <span class="text-muted">({{ account.username }})</span> <span class="caret"></span>
|
|
||||||
{% else %}
|
|
||||||
Logged in as {{ account.username }} <span class="caret"></span>
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
|
||||||
<div class="dropdown-menu" aria-labelledby="user_options">
|
|
||||||
<a class="dropdown-item" href="{% url 'character-create' %}">Create Character</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'character-manage' %}">Manage Characters</a>
|
|
||||||
<div class="dropdown-divider"></div>
|
|
||||||
{% for character in account.characters|slice:"10" %}
|
|
||||||
<a class="dropdown-item" href="{{ character.web_get_puppet_url }}?next={{ request.path }}">{{ character }}</a>
|
|
||||||
{% empty %}
|
|
||||||
<a class="dropdown-item" href="#">No characters found!</a>
|
|
||||||
{% endfor %}
|
|
||||||
<div class="dropdown-divider"></div>
|
|
||||||
<a class="dropdown-item" href="{% url 'password_change' %}">Change Password</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'logout' %}">Log Out</a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a class="nav-link" href="{% url 'logout' %}">Log Out</a>
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li>
|
|
||||||
<a class="nav-link" href="{% url 'login' %}?next={{ request.path }}">Log In</a>
|
|
||||||
</li>
|
|
||||||
{% if register_enabled %}
|
|
||||||
<li>
|
|
||||||
<a class="nav-link" href="{% url 'register' %}">Register</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="menu-content">
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
{% block navbar_left %}
|
||||||
|
<li>
|
||||||
|
<a class="nav-link" href="{% url 'index' %}">Home</a>
|
||||||
|
</li>
|
||||||
|
<!-- game views -->
|
||||||
|
<li><a class="nav-link" href="{% url 'characters' %}">Characters</a></li>
|
||||||
|
<li><a class="nav-link" href="{% url 'channels' %}">Channels</a></li>
|
||||||
|
<li><a class="nav-link" href="{% url 'help' %}">Help</a></li>
|
||||||
|
<!-- end game views -->
|
||||||
|
|
||||||
|
{% if webclient_enabled %}
|
||||||
|
<li><a class="nav-link" href="{% url 'webclient:index' %}">Play Online</a></li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if user.is_staff %}
|
||||||
|
<li><a class="nav-link" href="{% url 'admin:index' %}">Admin</a></li>
|
||||||
|
{% if rest_api_enabled %}
|
||||||
|
<li><a class="nav-link" href="/api">API</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
</ul>
|
||||||
|
<ul class="nav navbar-nav ml-auto w-120 justify-content-end">
|
||||||
|
{% block navbar_right %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navbar_user %}
|
||||||
|
{% if account %}
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" id="user_options" aria-expanded="false">
|
||||||
|
{% if puppet %}
|
||||||
|
Welcome, {{ puppet }}! <span class="text-muted">({{ account.username }})</span> <span class="caret"></span>
|
||||||
|
{% else %}
|
||||||
|
Logged in as {{ account.username }} <span class="caret"></span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="user_options">
|
||||||
|
<a class="dropdown-item" href="{% url 'character-create' %}">Create Character</a>
|
||||||
|
<a class="dropdown-item" href="{% url 'character-manage' %}">Manage Characters</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
{% for character in account.characters|slice:"10" %}
|
||||||
|
<a class="dropdown-item" href="{{ character.web_get_puppet_url }}?next={{ request.path }}">{{ character }}</a>
|
||||||
|
{% empty %}
|
||||||
|
<a class="dropdown-item" href="#">No characters found!</a>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item" href="{% url 'password_change' %}">Change Password</a>
|
||||||
|
<form method="post" action="{% url 'logout' %}" style="display:inline;">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="dropdown-item">Log Out</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<form method="post" action="{% url 'logout' %}" style="display:inline;">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="nav-link btn btn-link">Log Out</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
<form method="post" action="{% url 'login' %}" style="display:inline;">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="next" value="{{ request.path }}">
|
||||||
|
<button type="submit" class="nav-link btn btn-link">Log In</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
{% if register_enabled %}
|
||||||
|
<li>
|
||||||
|
<a class="nav-link" href="{% url 'register' %}">Register</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,20 @@ class LoginTest(EvenniaWebTest):
|
||||||
class LogoutTest(EvenniaWebTest):
|
class LogoutTest(EvenniaWebTest):
|
||||||
url_name = "logout"
|
url_name = "logout"
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
"""Since Django 5.0, logout is no longer supported with GET requests"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_post(self):
|
||||||
|
"""Do the logout test with a POST request"""
|
||||||
|
response = self.client.post(reverse(self.url_name), follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_get_authenticated(self):
|
||||||
|
"""Do the logout test with a POST instead of GET"""
|
||||||
|
response = self.client.post(reverse(self.url_name), follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
class PasswordResetTest(EvenniaWebTest):
|
class PasswordResetTest(EvenniaWebTest):
|
||||||
url_name = "password_change"
|
url_name = "password_change"
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ classifiers = [
|
||||||
dependencies = [
|
dependencies = [
|
||||||
# core dependencies
|
# core dependencies
|
||||||
"legacy-cgi;python_version >= '3.13'",
|
"legacy-cgi;python_version >= '3.13'",
|
||||||
"django >= 4.2, < 4.3",
|
"django >= 5.1, < 5.2",
|
||||||
"twisted >= 24.11.0, < 25",
|
"twisted >= 24.11.0, < 25",
|
||||||
"pytz >= 2022.6",
|
"pytz >= 2022.6",
|
||||||
"djangorestframework >= 3.14, < 3.15",
|
"djangorestframework >= 3.14, < 3.15",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue