Add locks to filehelp entities. Test view/read locks
This commit is contained in:
parent
905cc78069
commit
0fb29073b2
3 changed files with 99 additions and 59 deletions
|
|
@ -259,46 +259,46 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
return help_index
|
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
|
Helper method. If this return True, the given help topic
|
||||||
auto-help will be viewable in the help listing.
|
be viewable in the help listing. Note that even if this returns False,
|
||||||
Override this to easily select what is shown to
|
the entry will still be visible in the help index unless `should_list_topic`
|
||||||
the account. Note that only commands available
|
is also returning False.
|
||||||
in the caller's merged cmdset are available.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmd (Command): Command class from the merged cmdset
|
cmd_or_topic (Command, HelpEntry or FileHelpEntry): The topic/command to test.
|
||||||
caller (Character, Account or Session): The current caller
|
caller: the caller checking for access.
|
||||||
executing the help command.
|
|
||||||
|
Returns:
|
||||||
|
bool: If command can be viewed or not.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# return only those with auto_help set and passing the cmd: lock
|
if inherits_from(cmd_or_topic, "evennia.commands.command.Command"):
|
||||||
return cmd.auto_help and cmd.access(caller)
|
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?
|
Should the specified command appear in the help table?
|
||||||
|
|
||||||
This method only checks whether a specified command should
|
This method only checks whether a specified command should appear in the table of
|
||||||
appear in the table of topics/commands. The command can be
|
topics/commands. The command can be used by the caller (see the 'check_show_help' method)
|
||||||
used by the caller (see the 'check_show_help' method) and
|
and the command will still be available, for instance, if a character type 'help name of the
|
||||||
the command will still be available, for instance, if a
|
command'. However, if you return False, the specified command will not appear in the table.
|
||||||
character type 'help name of the command'. However, if
|
This is sometimes useful to "hide" commands in the table, but still access them through the
|
||||||
you return False, the specified command will not appear in
|
help system.
|
||||||
the table. This is sometimes useful to "hide" commands in
|
|
||||||
the table, but still access them through the help system.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cmd: the command to be tested.
|
cmd_or_topic (Command, HelpEntry or FileHelpEntry): The topic/command to test.
|
||||||
caller: the caller of the help system.
|
caller: the caller checking for access.
|
||||||
|
|
||||||
Return:
|
Returns:
|
||||||
True: the command should appear in the table.
|
bool: If command should be listed or not.
|
||||||
False: the command shouldn't appear in the table.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return True
|
return cmd_or_topic.access(caller, 'view', default=True)
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -336,39 +336,49 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
cmdset.make_unique(caller)
|
cmdset.make_unique(caller)
|
||||||
|
|
||||||
# retrieve all available commands and database / file-help topics
|
# 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
|
# get all file-based help entries, checking perms
|
||||||
file_help_topics = FILE_HELP_ENTRIES.all(return_dict=True)
|
file_help_topics = {
|
||||||
db_topics = {
|
topic.key.lower().strip(): topic
|
||||||
topic.key.lower().strip(): topic for topic in HelpEntry.objects.all()
|
for topic in FILE_HELP_ENTRIES.all()
|
||||||
if topic.access(caller, "view", default=True)
|
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(
|
all_categories = list(set(
|
||||||
[HelpCategory(cmd.help_category) for cmd in all_cmds]
|
[HelpCategory(cmd.help_category) for cmd in all_cmd_topics]
|
||||||
+ [HelpCategory(topic.help_category) for topic in all_db_topics]
|
+ [HelpCategory(topic.help_category) for topic in all_file_db_topics]
|
||||||
))
|
))
|
||||||
|
|
||||||
if not query:
|
if not query:
|
||||||
# list all available help entries, grouped by category. We want to
|
# list all available help entries, grouped by category. We want to
|
||||||
# build dictionaries {category: [topic, topic, ...], ...}
|
# build dictionaries {category: [topic, topic, ...], ...}
|
||||||
cmd_help_dict = defaultdict(list)
|
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.
|
# system, but not be displayed in the table, or be displayed differently.
|
||||||
for cmd in all_cmds:
|
for cmd in all_cmd_topics:
|
||||||
if self.should_list_cmd(cmd, caller):
|
if self.should_list_topic(cmd, caller):
|
||||||
key = (cmd.auto_help_display_key
|
key = (
|
||||||
if hasattr(cmd, "auto_help_display_key") else cmd.key)
|
cmd.auto_help_display_key
|
||||||
|
if hasattr(cmd, "auto_help_display_key") else cmd.key
|
||||||
|
)
|
||||||
cmd_help_dict[cmd.help_category].append(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:
|
output = self.format_help_index(cmd_help_dict, file_db_help_dict)
|
||||||
db_help_dict[db_topic.help_category].append(db_topic.key)
|
|
||||||
|
|
||||||
output = self.format_help_index(cmd_help_dict, db_help_dict)
|
|
||||||
self.msg_help(output)
|
self.msg_help(output)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
@ -376,8 +386,8 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
# We have a query - try to find a specific topic/category using the
|
# We have a query - try to find a specific topic/category using the
|
||||||
# Lunr search engine
|
# Lunr search engine
|
||||||
|
|
||||||
# all available options
|
# all available help options - will be searched in order
|
||||||
entries = [cmd for cmd in all_cmds if cmd] + all_db_topics + all_categories
|
entries = all_cmd_topics + all_file_db_topics + all_categories
|
||||||
|
|
||||||
# lunr search fields/boosts
|
# lunr search fields/boosts
|
||||||
search_fields = [
|
search_fields = [
|
||||||
|
|
@ -443,14 +453,14 @@ class CmdHelp(COMMAND_DEFAULT_CLASS):
|
||||||
{
|
{
|
||||||
match.key: [
|
match.key: [
|
||||||
cmd.key
|
cmd.key
|
||||||
for cmd in all_cmds
|
for cmd in all_cmd_topics
|
||||||
if match.key.lower() == cmd.help_category
|
if match.key.lower() == cmd.help_category
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match.key: [
|
match.key: [
|
||||||
topic.key
|
topic.key
|
||||||
for topic in all_db_topics
|
for topic in all_file_db_topics
|
||||||
if match.key.lower() == topic.help_category
|
if match.key.lower() == topic.help_category
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,13 @@ Each help-entry dict is on the form
|
||||||
::
|
::
|
||||||
|
|
||||||
{'key': <str>,
|
{'key': <str>,
|
||||||
|
'text': <str>,
|
||||||
'category': <str>, # optional, otherwise settings.DEFAULT_HELP_CATEGORY
|
'category': <str>, # optional, otherwise settings.DEFAULT_HELP_CATEGORY
|
||||||
'aliases': <list>, # optional
|
'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
|
The `text`` should be formatted on the same form as other help entry-texts and
|
||||||
same form as other help entry-texts and contain ``# subtopics`` as normal.
|
can contain ``# subtopics`` as normal.
|
||||||
|
|
||||||
New help-entry modules are added to the system by providing the python-path to
|
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
|
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
|
"key": "The Gods", # case-insensitive, also partial-matching ('gods') works
|
||||||
"aliases": ['pantheon', 'religion'],
|
"aliases": ['pantheon', 'religion'],
|
||||||
"category": "Lore",
|
"category": "Lore",
|
||||||
|
"locks": "view:all()", # this is optional unless restricting access
|
||||||
"text": '''
|
"text": '''
|
||||||
The gods formed the world ...
|
The gods formed the world ...
|
||||||
|
|
||||||
|
|
@ -68,6 +70,8 @@ from django.conf import settings
|
||||||
from evennia.utils.utils import (
|
from evennia.utils.utils import (
|
||||||
variable_from_module, make_iter, all_from_module)
|
variable_from_module, make_iter, all_from_module)
|
||||||
from evennia.utils import logger
|
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
|
_DEFAULT_HELP_CATEGORY = settings.DEFAULT_HELP_CATEGORY
|
||||||
|
|
||||||
|
|
@ -84,6 +88,7 @@ class FileHelpEntry:
|
||||||
aliases: list
|
aliases: list
|
||||||
help_category: str
|
help_category: str
|
||||||
entrytext: str
|
entrytext: str
|
||||||
|
lock_storage: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def search_index_entry(self):
|
def search_index_entry(self):
|
||||||
|
|
@ -96,6 +101,7 @@ class FileHelpEntry:
|
||||||
"aliases": " ".join(self.aliases),
|
"aliases": " ".join(self.aliases),
|
||||||
"category": self.help_category,
|
"category": self.help_category,
|
||||||
"tags": "",
|
"tags": "",
|
||||||
|
"locks": "",
|
||||||
"text": self.entrytext,
|
"text": self.entrytext,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,6 +111,22 @@ class FileHelpEntry:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<FileHelpEntry {self.key}>"
|
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:
|
class FileHelpStorageHandler:
|
||||||
"""
|
"""
|
||||||
|
|
@ -154,14 +176,15 @@ class FileHelpStorageHandler:
|
||||||
key = dct.get('key').lower().strip()
|
key = dct.get('key').lower().strip()
|
||||||
category = dct.get('category', _DEFAULT_HELP_CATEGORY).strip()
|
category = dct.get('category', _DEFAULT_HELP_CATEGORY).strip()
|
||||||
aliases = list(dct.get('aliases', []))
|
aliases = list(dct.get('aliases', []))
|
||||||
entrytext = dct.get('text')
|
entrytext = dct.get('text', '')
|
||||||
|
locks = dct.get('locks', '')
|
||||||
|
|
||||||
if not key and entrytext:
|
if not key and entrytext:
|
||||||
logger.error(f"Cannot load file-help-entry (missing key or text): {dct}")
|
logger.error(f"Cannot load file-help-entry (missing key or text): {dct}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
unique_help_entries[key] = FileHelpEntry(
|
unique_help_entries[key] = FileHelpEntry(
|
||||||
key=key, help_category=category, aliases=aliases,
|
key=key, help_category=category, aliases=aliases, lock_storage=locks,
|
||||||
entrytext=entrytext)
|
entrytext=entrytext)
|
||||||
|
|
||||||
self.help_entries_dict = unique_help_entries
|
self.help_entries_dict = unique_help_entries
|
||||||
|
|
|
||||||
|
|
@ -114,12 +114,19 @@ class HelpEntry(SharedMemoryModel):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<HelpEntry {self.key}>"
|
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.
|
Determines if another object has permission to access this help entry.
|
||||||
accessing_obj - object trying to access this one
|
|
||||||
access_type - type of access sought
|
Accesses used by default:
|
||||||
default - what to return if no lock of access_type was found
|
'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)
|
return self.locks.check(accessing_obj, access_type=access_type, default=default)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue