Adds better help functionality
- expanded help command, allowing adding, deleting and appending to help the help database - auto-doc functionality using the __doc__ property of commands - markup in help text for creation of multiple help subtopics at once - dynamic help index (use 'help topic' to get the normal mux topic list) /Starkiel
This commit is contained in:
parent
46f35bc574
commit
4cc8e57774
3 changed files with 424 additions and 34 deletions
|
|
@ -15,6 +15,9 @@ settings.CUSTOM_COMMAND_MODULES. Each module imports cmdtable.py and runs
|
||||||
add_command on the command table each command belongs to.
|
add_command on the command table each command belongs to.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from src.helpsys.management.commands.edit_helpfiles import add_help
|
||||||
|
from src.ansi import ansi
|
||||||
|
|
||||||
class CommandTable(object):
|
class CommandTable(object):
|
||||||
"""
|
"""
|
||||||
Stores command tables and performs lookups.
|
Stores command tables and performs lookups.
|
||||||
|
|
@ -26,7 +29,7 @@ class CommandTable(object):
|
||||||
self.ctable = {}
|
self.ctable = {}
|
||||||
|
|
||||||
def add_command(self, command_string, function, priv_tuple=None,
|
def add_command(self, command_string, function, priv_tuple=None,
|
||||||
extra_vals=None):
|
extra_vals=None, auto_help=False, staff_help=False):
|
||||||
"""
|
"""
|
||||||
Adds a command to the command table.
|
Adds a command to the command table.
|
||||||
|
|
||||||
|
|
@ -34,9 +37,30 @@ class CommandTable(object):
|
||||||
function: (reference) The command's function.
|
function: (reference) The command's function.
|
||||||
priv_tuple: (tuple) String tuple of permissions required for command.
|
priv_tuple: (tuple) String tuple of permissions required for command.
|
||||||
extra_vals: (dict) Dictionary to add to the Command object.
|
extra_vals: (dict) Dictionary to add to the Command object.
|
||||||
|
|
||||||
|
Auto-help system:
|
||||||
|
auto_help (bool): If true, automatically creates/replaces a help topic with the
|
||||||
|
same name as the command_string, using the functions's __doc__ property
|
||||||
|
for help text.
|
||||||
|
staff_help (bool): Only relevant if help_auto is activated; It True, makes the
|
||||||
|
help topic (and all eventual subtopics) only visible to staff.
|
||||||
|
|
||||||
|
Note: the auto_help system also supports limited markup. If you divide your __doc__
|
||||||
|
with markers of the form <<TOPIC:MyTopic>>, the system will automatically create
|
||||||
|
separate help topics for each topic. Your initial text (if you define no TOPIC)
|
||||||
|
will still default to the name of your command.
|
||||||
|
You can also custon-set the staff_only flag for individual subtopics by
|
||||||
|
using the markup <<TOPIC:STAFF:MyTopic>> and <<TOPIC:NOSTAFF:MyTopic>>.
|
||||||
"""
|
"""
|
||||||
self.ctable[command_string] = (function, priv_tuple, extra_vals)
|
self.ctable[command_string] = (function, priv_tuple, extra_vals)
|
||||||
|
|
||||||
|
if auto_help:
|
||||||
|
#add automatic help text from the command's doc string
|
||||||
|
topicstr = command_string
|
||||||
|
entrytext = function.__doc__
|
||||||
|
add_help(topicstr, entrytext, staff_only=staff_help,
|
||||||
|
force_create=True, auto_help=True)
|
||||||
|
|
||||||
def get_command_tuple(self, func_name):
|
def get_command_tuple(self, func_name):
|
||||||
"""
|
"""
|
||||||
Returns a reference to the command's tuple. If there are no matches,
|
Returns a reference to the command's tuple. If there are no matches,
|
||||||
|
|
@ -50,4 +74,4 @@ Command tables
|
||||||
# Global command table, for authenticated users.
|
# Global command table, for authenticated users.
|
||||||
GLOBAL_CMD_TABLE = CommandTable()
|
GLOBAL_CMD_TABLE = CommandTable()
|
||||||
# Global unconnected command table, for unauthenticated users.
|
# Global unconnected command table, for unauthenticated users.
|
||||||
GLOBAL_UNCON_CMD_TABLE = CommandTable()
|
GLOBAL_UNCON_CMD_TABLE = CommandTable()
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ from src import defines_global
|
||||||
from src import session_mgr
|
from src import session_mgr
|
||||||
from src import ansi
|
from src import ansi
|
||||||
from src.util import functions_general
|
from src.util import functions_general
|
||||||
|
import src.helpsys.management.commands.edit_helpfiles as edit_help
|
||||||
|
|
||||||
from src.cmdtable import GLOBAL_CMD_TABLE
|
from src.cmdtable import GLOBAL_CMD_TABLE
|
||||||
|
|
||||||
def cmd_password(command):
|
def cmd_password(command):
|
||||||
|
|
@ -138,7 +140,7 @@ def cmd_look(command):
|
||||||
# SCRIPT: Get the item's appearance from the scriptlink.
|
# SCRIPT: Get the item's appearance from the scriptlink.
|
||||||
source_object.emit_to(target_obj.scriptlink.return_appearance(pobject=source_object))
|
source_object.emit_to(target_obj.scriptlink.return_appearance(pobject=source_object))
|
||||||
|
|
||||||
# SCRIPT: Call the object's script's a_desc() method.
|
# SCRIPT: Call the object's script's at_desc() method.
|
||||||
target_obj.scriptlink.at_desc(pobject=source_object)
|
target_obj.scriptlink.at_desc(pobject=source_object)
|
||||||
GLOBAL_CMD_TABLE.add_command("look", cmd_look)
|
GLOBAL_CMD_TABLE.add_command("look", cmd_look)
|
||||||
|
|
||||||
|
|
@ -273,17 +275,20 @@ def cmd_examine(command):
|
||||||
Player is examining an object. Return a full readout of attributes,
|
Player is examining an object. Return a full readout of attributes,
|
||||||
along with detailed information about said object.
|
along with detailed information about said object.
|
||||||
"""
|
"""
|
||||||
|
s = ""
|
||||||
|
newl = "\r\n"
|
||||||
# Format the examine header area with general flag/type info.
|
# Format the examine header area with general flag/type info.
|
||||||
source_object.emit_to(target_obj.get_name(fullname=True))
|
|
||||||
source_object.emit_to("Type: %s Flags: %s" % (target_obj.get_type(),
|
|
||||||
target_obj.get_flags()))
|
|
||||||
source_object.emit_to("Desc: %s" % target_obj.get_description(no_parsing=True))
|
|
||||||
source_object.emit_to("Owner: %s " % (target_obj.get_owner(),))
|
|
||||||
source_object.emit_to("Zone: %s" % (target_obj.get_zone(),))
|
|
||||||
source_object.emit_to("Parent: %s " % target_obj.get_script_parent())
|
|
||||||
|
|
||||||
for attribute in target_obj.get_all_attributes():
|
s += str(target_obj.get_name(fullname=True)) + newl
|
||||||
source_object.emit_to(attribute.get_attrline())
|
s += str("Type: %s Flags: %s" % (target_obj.get_type(),
|
||||||
|
target_obj.get_flags())) + newl
|
||||||
|
s += str("Desc: %s" % target_obj.get_description(no_parsing=True)) + newl
|
||||||
|
s += str("Owner: %s " % target_obj.get_owner()) + newl
|
||||||
|
s += str("Zone: %s" % target_obj.get_zone()) + newl
|
||||||
|
s += str("Parent: %s " % target_obj.get_script_parent()) + newl
|
||||||
|
|
||||||
|
for attribute in target_obj.get_all_attributes():
|
||||||
|
s += str(attribute.get_attrline()) + newl
|
||||||
|
|
||||||
# Contents container lists for sorting by type.
|
# Contents container lists for sorting by type.
|
||||||
con_players = []
|
con_players = []
|
||||||
|
|
@ -301,30 +306,32 @@ def cmd_examine(command):
|
||||||
|
|
||||||
# Render Contents display.
|
# Render Contents display.
|
||||||
if con_players or con_things:
|
if con_players or con_things:
|
||||||
source_object.emit_to("%sContents:%s" % (ansi.ansi["hilite"],
|
s += str("%sContents:%s" % (ansi.ansi["hilite"],
|
||||||
ansi.ansi["normal"],))
|
ansi.ansi["normal"])) + newl
|
||||||
for player in con_players:
|
for player in con_players:
|
||||||
source_object.emit_to('%s' % (player.get_name(fullname=True),))
|
s += str(' %s' % player.get_name(fullname=True)) + newl
|
||||||
for thing in con_things:
|
for thing in con_things:
|
||||||
source_object.emit_to('%s' % (thing.get_name(fullname=True),))
|
s += str(' %s' % thing.get_name(fullname=True)) + newl
|
||||||
|
|
||||||
# Render Exists display.
|
# Render Exists display.
|
||||||
if con_exits:
|
if con_exits:
|
||||||
source_object.emit_to("%sExits:%s" % (ansi.ansi["hilite"],
|
s += str("%sExits:%s" % (ansi.ansi["hilite"],
|
||||||
ansi.ansi["normal"],))
|
ansi.ansi["normal"])) + newl
|
||||||
for exit in con_exits:
|
for exit in con_exits:
|
||||||
source_object.emit_to('%s' %(exit.get_name(fullname=True),))
|
s += str(' %s' % exit.get_name(fullname=True)) + newl
|
||||||
|
|
||||||
# Render the object's home or destination (for exits).
|
# Render the object's home or destination (for exits).
|
||||||
if not target_obj.is_room():
|
if not target_obj.is_room():
|
||||||
if target_obj.is_exit():
|
if target_obj.is_exit():
|
||||||
# The Home attribute on an exit is really its destination.
|
# The Home attribute on an exit is really its destination.
|
||||||
source_object.emit_to("Destination: %s" % (target_obj.get_home(),))
|
s += str("Destination: %s" % target_obj.get_home()) + newl
|
||||||
else:
|
else:
|
||||||
# For everything else, home is home.
|
# For everything else, home is home.
|
||||||
source_object.emit_to("Home: %s" % (target_obj.get_home(),))
|
s += str("Home: %s" % target_obj.get_home()) + newl
|
||||||
# This obviously isn't valid for rooms.
|
# This obviously isn't valid for rooms.
|
||||||
source_object.emit_to("Location: %s" % (target_obj.get_location(),))
|
s += str("Location: %s" % target_obj.get_location()) + newl
|
||||||
|
source_object.emit_to(s)
|
||||||
|
|
||||||
GLOBAL_CMD_TABLE.add_command("examine", cmd_examine)
|
GLOBAL_CMD_TABLE.add_command("examine", cmd_examine)
|
||||||
|
|
||||||
def cmd_quit(command):
|
def cmd_quit(command):
|
||||||
|
|
@ -441,22 +448,151 @@ def cmd_pose(command):
|
||||||
|
|
||||||
source_object.get_location().emit_to_contents(sent_msg)
|
source_object.get_location().emit_to_contents(sent_msg)
|
||||||
GLOBAL_CMD_TABLE.add_command("pose", cmd_pose)
|
GLOBAL_CMD_TABLE.add_command("pose", cmd_pose)
|
||||||
|
|
||||||
|
|
||||||
def cmd_help(command):
|
def cmd_help(command):
|
||||||
"""
|
"""
|
||||||
Help system commands.
|
Help command
|
||||||
|
Usage: help <topic>
|
||||||
|
|
||||||
|
Examples: help index
|
||||||
|
help topic
|
||||||
|
help 2
|
||||||
|
|
||||||
|
Shows the available help on <topic>. Use without <topic> to
|
||||||
|
get the help index. If more than one topic match your query, you will get a
|
||||||
|
list of topics to choose between. You can also supply a help entry number
|
||||||
|
directly if you know it.
|
||||||
|
|
||||||
|
<<TOPIC:STAFF:help_staff>>
|
||||||
|
Help command extra functions for staff:
|
||||||
|
|
||||||
|
help index - the normal index
|
||||||
|
help index_staff - show only help files unique to staff
|
||||||
|
help index_player - show only help files visible to all players
|
||||||
|
|
||||||
|
The help command has a range of staff-only switches for manipulating the
|
||||||
|
help data base:
|
||||||
|
|
||||||
|
help/add <topic>:<text> - add/replace help topic with text (staff only)
|
||||||
|
help/append <topic>:<text> - add text to the end of a topic (staff only)
|
||||||
|
(use the /newline switch to add a new paragraph
|
||||||
|
to your help entry.)
|
||||||
|
help/delete <topic> - delete help topic (staff only)
|
||||||
|
|
||||||
|
Note: further switches are /force and /staff. /force is used together with /add to
|
||||||
|
always create a help entry, also when they partially match a previous entry. /staff
|
||||||
|
makes the help file visible to staff only. The /append switch can be used to change the
|
||||||
|
/staff setting of an existing help file if required.
|
||||||
|
|
||||||
|
The <text> entry supports markup to automatically divide the help text into
|
||||||
|
sub-entries. These are started by the markup < <TOPIC:MyTopic> > (with no spaces
|
||||||
|
between the << >>), which will create a new subsectioned entry 'MyTopic' for all
|
||||||
|
text to follow it. All subsections to be added this way are automatically
|
||||||
|
referred to in the footer of each help entry. Normally the subsections inherit the
|
||||||
|
staff_only flag from the main entry (so if this is a staff-only help, all subentries
|
||||||
|
will also be staff-only and vice versa). You can override this behaviour using the
|
||||||
|
alternate forms < <TOPIC:STAFF:MyTopic> > and < <TOPIC:ALL:MyTopic> >.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
source_object = command.source_object
|
source_object = command.source_object
|
||||||
topicstr = command.command_argument
|
topicstr = command.command_argument
|
||||||
|
switches = command.command_switches
|
||||||
|
|
||||||
if not command.command_argument:
|
if not command.command_argument:
|
||||||
topicstr = "Help Index"
|
#display topic index if just help command is given
|
||||||
|
if not switches:
|
||||||
|
topicstr = "index"
|
||||||
|
else:
|
||||||
|
#avoid applying things to "topic" by mistake
|
||||||
|
source_object.emit_to("You have to supply a topic.")
|
||||||
|
return
|
||||||
|
|
||||||
elif len(topicstr) < 2 and not topicstr.isdigit():
|
elif len(topicstr) < 2 and not topicstr.isdigit():
|
||||||
source_object.emit_to("Your search query is too short. It must be at least three letters long.")
|
#check valid query
|
||||||
|
source_object.emit_to("Your search query must be at least two letters long.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
#speciel help index names. These entries are dynamically
|
||||||
|
#created upon request.
|
||||||
|
if topicstr == 'index':
|
||||||
|
#the normal index, affected by permissions
|
||||||
|
edit_help.get_help_index(source_object)
|
||||||
|
return
|
||||||
|
elif topicstr == 'index_staff':
|
||||||
|
#allows staff to view only staff-specific help
|
||||||
|
edit_help.get_help_index(source_object,filter='staff')
|
||||||
|
return
|
||||||
|
elif topicstr == 'index_player':
|
||||||
|
#allows staff to view only the help files a player sees
|
||||||
|
edit_help.get_help_index(source_object,filter='player')
|
||||||
|
return
|
||||||
|
|
||||||
|
#handle special switches
|
||||||
|
|
||||||
|
force_create = 'for' in switches or 'force' in switches
|
||||||
|
staff_only = 'sta' in switches or 'staff' in switches
|
||||||
|
|
||||||
|
if 'add' in switches:
|
||||||
|
#try to add/replace help text for a command
|
||||||
|
if not source_object.is_staff():
|
||||||
|
source_object.emit_to("Only staff can add new help entries.")
|
||||||
|
return
|
||||||
|
spl = (topicstr.split(':',1))
|
||||||
|
if len(spl) != 2:
|
||||||
|
source_object.emit_to("Format is help/add <topic>:<helptext>")
|
||||||
|
return
|
||||||
|
topicstr = spl[0]
|
||||||
|
text = spl[1]
|
||||||
|
topics = edit_help.add_help(topicstr,text,staff_only,force_create,source_object)
|
||||||
|
if not topics:
|
||||||
|
source_object.emit_to("No topic(s) added due to errors. Check syntax and that you don't have duplicate subtopics with the same name defined.")
|
||||||
|
return
|
||||||
|
elif len(topics)>1:
|
||||||
|
source_object.emit_to("Added or replaced multiple help entries.")
|
||||||
|
else:
|
||||||
|
source_object.emit_to("Added or replaced help entry for %s." % topicstr )
|
||||||
|
|
||||||
|
elif 'append' in switches or 'app' in switches:
|
||||||
|
#append text to a help entry
|
||||||
|
if not source_object.is_staff():
|
||||||
|
source_object.emit_to("Only staff can append to help entries.")
|
||||||
|
return
|
||||||
|
spl = (topicstr.split(':',1))
|
||||||
|
if len(spl) != 2:
|
||||||
|
source_object.emit_to("""Format is help/append <topic>:<text to add>
|
||||||
|
Use the /newline switch to make a new paragraph.""")
|
||||||
|
return
|
||||||
|
topicstr = spl[0]
|
||||||
|
text = spl[1]
|
||||||
|
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
|
||||||
|
if len(topics) == 1:
|
||||||
|
newtext = topics[0].get_entrytext_ingame()
|
||||||
|
if 'newl' in switches or 'newline' in switches:
|
||||||
|
newtext += "\n\r\n\r%s" % text
|
||||||
|
else:
|
||||||
|
newtext += "\n\r%s" % text
|
||||||
|
topics = edit_help.add_help(topicstr,newtext,staff_only,force_create,source_object)
|
||||||
|
if topics:
|
||||||
|
source_object.emit_to("Appended text to help entry for %s." % topicstr)
|
||||||
|
|
||||||
|
elif 'del' in switches or 'delete' in switches:
|
||||||
|
#delete a help entry
|
||||||
|
if not source_object.is_staff():
|
||||||
|
source_object.emit_to("Only staff can add delete help entries.")
|
||||||
|
return
|
||||||
|
topics = edit_help.del_help(source_object,topicstr)
|
||||||
|
if type(topics) != type(list()):
|
||||||
|
source_object.emit_to("Help entry '%s' deleted." % topicstr)
|
||||||
|
return
|
||||||
|
|
||||||
|
else:
|
||||||
|
#no switch; just try to get the help as normal
|
||||||
|
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
|
||||||
|
|
||||||
topics = HelpEntry.objects.find_topicmatch(source_object, topicstr)
|
#display help entry or handle no/multiple matches
|
||||||
|
|
||||||
if len(topics) == 0:
|
if len(topics) == 0:
|
||||||
source_object.emit_to("No matching topics found, please refine your search.")
|
source_object.emit_to("No matching topics found, please refine your search.")
|
||||||
suggestions = HelpEntry.objects.find_topicsuggestions(source_object,
|
suggestions = HelpEntry.objects.find_topicsuggestions(source_object,
|
||||||
|
|
@ -464,14 +600,14 @@ def cmd_help(command):
|
||||||
if len(suggestions) > 0:
|
if len(suggestions) > 0:
|
||||||
source_object.emit_to("Matching similarly named topics:")
|
source_object.emit_to("Matching similarly named topics:")
|
||||||
for result in suggestions:
|
for result in suggestions:
|
||||||
source_object.emit_to(" %s" % (result,))
|
source_object.emit_to(" %s" % (result,))
|
||||||
source_object.emit_to("You may type 'help <#>' to see any of these topics.")
|
source_object.emit_to("You may type 'help <#>' to see any of these topics.")
|
||||||
elif len(topics) > 1:
|
elif len(topics) > 1:
|
||||||
source_object.emit_to("More than one match found:")
|
source_object.emit_to("More than one match found:")
|
||||||
for result in topics:
|
for result in topics:
|
||||||
source_object.emit_to("%3d. %s" % (result.id, result.get_topicname()))
|
source_object.emit_to(" %3d. %s" % (result.id, result.get_topicname()))
|
||||||
source_object.emit_to("You may type 'help <#>' to see any of these topics.")
|
source_object.emit_to("You may type 'help <#>' to see any of these topics.")
|
||||||
else:
|
else:
|
||||||
topic = topics[0]
|
topic = topics[0]
|
||||||
source_object.emit_to("\n\r"+ topic.get_entrytext_ingame())
|
source_object.emit_to("\n\r "+ topic.get_entrytext_ingame())
|
||||||
GLOBAL_CMD_TABLE.add_command("help", cmd_help)
|
GLOBAL_CMD_TABLE.add_command("help", cmd_help, auto_help=True)
|
||||||
|
|
|
||||||
230
src/helpsys/management/commands/edit_helpfiles.py
Normal file
230
src/helpsys/management/commands/edit_helpfiles.py
Normal file
|
|
@ -0,0 +1,230 @@
|
||||||
|
"""
|
||||||
|
Support commands for a more advanced help system.
|
||||||
|
Allows adding help to the data base from inside the mud as
|
||||||
|
well as creating auto-docs of commands based on their doc strings.
|
||||||
|
The system supports help-markup for multiple help entries as well
|
||||||
|
as a dynamically updating help index.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from src.helpsys.models import HelpEntry
|
||||||
|
from src.ansi import ansi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Helper functions
|
||||||
|
#
|
||||||
|
|
||||||
|
def _privileged_help_search(topicstr):
|
||||||
|
"""
|
||||||
|
searches the topic data base without needing to know who calls it. Needed
|
||||||
|
for autohelp functionality. Will show all help entries, also those set to staff
|
||||||
|
only.
|
||||||
|
"""
|
||||||
|
if topicstr.isdigit():
|
||||||
|
t_query = HelpEntry.objects.filter(id=topicstr)
|
||||||
|
else:
|
||||||
|
exact_match = HelpEntry.objects.filter(topicname__iexact=topicstr)
|
||||||
|
if exact_match:
|
||||||
|
t_query = exact_match
|
||||||
|
else:
|
||||||
|
t_query = HelpEntry.objects.filter(topicname__istartswith=topicstr)
|
||||||
|
return t_query
|
||||||
|
|
||||||
|
|
||||||
|
def _create_help(topicstr, entrytext, staff_only=False, force_create=False,
|
||||||
|
pobject=None, noauth=False):
|
||||||
|
"""
|
||||||
|
Add a help entry to the database, replace an old one if it exists.
|
||||||
|
|
||||||
|
Note - noauth=True will bypass permission checks, so do not use this from
|
||||||
|
inside mud, it is needed by the autohelp system only.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if noauth:
|
||||||
|
#do not check permissions (for autohelp)
|
||||||
|
topic = _privileged_help_search(topicstr)
|
||||||
|
elif pobject:
|
||||||
|
#checks access rights before searching (this should have been
|
||||||
|
#done already at the command level)
|
||||||
|
if not pobject.is_staff(): return []
|
||||||
|
topic = HelpEntry.objects.find_topicmatch(pobject, topicstr)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if len(topic) == 1:
|
||||||
|
#replace an old help file
|
||||||
|
topic = topic[0]
|
||||||
|
topic.entrytext = entrytext
|
||||||
|
topic.staff_only = staff_only
|
||||||
|
topic.save()
|
||||||
|
return [topic]
|
||||||
|
elif len(topic) > 1 and not force_create:
|
||||||
|
#a partial match, return it for inspection.
|
||||||
|
return topic
|
||||||
|
else:
|
||||||
|
#we have a new topic - create a new help object
|
||||||
|
new_entry = HelpEntry(topicname=topicstr,
|
||||||
|
entrytext=entrytext,
|
||||||
|
staff_only=staff_only)
|
||||||
|
new_entry.save()
|
||||||
|
return [new_entry]
|
||||||
|
|
||||||
|
def _handle_help_markup(topicstr, entrytext, staff_only, identifier="<<TOPIC:"):
|
||||||
|
"""
|
||||||
|
Handle help markup in order to split help into subsections.
|
||||||
|
Handles markup of the form <<TOPIC:STAFF:TopicTitle>> and
|
||||||
|
<<TOPIC:ALL:TopicTitle>> to override the staff_only flag on a per-subtopic
|
||||||
|
basis.
|
||||||
|
"""
|
||||||
|
topic_list = entrytext.split(identifier)
|
||||||
|
topic_dict = {}
|
||||||
|
staff_dict = {}
|
||||||
|
for txt in topic_list:
|
||||||
|
txt = txt.strip()
|
||||||
|
if txt.count('>>'):
|
||||||
|
topic, text = txt.split('>>',1)
|
||||||
|
text = text.strip()
|
||||||
|
topic = topic.lower()
|
||||||
|
|
||||||
|
if topic in topic_dict.keys():
|
||||||
|
#do not allow multiple topics of the same name
|
||||||
|
return {}, []
|
||||||
|
if 'all:' in topic:
|
||||||
|
topic = topic[4:]
|
||||||
|
staff_dict[topic] = False
|
||||||
|
elif 'staff:' in topic:
|
||||||
|
topic = topic[6:]
|
||||||
|
staff_dict[topic] = True
|
||||||
|
else:
|
||||||
|
staff_dict[topic] = staff_only
|
||||||
|
topic_dict[topic] = text
|
||||||
|
else:
|
||||||
|
#no markup, just add the entry as-is
|
||||||
|
topic = topicstr.lower()
|
||||||
|
topic_dict[topic] = '\r\n' + txt
|
||||||
|
staff_dict[topic] = staff_only
|
||||||
|
return topic_dict, staff_dict
|
||||||
|
|
||||||
|
def _format_footer(top, text, topic_dict, staff_dict):
|
||||||
|
"""
|
||||||
|
Formats the subtopic with a 'Related Topics:' footer. If mixed
|
||||||
|
staff-only flags are set, those help entries without the staff-only flag
|
||||||
|
will not see staff-only help files recommended in the footer. This allows
|
||||||
|
to separate out the staff-only help switches etc into a separate
|
||||||
|
help file so as not to confuse normal players.
|
||||||
|
"""
|
||||||
|
if text:
|
||||||
|
#only include non-staff related footers to non-staff commands
|
||||||
|
if staff_dict[top]:
|
||||||
|
other_topics = other_topics = filter(lambda o: o != top, topic_dict.keys())
|
||||||
|
else:
|
||||||
|
other_topics = other_topics = filter(lambda o: o != top and not staff_dict[o],
|
||||||
|
topic_dict.keys())
|
||||||
|
if other_topics:
|
||||||
|
footer = ansi['normal'] + "\n\r Related Topics: "
|
||||||
|
for t in other_topics:
|
||||||
|
footer += t + ', '
|
||||||
|
footer = footer[:-2] + '.'
|
||||||
|
return text + footer
|
||||||
|
else:
|
||||||
|
return text
|
||||||
|
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#
|
||||||
|
# Access functions
|
||||||
|
#
|
||||||
|
|
||||||
|
def add_help(topicstr, entrytext, staff_only=False, force_create=False,
|
||||||
|
pobject=None, auto_help=False):
|
||||||
|
"""
|
||||||
|
Add a help topic to the database. This is also usable by autohelp, with auto=True.
|
||||||
|
|
||||||
|
Allows <<TOPIC:TopicTitle>> markup in the help text, to automatically spawn
|
||||||
|
subtopics. For creating mixed staff/ordinary subtopics, the <<TOPIC:STAFF:TopicTitle>> and
|
||||||
|
<<TOPIC:ALL:TopicTitle>> commands can override the overall staff_only setting for
|
||||||
|
that entry only.
|
||||||
|
"""
|
||||||
|
identifier = '<<TOPIC:'
|
||||||
|
if identifier in entrytext:
|
||||||
|
#There is markup in the entry, so we should split the doc into separate subtopics
|
||||||
|
topic_dict, staff_dict = _handle_help_markup(topicstr, entrytext,
|
||||||
|
staff_only, identifier)
|
||||||
|
topics = []
|
||||||
|
for topic, text in topic_dict.items():
|
||||||
|
|
||||||
|
#format with nice footer
|
||||||
|
entry = _format_footer(topic, text, topic_dict, staff_dict)
|
||||||
|
|
||||||
|
if entry:
|
||||||
|
#create the subtopic
|
||||||
|
newtopic = _create_help(topic, entry,staff_only=staff_dict[topic],
|
||||||
|
force_create=force_create,pobject=pobject,noauth=auto_help)
|
||||||
|
topics.extend(newtopic)
|
||||||
|
return topics
|
||||||
|
|
||||||
|
elif entrytext:
|
||||||
|
#if there were no topic sections, just create the help entry as normal
|
||||||
|
return _create_help(topicstr.lower(),entrytext,staff_only=staff_only,
|
||||||
|
force_create=force_create,pobject=pobject,noauth=auto_help)
|
||||||
|
|
||||||
|
def del_help(pobject,topicstr):
|
||||||
|
"""
|
||||||
|
Delete a help entry from the data base.
|
||||||
|
|
||||||
|
Note that it makes no sense to delete auto-added help entries this way since
|
||||||
|
they will just be re-added on the next @reload. Delete such entries by turning
|
||||||
|
off their auto-help functionality first.
|
||||||
|
"""
|
||||||
|
#find topic with permission checks
|
||||||
|
if not pobject.is_staff(): return []
|
||||||
|
topic = HelpEntry.objects.find_topicmatch(pobject, topicstr)
|
||||||
|
if topic:
|
||||||
|
if len(topic) == 1:
|
||||||
|
#delete topic
|
||||||
|
topic.delete()
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return topic
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_help_index(pobject,filter=None):
|
||||||
|
"""
|
||||||
|
Dynamically builds a help index depending on who asks for it, so
|
||||||
|
normal players won't see staff-only help files, for example.
|
||||||
|
|
||||||
|
The filter parameter allows staff to limit their view of the help index
|
||||||
|
no filter (default) - view all help files, staff and non-staff
|
||||||
|
filter='staff' - view only staff-specific help files
|
||||||
|
filter='player' - view only those files visible to all
|
||||||
|
"""
|
||||||
|
if pobject.is_staff():
|
||||||
|
if filter == 'staff':
|
||||||
|
helpentries = HelpEntry.objects.filter(staff_only=True).order_by('topicname')
|
||||||
|
elif filter == 'player':
|
||||||
|
helpentries = HelpEntry.objects.filter(staff_only=False).order_by('topicname')
|
||||||
|
else:
|
||||||
|
helpentries = HelpEntry.objects.all().order_by('topicname')
|
||||||
|
else:
|
||||||
|
helpentries = HelpEntry.objects.filter(staff_only=False).order_by('topicname')
|
||||||
|
|
||||||
|
if not helpentries:
|
||||||
|
pobject.emit_to("No help entries found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
topics = [entry.topicname for entry in helpentries]
|
||||||
|
#format help entries into suitable alphabetized collumns.
|
||||||
|
percollumn = 8
|
||||||
|
s = ""
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
i += 1
|
||||||
|
try:
|
||||||
|
top = topics.pop(0)
|
||||||
|
s+= " %s " % top
|
||||||
|
if i%percollumn == 0: s += '\n\r'
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
s += " (%i entries)" % (i-1)
|
||||||
|
pobject.emit_to(s)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue