Add locks to filehelp entities. Test view/read locks

This commit is contained in:
Griatch 2021-05-30 10:59:36 +02:00
parent 905cc78069
commit 0fb29073b2
3 changed files with 99 additions and 59 deletions

View file

@ -259,46 +259,46 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
return help_index
def check_show_help(self, cmd, caller):
def check_show_help(self, cmd_or_topic, caller):
"""
Helper method. If this return True, the given cmd
auto-help will be viewable in the help listing.
Override this to easily select what is shown to
the account. Note that only commands available
in the caller's merged cmdset are available.
Helper method. If this return True, the given help topic
be viewable in the help listing. Note that even if this returns False,
the entry will still be visible in the help index unless `should_list_topic`
is also returning False.
Args:
cmd (Command): Command class from the merged cmdset
caller (Character, Account or Session): The current caller
executing the help command.
cmd_or_topic (Command, HelpEntry or FileHelpEntry): The topic/command to test.
caller: the caller checking for access.
Returns:
bool: If command can be viewed or not.
"""
# return only those with auto_help set and passing the cmd: lock
return cmd.auto_help and cmd.access(caller)
if inherits_from(cmd_or_topic, "evennia.commands.command.Command"):
return cmd_or_topic.auto_help and cmd_or_topic.access(caller)
else:
return cmd_or_topic.access(caller, 'read', default=True)
def should_list_cmd(self, cmd, caller):
def should_list_topic(self, cmd_or_topic, caller):
"""
Should the specified command appear in the help table?
This method only checks whether a specified command should
appear in the table of topics/commands. The command can be
used by the caller (see the 'check_show_help' method) and
the command will still be available, for instance, if a
character type 'help name of the command'. However, if
you return False, the specified command will not appear in
the table. This is sometimes useful to "hide" commands in
the table, but still access them through the help system.
This method only checks whether a specified command should appear in the table of
topics/commands. The command can be used by the caller (see the 'check_show_help' method)
and the command will still be available, for instance, if a character type 'help name of the
command'. However, if you return False, the specified command will not appear in the table.
This is sometimes useful to "hide" commands in the table, but still access them through the
help system.
Args:
cmd: the command to be tested.
caller: the caller of the help system.
cmd_or_topic (Command, HelpEntry or FileHelpEntry): The topic/command to test.
caller: the caller checking for access.
Return:
True: the command should appear in the table.
False: the command shouldn't appear in the table.
Returns:
bool: If command should be listed or not.
"""
return True
return cmd_or_topic.access(caller, 'view', default=True)
def parse(self):
"""
@ -336,39 +336,49 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
cmdset.make_unique(caller)
# retrieve all available commands and database / file-help topics
all_cmds = [cmd for cmd in cmdset if self.check_show_help(cmd, caller)]
all_cmd_topics = [cmd for cmd in cmdset if self.check_show_help(cmd, caller) if cmd]
# we group the file-help topics with the db ones, giving the db ones priority
file_help_topics = FILE_HELP_ENTRIES.all(return_dict=True)
db_topics = {
topic.key.lower().strip(): topic for topic in HelpEntry.objects.all()
if topic.access(caller, "view", default=True)
# get all file-based help entries, checking perms
file_help_topics = {
topic.key.lower().strip(): topic
for topic in FILE_HELP_ENTRIES.all()
if topic.access(caller)
}
all_db_topics = list({**file_help_topics, **db_topics}.values())
# get db-based help entries, checking perms
db_topics = {
topic.key.lower().strip(): topic
for topic in HelpEntry.objects.all()
if topic.access(caller)
}
# merge so db topics override file topics with same key
all_file_db_topics = list({**file_help_topics, **db_topics}.values())
# get all categories
all_categories = list(set(
[HelpCategory(cmd.help_category) for cmd in all_cmds]
+ [HelpCategory(topic.help_category) for topic in all_db_topics]
[HelpCategory(cmd.help_category) for cmd in all_cmd_topics]
+ [HelpCategory(topic.help_category) for topic in all_file_db_topics]
))
if not query:
# list all available help entries, grouped by category. We want to
# build dictionaries {category: [topic, topic, ...], ...}
cmd_help_dict = defaultdict(list)
db_help_dict = defaultdict(list)
file_db_help_dict = defaultdict(list)
# Filter commands that should be reached by the help
# Filter commands/topics that should be reached by the help
# system, but not be displayed in the table, or be displayed differently.
for cmd in all_cmds:
if self.should_list_cmd(cmd, caller):
key = (cmd.auto_help_display_key
if hasattr(cmd, "auto_help_display_key") else cmd.key)
for cmd in all_cmd_topics:
if self.should_list_topic(cmd, caller):
key = (
cmd.auto_help_display_key
if hasattr(cmd, "auto_help_display_key") else cmd.key
)
cmd_help_dict[cmd.help_category].append(key)
for topic in all_file_db_topics:
if self.should_list_topic(topic, caller):
file_db_help_dict[topic.help_category].append(topic.key)
for db_topic in all_db_topics:
db_help_dict[db_topic.help_category].append(db_topic.key)
output = self.format_help_index(cmd_help_dict, db_help_dict)
output = self.format_help_index(cmd_help_dict, file_db_help_dict)
self.msg_help(output)
return
@ -376,8 +386,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
# We have a query - try to find a specific topic/category using the
# Lunr search engine
# all available options
entries = [cmd for cmd in all_cmds if cmd] + all_db_topics + all_categories
# all available help options - will be searched in order
entries = all_cmd_topics + all_file_db_topics + all_categories
# lunr search fields/boosts
search_fields = [
@ -443,14 +453,14 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
{
match.key: [
cmd.key
for cmd in all_cmds
for cmd in all_cmd_topics
if match.key.lower() == cmd.help_category
]
},
{
match.key: [
topic.key
for topic in all_db_topics
for topic in all_file_db_topics
if match.key.lower() == topic.help_category
]
},

View file

@ -13,12 +13,13 @@ Each help-entry dict is on the form
::
{'key': <str>,
'text': <str>,
'category': <str>, # optional, otherwise settings.DEFAULT_HELP_CATEGORY
'aliases': <list>, # optional
'text': <str>}
'locks': <str>} # optional, use access-type 'view'. Default is view:all()
where the `category` is optional and the `text`` should be formatted on the
same form as other help entry-texts and contain ``# subtopics`` as normal.
The `text`` should be formatted on the same form as other help entry-texts and
can contain ``# subtopics`` as normal.
New help-entry modules are added to the system by providing the python-path to
the module to `settings.FILE_HELP_ENTRY_MODULES`. Note that if same-key entries are
@ -33,6 +34,7 @@ An example of the contents of a module:
"key": "The Gods", # case-insensitive, also partial-matching ('gods') works
"aliases": ['pantheon', 'religion'],
"category": "Lore",
"locks": "view:all()", # this is optional unless restricting access
"text": '''
The gods formed the world ...
@ -68,6 +70,8 @@ from django.conf import settings
from evennia.utils.utils import (
variable_from_module, make_iter, all_from_module)
from evennia.utils import logger
from evennia.utils.utils import lazy_property
from evennia.locks.lockhandler import LockHandler
_DEFAULT_HELP_CATEGORY = settings.DEFAULT_HELP_CATEGORY
@ -84,6 +88,7 @@ class FileHelpEntry:
aliases: list
help_category: str
entrytext: str
lock_storage: str
@property
def search_index_entry(self):
@ -96,6 +101,7 @@ class FileHelpEntry:
"aliases": " ".join(self.aliases),
"category": self.help_category,
"tags": "",
"locks": "",
"text": self.entrytext,
}
@ -105,6 +111,22 @@ class FileHelpEntry:
def __repr__(self):
return f"<FileHelpEntry {self.key}>"
@lazy_property
def locks(self):
return LockHandler(self)
def access(self, accessing_obj, access_type="view", default=True):
"""
Determines if another object has permission to access this help entry.
Args:
accessing_obj (Object or Account): Entity trying to access this one.
access_type (str): type of access sought.
default (bool): What to return if no lock of `access_type` was found.
"""
return self.locks.check(accessing_obj, access_type=access_type, default=default)
class FileHelpStorageHandler:
"""
@ -154,14 +176,15 @@ class FileHelpStorageHandler:
key = dct.get('key').lower().strip()
category = dct.get('category', _DEFAULT_HELP_CATEGORY).strip()
aliases = list(dct.get('aliases', []))
entrytext = dct.get('text')
entrytext = dct.get('text', '')
locks = dct.get('locks', '')
if not key and entrytext:
logger.error(f"Cannot load file-help-entry (missing key or text): {dct}")
continue
unique_help_entries[key] = FileHelpEntry(
key=key, help_category=category, aliases=aliases,
key=key, help_category=category, aliases=aliases, lock_storage=locks,
entrytext=entrytext)
self.help_entries_dict = unique_help_entries

View file

@ -114,12 +114,19 @@ class HelpEntry(SharedMemoryModel):
def __repr__(self):
return f"<HelpEntry {self.key}>"
def access(self, accessing_obj, access_type="read", default=False):
def access(self, accessing_obj, access_type="read", default=True):
"""
Determines if another object has permission to access.
accessing_obj - object trying to access this one
access_type - type of access sought
default - what to return if no lock of access_type was found
Determines if another object has permission to access this help entry.
Accesses used by default:
'read' - read the help entry itself.
'view' - see help entry in help index.
Args:
accessing_obj (Object or Account): Entity trying to access this one.
access_type (str): type of access sought.
default (bool): What to return if no lock of `access_type` was found.
"""
return self.locks.check(accessing_obj, access_type=access_type, default=default)