Merge branch 'cmd_and_file_help_web_support' of https://github.com/davewiththenicehat/evennia into davewiththenicehat-cmd_and_file_help_web_support
This commit is contained in:
commit
d00915d092
7 changed files with 437 additions and 82 deletions
|
|
@ -9,6 +9,8 @@ import math
|
|||
import inspect
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
|
||||
from evennia.locks.lockhandler import LockHandler
|
||||
from evennia.utils.utils import is_iter, fill, lazy_property, make_iter
|
||||
|
|
@ -514,6 +516,53 @@ Command {self} has no defined `func()` - showing on-command variables:
|
|||
"""
|
||||
return self.__doc__
|
||||
|
||||
def web_get_detail_url(self):
|
||||
"""
|
||||
Returns the URI path for a View that allows users to view details for
|
||||
this object.
|
||||
|
||||
ex. Oscar (Character) = '/characters/oscar/1/'
|
||||
|
||||
For this to work, the developer must have defined a named view somewhere
|
||||
in urls.py that follows the format 'modelname-action', so in this case
|
||||
a named view of 'character-detail' would be referenced by this method.
|
||||
|
||||
ex.
|
||||
::
|
||||
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$',
|
||||
CharDetailView.as_view(), name='character-detail')
|
||||
|
||||
If no View has been created and defined in urls.py, returns an
|
||||
HTML anchor.
|
||||
|
||||
This method is naive and simply returns a path. Securing access to
|
||||
the actual view and limiting who can view this object is the developer's
|
||||
responsibility.
|
||||
|
||||
Returns:
|
||||
path (str): URI path to object detail page, if defined.
|
||||
|
||||
"""
|
||||
try:
|
||||
return reverse(
|
||||
'help-entry-detail',
|
||||
kwargs={"category": slugify(self.help_category), "topic": slugify(self.key)},
|
||||
)
|
||||
except Exception as e:
|
||||
return "#"
|
||||
|
||||
def web_get_admin_url(self):
|
||||
"""
|
||||
Returns the URI path for the Django Admin page for this object.
|
||||
|
||||
ex. Account#1 = '/admin/accounts/accountdb/1/change/'
|
||||
|
||||
Returns:
|
||||
path (str): URI path to Django Admin page for object.
|
||||
|
||||
"""
|
||||
return False
|
||||
|
||||
def client_width(self):
|
||||
"""
|
||||
Get the client screenwidth for the session using this command.
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ An example of the contents of a module:
|
|||
|
||||
from dataclasses import dataclass
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
from evennia.utils.utils import (
|
||||
variable_from_module, make_iter, all_from_module)
|
||||
from evennia.utils import logger
|
||||
|
|
@ -115,6 +117,53 @@ class FileHelpEntry:
|
|||
def locks(self):
|
||||
return LockHandler(self)
|
||||
|
||||
def web_get_detail_url(self):
|
||||
"""
|
||||
Returns the URI path for a View that allows users to view details for
|
||||
this object.
|
||||
|
||||
ex. Oscar (Character) = '/characters/oscar/1/'
|
||||
|
||||
For this to work, the developer must have defined a named view somewhere
|
||||
in urls.py that follows the format 'modelname-action', so in this case
|
||||
a named view of 'character-detail' would be referenced by this method.
|
||||
|
||||
ex.
|
||||
::
|
||||
url(r'characters/(?P<slug>[\w\d\-]+)/(?P<pk>[0-9]+)/$',
|
||||
CharDetailView.as_view(), name='character-detail')
|
||||
|
||||
If no View has been created and defined in urls.py, returns an
|
||||
HTML anchor.
|
||||
|
||||
This method is naive and simply returns a path. Securing access to
|
||||
the actual view and limiting who can view this object is the developer's
|
||||
responsibility.
|
||||
|
||||
Returns:
|
||||
path (str): URI path to object detail page, if defined.
|
||||
|
||||
"""
|
||||
try:
|
||||
return reverse(
|
||||
'help-entry-detail',
|
||||
kwargs={"category": slugify(self.help_category), "topic": slugify(self.key)},
|
||||
)
|
||||
except Exception:
|
||||
return "#"
|
||||
|
||||
def web_get_admin_url(self):
|
||||
"""
|
||||
Returns the URI path for the Django Admin page for this object.
|
||||
|
||||
ex. Account#1 = '/admin/accounts/accountdb/1/change/'
|
||||
|
||||
Returns:
|
||||
path (str): URI path to Django Admin page for object.
|
||||
|
||||
"""
|
||||
return False
|
||||
|
||||
def access(self, accessing_obj, access_type="view", default=True):
|
||||
"""
|
||||
Determines if another object has permission to access this help entry.
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ class HelpEntry(SharedMemoryModel):
|
|||
db_key = models.CharField(
|
||||
"help key", max_length=255, unique=True, help_text="key to search for"
|
||||
)
|
||||
|
||||
# help category
|
||||
db_help_category = models.CharField(
|
||||
"help category",
|
||||
|
|
@ -63,6 +64,7 @@ class HelpEntry(SharedMemoryModel):
|
|||
default="General",
|
||||
help_text="organizes help entries in lists",
|
||||
)
|
||||
|
||||
# the actual help entry text, in any formatting.
|
||||
db_entrytext = models.TextField(
|
||||
"help entry", blank=True, help_text="the main body of help text"
|
||||
|
|
@ -221,6 +223,7 @@ class HelpEntry(SharedMemoryModel):
|
|||
path (str): URI path to object detail page, if defined.
|
||||
|
||||
"""
|
||||
|
||||
try:
|
||||
return reverse(
|
||||
"%s-detail" % slugify(self._meta.verbose_name),
|
||||
|
|
|
|||
|
|
@ -17,15 +17,15 @@
|
|||
<hr />
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'help' %}">Help Index</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'help' %}#{{ object.db_help_category }}">{{ object.db_help_category|title }}</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">{{ object.db_key|title }}</li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'help' %}#{{ object.web_help_category }}">{{ object.help_category|title }}</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">{{ object.web_help_key|title }}</li>
|
||||
</ol>
|
||||
<hr />
|
||||
|
||||
<div class="row">
|
||||
<!-- left column -->
|
||||
<div class="col-lg-9 col-sm-12">
|
||||
<p>{{ entry_text }}</p>
|
||||
<pre>{{ entry_text }}</pre>
|
||||
|
||||
{% if topic_previous or topic_next %}
|
||||
<hr />
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
{% endif %}
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">{{ object.db_help_category|title }}</div>
|
||||
<div class="card-header">{{ object.web_help_category|title }}</div>
|
||||
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for topic in topic_list %}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
</ol>
|
||||
<hr />
|
||||
<div class="row">
|
||||
{% regroup object_list by help_category as category_list %}
|
||||
{% regroup object_list by web_help_category as category_list %}
|
||||
|
||||
{% if category_list %}
|
||||
<!-- left column -->
|
||||
|
|
@ -36,11 +36,18 @@
|
|||
|
||||
<!-- index list -->
|
||||
<div class="mx-3">
|
||||
{% for help_category in category_list %}
|
||||
<h5><a id="{{ help_category.grouper }}"></a>{{ help_category.grouper|title }}</h5>
|
||||
{% for web_help_category in category_list %}
|
||||
<h5><a id="{{ web_help_category.grouper }}"></a>{{ web_help_category.grouper|title }}</h5>
|
||||
<ul>
|
||||
{% for object in help_category.list %}
|
||||
<li><a href="{{ object.web_get_detail_url }}">{{ object|title }}</a></li>
|
||||
{% for object in web_help_category.list %}
|
||||
<li>
|
||||
<a href="{{ object.web_get_detail_url }}">{{ object|title }}</a>
|
||||
{% if object.web_get_admin_url %}
|
||||
{% if user.is_staff %}
|
||||
<a href="{{ object.web_get_admin_url }}">-edit-</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ from django.utils.text import slugify
|
|||
from django.test import Client, override_settings
|
||||
from django.urls import reverse
|
||||
from evennia.utils import class_from_module
|
||||
from evennia.utils.create import create_help_entry
|
||||
from evennia.utils.test_resources import EvenniaTest
|
||||
from evennia.help import filehelp
|
||||
|
||||
_FILE_HELP_ENTRIES = None
|
||||
|
||||
|
||||
class EvenniaWebTest(EvenniaTest):
|
||||
|
|
@ -135,6 +139,79 @@ class ChannelDetailTest(EvenniaWebTest):
|
|||
return {"slug": slugify("demo")}
|
||||
|
||||
|
||||
class HelpListTest(EvenniaWebTest):
|
||||
url_name = "help"
|
||||
|
||||
|
||||
HELP_ENTRY_DICTS = [
|
||||
{
|
||||
"key": "unit test file entry",
|
||||
"category": "General",
|
||||
"text": "cache test file entry text"
|
||||
}
|
||||
]
|
||||
|
||||
class HelpDetailTest(EvenniaWebTest):
|
||||
url_name = "help-entry-detail"
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
# create a db help entry
|
||||
create_help_entry('unit test db entry', 'unit test db entry text', category="General")
|
||||
|
||||
def get_kwargs(self):
|
||||
return {"category": slugify("general"),
|
||||
"topic": slugify('unit test db entry')}
|
||||
|
||||
def test_view(self):
|
||||
response = self.client.get(reverse(self.url_name, kwargs=self.get_kwargs()), follow=True)
|
||||
self.assertEqual(response.context["entry_text"], 'unit test db entry text')
|
||||
|
||||
def test_object_cache(self):
|
||||
# clear file help entries, use local HELP_ENTRY_DICTS to recreate new entries
|
||||
global _FILE_HELP_ENTRIES
|
||||
if _FILE_HELP_ENTRIES is None:
|
||||
from evennia.help.filehelp import FILE_HELP_ENTRIES as _FILE_HELP_ENTRIES
|
||||
help_module = 'evennia.web.website.tests'
|
||||
self.file_help_store = _FILE_HELP_ENTRIES.__init__(help_file_modules=[help_module])
|
||||
|
||||
# request access to an entry
|
||||
response = self.client.get(reverse(self.url_name, kwargs=self.get_kwargs()), follow=True)
|
||||
self.assertEqual(response.context["entry_text"], 'unit test db entry text')
|
||||
# request a second entry, verifing the cached object is not provided on a new topic request
|
||||
entry_two_args = {"category": slugify("general"), "topic": slugify('unit test file entry')}
|
||||
response = self.client.get(reverse(self.url_name, kwargs=entry_two_args), follow=True)
|
||||
self.assertEqual(response.context["entry_text"], 'cache test file entry text')
|
||||
|
||||
|
||||
class HelpLockedDetailTest(EvenniaWebTest):
|
||||
url_name = "help-entry-detail"
|
||||
|
||||
def setUp(self):
|
||||
super(HelpLockedDetailTest, self).setUp()
|
||||
|
||||
# create a db entry with a lock
|
||||
self.db_help_entry = create_help_entry('unit test locked topic', 'unit test locked entrytext',
|
||||
category="General", locks='read:perm(Developer)')
|
||||
|
||||
def get_kwargs(self):
|
||||
return {"category": slugify("general"),
|
||||
"topic": slugify('unit test locked topic')}
|
||||
|
||||
def test_locked_entry(self):
|
||||
# request access to an entry for permission the account does not have
|
||||
response = self.client.get(reverse(self.url_name, kwargs=self.get_kwargs()), follow=True)
|
||||
self.assertEqual(response.context["entry_text"], 'Failed to find entry.')
|
||||
|
||||
def test_lock_with_perm(self):
|
||||
# log TestAccount in, grant permission required, read the entry
|
||||
self.login()
|
||||
self.account.permissions.add("Developer")
|
||||
response = self.client.get(reverse(self.url_name, kwargs=self.get_kwargs()), follow=True)
|
||||
self.assertEqual(response.context["entry_text"], 'unit test locked entrytext')
|
||||
|
||||
|
||||
class CharacterCreateView(EvenniaWebTest):
|
||||
url_name = "character-create"
|
||||
unauthenticated_response = 302
|
||||
|
|
|
|||
|
|
@ -1,17 +1,161 @@
|
|||
"""
|
||||
Views to manipulate help entries.
|
||||
|
||||
Multi entry object type supported added by DaveWithTheNiceHat 2021
|
||||
Pull Request #2429
|
||||
"""
|
||||
|
||||
from django.utils.text import slugify
|
||||
from django.views.generic import ListView
|
||||
from django.conf import settings
|
||||
from evennia.utils.utils import inherits_from
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.http import HttpResponseBadRequest
|
||||
from django.db.models.functions import Lower
|
||||
from evennia.help.models import HelpEntry
|
||||
from .mixins import TypeclassMixin, EvenniaDetailView
|
||||
from evennia.help.filehelp import FILE_HELP_ENTRIES
|
||||
from evennia.utils.ansi import strip_ansi
|
||||
|
||||
DEFAULT_HELP_CATEGORY = settings.DEFAULT_HELP_CATEGORY
|
||||
|
||||
|
||||
class HelpMixin(TypeclassMixin):
|
||||
def get_help_category(help_entry, slugify_cat=True):
|
||||
"""Returns help category.
|
||||
|
||||
Args:
|
||||
help_entry (HelpEntry, FileHelpEntry or Command): Help entry instance.
|
||||
slugify_cat (bool): If true the return string is slugified. Default is True.
|
||||
|
||||
Notes:
|
||||
If an entry does not have a `help_category` attribute, DEFAULT_HELP_CATEGORY from the
|
||||
settings file is used.
|
||||
If the entry does not have attribute 'web_help_entries'. One is created with a slugified
|
||||
copy of the attribute help_category. This attribute is used for sorting the entries for the
|
||||
help index (ListView) page.
|
||||
|
||||
Returns:
|
||||
help_category (str): The category for the help entry.
|
||||
"""
|
||||
help_category = getattr(help_entry, 'help_category', None)
|
||||
if not help_category:
|
||||
help_category = getattr(help_entry, 'db_help_category', DEFAULT_HELP_CATEGORY)
|
||||
# if one does not exist, create a category for ease of use with web views html templates
|
||||
if not hasattr(help_entry, 'web_help_category'):
|
||||
setattr(help_entry, 'web_help_category', slugify(help_category))
|
||||
return slugify(help_category) if slugify_cat else help_category
|
||||
|
||||
|
||||
def get_help_topic(help_entry):
|
||||
"""Get the topic of the help entry.
|
||||
|
||||
Args:
|
||||
help_entry (HelpEntry, FileHelpEntry or Command): Help entry instance.
|
||||
|
||||
Returns:
|
||||
help_topic (str): The topic of the help entry. Default is 'unknown_topic'.
|
||||
"""
|
||||
help_topic = getattr(help_entry, 'key', None)
|
||||
# if object has no key, assume it is a db help entry.
|
||||
if not help_topic:
|
||||
help_topic = getattr(help_entry, 'db_key', 'unknown_topic')
|
||||
# if one does not exist, create a key for ease of use with web views html templates
|
||||
if not hasattr(help_entry, 'web_help_key'):
|
||||
setattr(help_entry, 'web_help_key', slugify(help_topic))
|
||||
return help_topic
|
||||
|
||||
|
||||
def can_read_topic(cmd_or_topic, account):
|
||||
"""Check if an account or puppet has read access to a help entry.
|
||||
|
||||
Args:
|
||||
cmd_or_topic (Command, HelpEntry or FileHelpEntry): The topic/command to test.
|
||||
account: the account or puppet checking for access.
|
||||
|
||||
Returns:
|
||||
bool: If command can be viewed or not.
|
||||
|
||||
Notes:
|
||||
This uses the 'read' lock. If no 'read' lock is defined, the topic is assumed readable
|
||||
by all.
|
||||
Even if this returns False, the entry will still be visible in the help index unless
|
||||
`can_list_topic` is also returning False.
|
||||
"""
|
||||
if inherits_from(cmd_or_topic, "evennia.commands.command.Command"):
|
||||
return cmd_or_topic.auto_help and cmd_or_topic.access(account, 'read', default=True)
|
||||
else:
|
||||
return cmd_or_topic.access(account, 'read', default=True)
|
||||
|
||||
|
||||
def collect_topics(account):
|
||||
"""Collect help topics from all sources (cmd/db/file).
|
||||
|
||||
Args:
|
||||
account (Character or Account): Account or Character to collect help topics from.
|
||||
|
||||
Returns:
|
||||
cmd_help_topics (dict): contains Command instances.
|
||||
db_help_topics (dict): contains HelpEntry instances.
|
||||
file_help_topics (dict): contains FileHelpEntry instances
|
||||
"""
|
||||
|
||||
# collect commands of account and all puppets
|
||||
# skip a command if an entry is recorded with the same topics, category and help entry
|
||||
cmd_help_topics = []
|
||||
if not str(account) == 'AnonymousUser':
|
||||
# create list of account and account's puppets
|
||||
puppets = account.db._playable_characters + [account]
|
||||
# add the account's and puppets' commands to cmd_help_topics list
|
||||
for puppet in puppets:
|
||||
for cmdset in puppet.cmdset.get():
|
||||
# removing doublets in cmdset, caused by cmdhandler
|
||||
# having to allow doublet commands to manage exits etc.
|
||||
cmdset.make_unique(puppet)
|
||||
# retrieve all available commands and database / file-help topics.
|
||||
# also check the 'cmd:' lock here
|
||||
for cmd in cmdset:
|
||||
# skip the command if the puppet does not have access
|
||||
if not cmd.access(puppet, 'cmd'):
|
||||
continue
|
||||
# skip the command if the puppet does not have read access
|
||||
if not can_read_topic(cmd, puppet):
|
||||
continue
|
||||
# skip the command if it's help entry already exists in the topic list
|
||||
entry_exists = False
|
||||
for verify_cmd in cmd_help_topics:
|
||||
if (
|
||||
verify_cmd.key and cmd.key and
|
||||
verify_cmd.help_category == cmd.help_category and
|
||||
verify_cmd.__doc__ == cmd.__doc__
|
||||
):
|
||||
entry_exists = True
|
||||
break
|
||||
if entry_exists:
|
||||
continue
|
||||
# add this command to the list
|
||||
cmd_help_topics.append(cmd)
|
||||
|
||||
# Verify account has read access to filehelp entries and gather them into a dictionary
|
||||
file_help_topics = {
|
||||
topic.key.lower().strip(): topic
|
||||
for topic in FILE_HELP_ENTRIES.all()
|
||||
if can_read_topic(topic, account)
|
||||
}
|
||||
|
||||
# Verify account has read access to database entries and gather them into a dictionary
|
||||
db_help_topics = {
|
||||
topic.key.lower().strip(): topic
|
||||
for topic in HelpEntry.objects.all()
|
||||
if can_read_topic(topic, account)
|
||||
}
|
||||
|
||||
# Collect commands into a dictionary, read access verified at puppet level
|
||||
cmd_help_topics = {
|
||||
cmd.auto_help_display_key
|
||||
if hasattr(cmd, "auto_help_display_key") else cmd.key: cmd
|
||||
for cmd in cmd_help_topics
|
||||
}
|
||||
|
||||
return cmd_help_topics, db_help_topics, file_help_topics
|
||||
|
||||
|
||||
class HelpMixin():
|
||||
"""
|
||||
This is a "mixin", a modifier of sorts.
|
||||
|
||||
|
|
@ -20,9 +164,6 @@ class HelpMixin(TypeclassMixin):
|
|||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
model = HelpEntry
|
||||
|
||||
# -- Evennia constructs --
|
||||
page_title = "Help"
|
||||
|
||||
|
|
@ -32,25 +173,24 @@ class HelpMixin(TypeclassMixin):
|
|||
and other documentation that the current user is allowed to see.
|
||||
|
||||
Returns:
|
||||
queryset (QuerySet): List of Help entries available to the user.
|
||||
queryset (list): List of Help entries available to the user.
|
||||
|
||||
"""
|
||||
account = self.request.user
|
||||
|
||||
# Get list of all HelpEntries
|
||||
entries = self.typeclass.objects.all().iterator()
|
||||
# collect all help entries
|
||||
cmd_help_topics, db_help_topics, file_help_topics = collect_topics(account)
|
||||
|
||||
# Now figure out which ones the current user is allowed to see
|
||||
bucket = [entry.id for entry in entries if entry.access(account, "view")]
|
||||
# merge the entries
|
||||
file_db_help_topics = {**file_help_topics, **db_help_topics}
|
||||
all_topics = {**file_db_help_topics, **cmd_help_topics}
|
||||
all_entries = list(all_topics.values())
|
||||
|
||||
# Re-query and set a sorted list
|
||||
filtered = (
|
||||
self.typeclass.objects.filter(id__in=bucket)
|
||||
.order_by(Lower("db_key"))
|
||||
.order_by(Lower("db_help_category"))
|
||||
)
|
||||
# sort the entries
|
||||
all_entries = sorted(all_entries, key=get_help_topic) # sort alphabetically
|
||||
all_entries.sort(key=get_help_category) # group by categories
|
||||
|
||||
return filtered
|
||||
return all_entries
|
||||
|
||||
|
||||
class HelpListView(HelpMixin, ListView):
|
||||
|
|
@ -68,15 +208,23 @@ class HelpListView(HelpMixin, ListView):
|
|||
page_title = "Help Index"
|
||||
|
||||
|
||||
class HelpDetailView(HelpMixin, EvenniaDetailView):
|
||||
class HelpDetailView(HelpMixin, DetailView):
|
||||
"""
|
||||
Returns the detail page for a given help entry.
|
||||
|
||||
"""
|
||||
|
||||
# -- Django constructs --
|
||||
# the html template to use when contructing the detail page for a help topic
|
||||
template_name = "website/help_detail.html"
|
||||
|
||||
@property
|
||||
def page_title(self):
|
||||
# Makes sure the page has a sensible title.
|
||||
obj = self.get_object()
|
||||
topic = get_help_topic(obj)
|
||||
return f'{topic} detail'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""
|
||||
Adds navigational data to the template to let browsers go to the next
|
||||
|
|
@ -88,19 +236,23 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
|
|||
"""
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
# get a full query set
|
||||
full_set = self.get_queryset()
|
||||
|
||||
# Get the object in question
|
||||
obj = self.get_object()
|
||||
obj = self.get_object(full_set)
|
||||
|
||||
# Get queryset and filter out non-related categories
|
||||
queryset = (
|
||||
self.get_queryset()
|
||||
.filter(db_help_category=obj.db_help_category)
|
||||
.order_by(Lower("db_key"))
|
||||
)
|
||||
context["topic_list"] = queryset
|
||||
# filter non related categories from the query set
|
||||
obj_category = get_help_category(obj)
|
||||
category_set = []
|
||||
for entry in full_set:
|
||||
entry_category = get_help_category(entry)
|
||||
if entry_category.lower() == obj_category.lower():
|
||||
category_set.append(entry)
|
||||
context["topic_list"] = category_set
|
||||
|
||||
# Find the index position of the given obj in the queryset
|
||||
objs = list(queryset)
|
||||
# Find the index position of the given obj in the category set
|
||||
objs = list(category_set)
|
||||
for i, x in enumerate(objs):
|
||||
if obj is x:
|
||||
break
|
||||
|
|
@ -118,12 +270,16 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
|
|||
except:
|
||||
context["topic_previous"] = None
|
||||
|
||||
# Format the help entry using HTML instead of newlines
|
||||
text = obj.db_entrytext
|
||||
text = text.replace("\r\n\r\n", "\n\n")
|
||||
text = text.replace("\r\n", "\n")
|
||||
text = text.replace("\n", "<br />")
|
||||
context["entry_text"] = text
|
||||
# Get the help entry text
|
||||
text = 'Failed to find entry.'
|
||||
if inherits_from(obj, "evennia.commands.command.Command"):
|
||||
text = obj.__doc__
|
||||
elif inherits_from(obj, "evennia.help.models.HelpEntry"):
|
||||
text = obj.db_entrytext
|
||||
elif inherits_from(obj, "evennia.help.filehelp.FileHelpEntry"):
|
||||
text = obj.entrytext
|
||||
text = strip_ansi(text) # remove ansii markups
|
||||
context["entry_text"] = text.strip()
|
||||
|
||||
return context
|
||||
|
||||
|
|
@ -132,31 +288,45 @@ class HelpDetailView(HelpMixin, EvenniaDetailView):
|
|||
Override of Django hook that retrieves an object by category and topic
|
||||
instead of pk and slug.
|
||||
|
||||
Args:
|
||||
queryset (list): A list of help entry objects.
|
||||
|
||||
Returns:
|
||||
entry (HelpEntry): HelpEntry requested in the URL.
|
||||
entry (HelpEntry, FileHelpEntry or Command): HelpEntry requested in the URL.
|
||||
|
||||
"""
|
||||
|
||||
if hasattr(self, 'obj'):
|
||||
return getattr(self, 'obj', None)
|
||||
|
||||
# Get the queryset for the help entries the user can access
|
||||
if not queryset:
|
||||
queryset = self.get_queryset()
|
||||
|
||||
# Find the object in the queryset
|
||||
# get the category and topic requested
|
||||
category = slugify(self.kwargs.get("category", ""))
|
||||
topic = slugify(self.kwargs.get("topic", ""))
|
||||
obj = next(
|
||||
(
|
||||
x
|
||||
for x in queryset
|
||||
if slugify(x.db_help_category) == category and slugify(x.db_key) == topic
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
# Find the object in the queryset
|
||||
obj = None
|
||||
for entry in queryset:
|
||||
# continue to next entry if the topics do not match
|
||||
entry_topic = get_help_topic(entry)
|
||||
if not slugify(entry_topic) == topic:
|
||||
continue
|
||||
# if the category also matches, object requested is found
|
||||
entry_category = get_help_category(entry)
|
||||
if slugify(entry_category) == category:
|
||||
obj = entry
|
||||
break
|
||||
|
||||
# Check if this object was requested in a valid manner
|
||||
if not obj:
|
||||
return HttpResponseBadRequest(
|
||||
"No %(verbose_name)s found matching the query"
|
||||
% {"verbose_name": queryset.model._meta.verbose_name}
|
||||
f"No ({category}/{topic})s found matching the query."
|
||||
)
|
||||
else:
|
||||
# cache the object if one was found
|
||||
self.obj = obj
|
||||
|
||||
return obj
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue