##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import Acquisition
from OFS.SimpleItem import Item
from OFS.ObjectManager import ObjectManager
from Globals import Persistent, DTMLFile, HTML
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import access_contents_information
from AccessControl.Permissions import add_documents_images_and_files
from AccessControl.Permissions import view as View
from Products.ZCatalog.ZCatalog import ZCatalog
from Products.ZCatalog.Lazy import LazyCat
from cgi import escape
import Products
import HelpTopic
class HelpSys(Acquisition.Implicit, ObjectManager, Item, Persistent):
"""
Zope Help System
Provides browsing and searching of Zope Product Help.
"""
meta_type='Help System'
security = ClassSecurityInfo()
security.declareObjectProtected(View)
manage_options=(
{'label' : 'Contents', 'action' : 'menu'},
{'label' : 'Search', 'action' : 'search'},
)
def __init__(self, id='HelpSys'):
self.id=id
security.declareProtected(access_contents_information, 'helpValues')
def helpValues(self, spec=None):
"ProductHelp objects of all Products that have help"
hv=[]
for product in self.Control_Panel.Products.objectValues():
productHelp=product.getProductHelp()
# only list products that actually have help
if productHelp.helpValues():
hv.append(productHelp)
return hv
# Seaching does an aggregated search of all ProductHelp
# objects. Only Help Topics for which the user has permissions
# are returned.
security.declareProtected(View, '__call__')
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)
security.declareProtected(View, 'searchResults')
searchResults=__call__
security.declareProtected(View, 'index_html')
index_html=DTMLFile('dtml/frame', globals())
security.declareProtected(View, 'menu')
menu=DTMLFile('dtml/menu', globals())
security.declareProtected(View, 'search')
search=DTMLFile('dtml/search', globals())
security.declareProtected(View, 'results')
results=DTMLFile('dtml/results', globals())
security.declareProtected(View, 'main')
main=HTML("""""")
standard_html_header=DTMLFile('dtml/menu_header', globals())
standard_html_footer=DTMLFile('dtml/menu_footer', globals())
button=DTMLFile('dtml/button', globals())
security.declareProtected(View, 'HelpButton')
def HelpButton(self, topic, product):
"""
Insert a help button linked to a help topic.
"""
return self.button(self, self.REQUEST, product=product, topic=topic)
helpURL=DTMLFile('dtml/helpURL',globals())
security.declareProtected(View, 'helpLink')
def helpLink(self, product='OFSP', topic='ObjectManager_Contents.stx'):
# Generate an tag linking to a help topic. This
# is a little lighter weight than the help button approach.
basepath=self.REQUEST['BASEPATH1']
help_url='%s/Control_Panel/Products/%s/Help/%s' % (
basepath,
product,
topic
)
help_url='%s?help_url=%s' % (self.absolute_url(), help_url)
script="window.open('%s','zope_help','width=600,height=500," \
"menubar=yes,toolbar=yes,scrollbars=yes,resizable=yes');" \
"return false;" % escape(help_url, 1).replace("'", "\\'")
h_link='Help!' % (
escape(help_url, 1), script
)
return h_link
def tpValues(self):
"""
Tree protocol - returns child nodes
Aggregates Product Helps with the same title.
"""
helps={}
for help in self.helpValues():
if helps.has_key(help.title):
helps[help.title].append(help)
else:
helps[help.title]=[help]
cols=[]
for k,v in helps.items():
cols.append(TreeCollection(k,v,0))
return cols
InitializeClass(HelpSys)
class TreeCollection:
"""
A temporary wrapper for a collection of objects
objects, used for help topic browsing to make a collection
of objects appear as a single object.
"""
def __init__(self, id, objs, simple=1):
self.id=self.title=id
self.objs=objs
self.simple=simple
def tpValues(self):
values=[]
if self.simple:
values=self.objs
else:
for obj in self.objs:
values=values + list(obj.tpValues())
# resolve overlap
ids={}
for value in values:
if ids.has_key(value.id):
ids[value.id].append(value)
else:
ids[value.id]=[value]
results=[]
for k,v in ids.items():
if len(v)==1:
results.append(v[0])
else:
values=[]
for topic in v:
values=values + list(topic.tpValues())
results.append(TreeCollection(k, values))
results.sort(lambda x, y: cmp(x.id, y.id))
return results
def tpId(self):
return self.id
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'
icon='p_/ProductHelp_icon'
security = ClassSecurityInfo()
lastRegistered=None
meta_types=({'name':'Help Topic',
'action':'addTopicForm',
'permission':'Add Documents, Images, and Files'},
)
manage_options=(
ObjectManager.manage_options +
Item.manage_options
)
def __init__(self, id='Help', title=''):
self.id=id
self.title=title
c=self.catalog=ZCatalog('catalog')
# clear catalog
for index in c.indexes():
c.delIndex(index)
for col in c.schema():
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')
security.declareProtected(add_documents_images_and_files, 'addTopicForm')
addTopicForm=DTMLFile('dtml/addTopic', globals())
security.declareProtected(add_documents_images_and_files, 'addTopic')
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 tpValues(self):
"""
Tree protocol - child nodes
"""
topics=[]
apitopics=[]
dtmltopics=[]
zpttopics=[]
for topic in self.objectValues('Help Topic'):
if hasattr(topic,'isAPIHelpTopic') and topic.isAPIHelpTopic:
apitopics.append(topic)
else:
try:
if callable(topic.id):
id=topic.id()
else:
id=topic.id
if id[:5]=='dtml-':
dtmltopics.append(topic)
if (id[:5] in ('metal', 'tales') and id[5] in ('.', '-')) or \
(id[:3]=='tal' and id[3] in ('.', '-')):
zpttopics.append(topic)
else:
topics.append(topic)
except ImportError:
# Don't blow up if we have references to non-existant
# products laying around
pass
if dtmltopics:
topics = topics + [TreeCollection(' DTML Reference', dtmltopics)]
if apitopics:
topics = topics + [TreeCollection(' API Reference', apitopics)]
if zpttopics:
topics = topics + [TreeCollection(' ZPT Reference', zpttopics)]
return 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=DTMLFile('dtml/topic_header', globals())
standard_html_footer=DTMLFile('dtml/topic_footer', globals())
InitializeClass(ProductHelp)