############################################################################## # # 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)