Commit aa3ed4ff authored by Amos Latteier's avatar Amos Latteier

First cut at a context sensitive online help system.

parent fa9176be
import Acquisition
from OFS.SimpleItem import Item
from OFS.ObjectManager import ObjectManager
from Globals import Persistent, HTMLFile, HTML
from Products.ZCatalog.ZCatalog import ZCatalog
from Products.ZCatalog.Lazy import LazyCat
import Products
import HelpTopic
class HelpSystem(Acquisition.Implicit, ObjectManager, Item, Persistent):
"""
Zope Help System
Provides browsing and searching of Zope Product Help.
"""
meta_type='Help System'
manage_options=(
{'label' : 'Contents', 'action' : 'menu'},
{'label' : 'Search', 'action' : 'search'},
)
__ac_permissions__=(
('View',
('__call__', 'searchResults', 'HelpButton', '',
'index_html', 'menu', 'search', 'results', 'main', )),
('Access contents information', ('helpValues',)),
)
def __init__(self, id):
self.id=id
def helpValues(self, spec=None):
"Help topics"
hv=[]
for Product in self.Control_Panel.Products.objectValues():
hv.append(Product.Help)
return hv
# Seaching does an aggregated search of all ProductHelp
# objects. Only Help Topics for which the user has permissions
# are returned.
def __call__(self, REQUEST=None, **kw):
"Searchable interface"
if REQUEST is not None:
perms=[]
user=REQUEST.AUTHENTICATED_USER
for p in self.ac_inherited_permissions():
if user.has_permission(p[0], self):
perms.append(p[0])
REQUEST.set('permissions',perms)
results=[]
for ph in self.helpValues():
results.append(apply(getattr(ph, '__call__'), (REQUEST,) , kw))
return LazyCat(results)
searchResults=__call__
index_html=HTMLFile('frame', globals())
menu=HTMLFile('menu', globals())
search=HTMLFile('search', globals())
results=HTMLFile('results', globals())
main=HTML("""<html></html>""")
button=HTMLFile('button', globals())
def HelpButton(self, topic, product='OFSP'):
"""
Insert a help button linked to a help topic.
"""
return self.button(self, product=product, topic=topic)
class ProductHelp(Acquisition.Implicit, ObjectManager, Item, Persistent):
"""
Manages a collection of Help Topics for a given Product.
Provides searching services to HelpSystem.
"""
meta_type='Product Help'
meta_types=({'name':'Help Topic',
'action':'addTopicForm',
'permission':'Add Documents, Images, and Files'},
)
manage_options=(
{'label':'Contents', 'action':'manage_main'},
{'label':'Import/Export', 'action':'manage_importExportForm'},
{'label':'Undo', 'action':'manage_UndoForm'},
)
__ac_permissions__=(
('Add Documents, Files, and Images', ('addTopicForm', 'addTopic')),
)
def __init__(self, id='Help', title=''):
self.id=id
self.title=title
self.catalog=ZCatalog('catalog')
c=self.catalog._catalog
# clear catalog
for index in c.indexes.keys():
c.delIndex(index)
for col in c.schema.keys():
c.delColumn(col)
c.addIndex('SearchableText', 'TextIndex')
c.addIndex('categories', 'KeywordIndex')
c.addIndex('permissions', 'KeywordIndex')
c.addColumn('categories')
c.addColumn('permissions')
c.addColumn('title_or_id')
c.addColumn('url')
c.addColumn('id')
addTopicForm=HTMLFile('addTopic', globals())
def addTopic(self, id, title, REQUEST=None):
"Add a Help Topic"
topic=HelpTopic.DTMLDocumentTopic(
HelpTopic.default_topic_content, __name__=id)
topic.title=title
self._setObject(id, topic)
if REQUEST is not None:
return self.manage_main(self, REQUEST,
manage_tabs_message='Help Topic added.')
def helpValues(self, REQUEST=None):
"""
Lists contained Help Topics.
Help Topics for which the user is not authorized
are not listed.
"""
topics=self.objectValues('Help Topic')
if REQUEST is None:
return topics
return filter(
lambda ht, u=REQUEST.AUTHENTICATED_USER: ht.authorized(u), topics)
def all_meta_types(self):
f=lambda x: x['name'] in ('Image', 'File')
return filter(f, Products.meta_types) + self.meta_types
def __call__(self, *args, **kw):
"""
Searchable interface
"""
return apply(self.catalog.__call__, args, kw)
standard_html_header=HTMLFile('topic_header', globals())
standard_html_footer=HTMLFile('topic_footer', globals())
import Acquisition
from ComputedAttribute import ComputedAttribute
from OFS.SimpleItem import Item
from Globals import Persistent, HTML, HTMLFile, ImageFile
from OFS.DTMLDocument import DTMLDocument
from OFS.PropertyManager import PropertyManager
import os.path
import string
class HelpTopicBase:
"Mix-in Help Topic support class"
_properties=(
{'id':'title', 'type':'string', 'mode':'w'},
{'id':'categories', 'type':'multiple selection',
'select_variable':'categories_values', 'mode':'w'},
{'id':'permissions', 'type':'multiple selection',
'select_variable':'permissions_values', 'mode':'w'},
)
# default values
categories=('Content Manager Information',)
permissions=('View',)
def _permissions_values(self):
perms=[]
for m in self.permission_settings():
perms.append(m['name'])
return perms
permissions_values=ComputedAttribute(_permissions_values, 1)
categories_values=(
'Content Manager Information',
'DTML Programmer Information',
'Python Programmer Information',
)
def helpValues(self, REQUEST=None):
return ()
def authorized(self, user):
"Is a given user authorized to view this Help Topic?"
if not self.permissions:
return 1
for perm in self.permissions:
if user.has_permission(perm, self):
return 1
return 0
# Indexable methods
# -----------------
def SearchableText(self):
"The full text of the Help Topic, for indexing purposes"
raise "Unimplemented"
def url(self):
"URL for indexing purposes"
return self.absolute_url(1)
# Private indexing methods
# ------------------------
def manage_afterAdd(self, item, container):
self.index_object()
def manage_afterClone(self, item):
self.index_object()
def manage_beforeDelete(self, item, container):
self.unindex_object()
def _setPropValue(self, id, value):
setattr(self,id,value)
self.reindex_object()
def index_object(self, prefix=''):
self.get_catalog().catalog_object(self, prefix + self.url())
def unindex_object(self, prefix=''):
self.get_catalog().uncatalog_object(prefix + self.url())
def reindex_object(self):
self.unindex_object()
self.index_object()
def get_catalog(self):
return self.catalog
class HelpTopic(Acquisition.Implicit, HelpTopicBase, Item, PropertyManager, Persistent):
"""
Abstract base class for Help Topics
"""
meta_type='Help Topic'
manage_options=(
{'label':'Properties', 'action':'manage_propertiesForm'},
{'label':'View', 'action':'index_html'},
)
__ac_permissions__=()
def index_html(self, REQUEST, RESPONSE):
"View the Help Topic"
raise "Unimplemented"
class DTMLDocumentTopic(HelpTopicBase, DTMLDocument):
"""
A user addable Help Topic based on DTML Document.
"""
meta_type='Help Topic'
def munge(self,*args, **kw):
apply(DTMLDocument.munge, (self,) + args, kw)
self.reindex_object()
def SearchableText(self):
return '%s %s' % (self.title, self.read())
default_topic_content="""\
<dtml-var standard_html_header>
<h2><dtml-var title></h2>
<p>This is the <dtml-var id> Help Topic.</p>
<dtml-var standard_html_footer>
"""
class DTMLTopic(HelpTopic):
"""
A basic Help Topic. Holds a HTMLFile object.
"""
def __init__(self, id, title, file, permissions=None, categories=None):
self.id=id
self.title=title
if string.rfind(file, '.dtml') == len(file) -5:
file=file[:-5]
self.index_html=HTMLFile(file,'')
if permissions is not None:
self.permissions=permissions
if categories is not None:
self.categories=categories
def SearchableText(self):
"The full text of the Help Topic, for indexing purposes"
return '%s %s' % (self.title, self.obj.read())
class TextTopic(HelpTopic):
"""
A basic Help Topic. Holds a text file.
"""
def __init__(self, id, title, file, permissions=None, categories=None):
self.id=id
self.title=title
self.obj=open(file).read()
if permissions is not None:
self.permissions=permissions
if categories is not None:
self.categories=categories
def index_html(self, REQUEST=None):
"View the Help Topic"
return self.obj
def SearchableText(self):
"The full text of the Help Topic, for indexing purposes"
return '%s %s' % (self.title, self.obj)
class STXTopic(TextTopic):
"""
A structured-text topic. Holds a HTMLFile object.
"""
index_html=HTML("""\
<dtml-var standard_html_header>
<dtml-var obj fmt="structured-text">
<dtml-var standard_html_footer>""")
class ImageTopic(HelpTopic):
"""
A image Help Topic. Holds an ImageFile object.
"""
meta_type='Help Image'
def __init__(self, id, title, file, categories=None, permissions=None):
self.id=id
self.title=title
dir, file=os.path.split(file)
self.image=ImageFile(file, dir)
if permissions is not None:
self.permissions=permissions
if categories is not None:
self.categories=categories
def index_html(self, REQUEST, RESPONSE):
"View the Help Topic"
return self.image.index_html(REQUEST, RESPONSE)
def SearchableText(self):
"The full text of the Help Topic, for indexing purposes"
return ''
<html>
<head><title>Add Help Topic</title></head>
<body>
<h2>Add Help Topic</h2>
<form action="addTopic" method="post">
<table>
<tr valign="top">
<th>Id</th>
<td><input type="text" name="id"></td>
</tr>
<tr valign="top">
<th>Title</th>
<td><input type="text" name="title"></td>
</tr>
<tr valign="top">
<td colspan="2"><input type="submit" value="Add"></td>
</tr>
</table>
</form>
</body>
</html>
<div align="right">
<font face="Verdana, Arial, Helvetica" size="1">
<form action="<dtml-var absolute_url>" target="zope_help" method="post">
<dtml-if "_.getitem('product') and _.getitem('topic')">
<input type="hidden" name="help_url" value="/Control_Panel/Products/&dtml-product;/Help/&dtml-topic;">
</dtml-if>
<input type="submit" value=" Help ">
</form>
</font>
</div>
\ No newline at end of file
<HTML>
<HEAD>
<TITLE>Zope Help System</TITLE>
</HEAD>
<FRAMESET COLS="225,*">
<FRAME SRC="menu" NAME="help_menu"
MARGINWIDTH="2" MARGINHEIGHT="2" SCROLLING="auto">
<FRAME
<dtml-if "_.has_key('help_url')">
SRC="<dtml-var help_url>"
<dtml-else>
SRC="main"
</dtml-if>
NAME="help_main" MARGINWIDTH="2" MARGINHEIGHT="0" SCROLLING="auto">
</FRAMESET>
<NOFRAMES>
Management interfaces require the use of a <B>frames-capable</B> web browser.
</NOFRAMES>
</HTML>
\ No newline at end of file
<dtml-var standard_html_header>
<dtml-call "REQUEST.set('MANAGE_TABS_NO_BANNER',1)">
<dtml-var manage_tabs>
<h2>Contents</h2>
<dtml-tree branches_expr="helpValues(REQUEST)">
<dtml-if "meta_type=='Product Help'">
<dtml-var title_or_id>
<dtml-else>
<a href="<dtml-var absolute_url>" target="help_main"><dtml-var title_or_id></a>
</dtml-if>
</dtml-tree>
<dtml-var standard_html_footer>
\ No newline at end of file
<dtml-var standard_html_header>
<dtml-call "REQUEST.set('MANAGE_TABS_NO_BANNER',1)">
<dtml-var manage_tabs>
<h2>Search Results</h2>
<p>
<dtml-in "searchResults(REQUEST)">
<a href="<dtml-var SCRIPT_NAME><dtml-var url>" target="help_main"><dtml-var title_or_id></a><br>
<dtml-else>
<em>No help topics were found matching <b><dtml-var SearchableText></b>.</em>
</dtml-in>
</p>
<dtml-var standard_html_footer>
\ No newline at end of file
<dtml-var standard_html_header>
<dtml-call "REQUEST.set('MANAGE_TABS_NO_BANNER',1)">
<dtml-var manage_tabs>
<h2>Search</h2>
<p>
<form action="results" method="GET">
Search terms
<input type="text" name="SearchableText" size="16">
<input type="submit" value=" Search ">
</form>
</p>
<dtml-var standard_html_footer>
\ No newline at end of file
<html>
<head>
<title><dtml-var title></title>
<style type="text/css">
<!--
h1, h2, h3, h4, h5, h6{
font-family: Verdana, Arial, Helvetica, san-serif;
}
.instructions{
background: #FFFFAA;
border-width: thin;
border-style: solid;
padding: 10pt;
}
.explanation{
border-width: thin;
border-style: solid;
padding: 10pt;
}
-->
</style>
</head>
<body>
<table width="100%">
<tr valign="top">
<td>Help Categories:
<dtml-in categories>
<dtml-var sequence-item><dtml-unless sequence-end>,</dtml-unless>
</dtml-in>
</td>
<td>Help Permissions:
<dtml-in permissions>
<dtml-var sequence-item><dtml-unless sequence-end>,</dtml-unless>
</dtml-in>
</td>
</tr>
</table>
<dtml-if title><h2><dtml-var title></h2></dtml-if>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment