Logo Search packages:      
Sourcecode: zope-backtalk version File versions

BackTalk.py

##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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
# 
##############################################################################
"""
Revision information:
$Id: BackTalk.py,v 1.18 2002/08/09 16:34:58 chrism Exp $
"""
import Globals
import os, string, urllib, re, sys
from OFS.DTMLDocument import DTMLDocument
from OFS.Folder import Folder
from OFS.ObjectManager import BadRequestException
from AccessControl import getSecurityManager, ClassSecurityInfo
from DateTime import DateTime
import Interface
import BackTalkBase, CommentableDocument, HTMLClass
import Retrievers
from App.ImageFile import ImageFile
from StructuredText import ST
from Acquisition import aq_inner, aq_parent, aq_base, Implicit
from cgi import escape
import zLOG
import cStringIO
from webdav.WriteLockInterface import WriteLockInterface

addDocumentForm=Globals.DTMLFile('dtml/methodAdd', globals())
addBookForm=Globals.DTMLFile('dtml/folderAdd', globals())

package_home = Globals.package_home(globals())
package_home = os.path.join(package_home, 'dtml')
template = os.path.join(package_home, 'rendering_template.dtml')
template = open(template).read()
comment_template = os.path.join(package_home, 'comment_template.dtml')
comment_template = open(comment_template).read()
_marker = []
bad_id=re.compile(r'[^a-zA-Z0-9-_~,.$\(\)# ]').search

MGMT_SCREEN = 'View management screens'
VIEW = 'View'
CREATE_PDFS = 'Create BackTalk PDFs'
USE_MAILHOST = 'Use mailhost services'
CHANGE = 'Change BackTalkComments'
VIEW_SOURCE = 'View BackTalk Source'
COMMENT = 'Add BackTalk Comments'

class BTNavigableInterface(Interface.Base):
    def up(self):
        """ redirects to the first document in the book """
    def next(self):
        """ redirects to the next document in the book """
    def previous(self):
        """ redirects to the previous document in the book """

00064 class CommentableDocumentInterface(Interface.Base):
    """
    A Structured Text document that supports the addition of comments
    to the raw text of the document by supporting a special variant of the
    structured text symbology.  The only additional symbol is the
    'comment', which is a paragraph with begins with '% '.
    """
00071     def addComment(key, comment):
        """ Appends a comment as subitem of element referenced by 'key'."""

00074 class BTNavigableImpl(Implicit):
    """ Abstract class for navigation """
    security = ClassSecurityInfo()
    __implements__ = (BTNavigableInterface,)

    security.declarePublic('up')
00080     def up(self):
        """ """
        self.REQUEST.RESPONSE.redirect(self.getUp().absolute_url())

    security.declarePublic('next')
00085     def next(self):
        """ """
        self.REQUEST.RESPONSE.redirect(self.getNext().absolute_url())
    
    security.declarePublic('previous')
00090     def previous(self):
        """ """
        self.REQUEST.RESPONSE.redirect(self.getPrevious().absolute_url())

    security.declarePublic('contents')
00095     def contents(self):
        """ html representation of table of contents """
        l = self.getContents()
        out = []
        write = out.append
        write('<p>')
        write(self.getSummary())
        write('</p>')
        write('<ol>')
        for d in l:
            write('<li><a href="%s">%s</a></li>' % (d['url'],d['title']))
        write('</ol>')
        out = string.join(out, '\n')
        t = Globals.HTML(template)
        r = self.REQUEST
        r['body'] = out
        return apply(t, (self, self.REQUEST))

00113     def index(self):
        """ """
        return 'This feature is unimplemented - sorry!'

    security.declarePublic('hasUp')
    def hasUp(self):
        return self.getUp() and self.getUp() is not self
    
    security.declarePublic('hasNext')
    def hasNext(self):
        return self.getNext() is not self

    security.declarePublic('hasPrevious')
    def hasPrevious(self):
        return self.getPrevious() is not self

    security.declarePublic('hasParentBook')
    def hasParentBook(self):
        return not not self.getParentBook()

    security.declarePublic('isFirstInBook')
    def isFirstInBook(self):
        p = self.getParentBook()
        if not p:
            return 0
        if filter(None, p.documents):
            id = map(string.strip, p.documents)[0]
        else:
            ids = p.objectIds()
            ids.sort()
            id = ids[0]
        return id == self.getId()
            
    security.declarePublic('upText')
    def upText(self):
        ob = self.getUp()
        if not ob or ob is self: return ''
        else: return ob.title_or_id()

    security.declarePublic('previousText')
    def previousText(self):
        ob = self.getPrevious()
        if ob is self: return ''
        else: return ob.title_or_id()

    security.declarePublic('nextText')
    def nextText(self):
        ob = self.getNext()
        if ob is self: return ''
        else: return ob.title_or_id()

    def getParentBook(self):
        p = aq_parent(aq_inner(self))
        if p.meta_type != BackTalkBook.meta_type:
            p = None
        return p

    def getUp(self):
        raise 'Unimplemented'

    def getNext(self):
        raise 'Unimplemented'

    def getPrevious(self):
        raise 'Unimplemented'

    def getSummary(self):
        raise 'Unimplemented'

    def getContents(self):
        raise 'Unimplemented'

Globals.InitializeClass(BTNavigableImpl)

class BackTalkBook(Folder, BTNavigableImpl):
    manage_options = (
        {'label':'Contents',
         'action':'manage_main',
         'help': ('BackTalk','Book-contents.stx')},
        ) + Folder.manage_options[1:] + (
        {'label': 'Printable',
         'action':'manage_PDFOutput',
         'help': ('BackTalk', 'Printable.stx')},
        )
    __implements__ = (BTNavigableInterface,)
    security = ClassSecurityInfo()
    meta_type = 'BackTalk Book'
    _properties = Folder._properties + (
        {'id':'summary', 'type':'text', 'mode':'w'},
        {'id':'permit_comments', 'type':'boolean', 'mode':'w'},
        {'id':'documents', 'type':'lines', 'mode':'w'},
        {'id':'catalog_name', 'type':'string', 'mode':'w'},
        {'id':'comment_email_addresses', 'type':'lines', 'mode':'w'},
        {'id':'comment_mail_from_address', 'type':'string', 'mode':'w'},
        {'id':'header', 'type':'text', 'mode':'w'},
        {'id':'create_titlepage', 'type':'boolean', 'mode':'w'},
        {'id':'create_bookmarks', 'type':'boolean', 'mode':'w'},
        {'id':'create_toc', 'type':'boolean', 'mode':'w'},
        {'id':'suppress_comments_in_output', 'type':'boolean', 'mode':'w'},
        {'id':'titlepage_header', 'type':'string', 'mode':'w'},
        {'id':'titlepage_author', 'type':'string', 'mode':'w'},
        {'id':'titlepage_center', 'type':'text', 'mode':'w'},
        {'id':'titlepage_bottom', 'type':'text', 'mode':'w'},
        {'id':'save_to', 'type':'selection',
         'select_variable':'save_to_types', 'mode':'w'},
        {'id':'printable_filename', 'type':'string', 'mode':'w'},
        )

    documents = []
    catalog_name = ''
    summary = ''
    permit_comments = 1
    header = ''
    title = ''
    create_titlepage = 1
    create_toc = 1
    create_bookmarks = 1
    titlepage_header = ''
    titlepage_author = ''
    titlepage_center = ''
    titlepage_bottom = ''
    suppress_comments_in_output = 1
    save_to = 'browser'
    save_to_types = ['browser', 'local']
    printable_filename = 'document.pdf'
    comment_email_addresses = []
    comment_mail_from_address = 'nobody@nowhere.com'
    manage_PDFOutput=Globals.DTMLFile('dtml/PDFOutput', globals())
    try: commentImage = ImageFile('www/comment2.gif', globals())
    except: pass
    try: tri_down = ImageFile('www/tri_down.gif', globals())
    except: pass
    try: tri_up = ImageFile('www/tri_up.gif', globals())
    except: pass
    try: nextIcon = ImageFile('www/next.gif', globals())
    except: pass
    try: previousIcon = ImageFile('www/previous.gif', globals())
    except: pass
    try: upIcon = ImageFile('www/up.gif', globals())
    except: pass
    try: tocIcon = ImageFile('www/contents.gif', globals())
    except: pass
    try: indexIcon = ImageFile('www/index.gif', globals())
    except: pass
    try: commentsIcon = ImageFile('www/comments.gif', globals())
    except: pass
    try: commentsOnIcon = ImageFile('www/commentsOn.gif', globals())
    except: pass
    try: commentsOffIcon = ImageFile('www/commentsOff.gif', globals())
    except: pass
    try: stxIcon = ImageFile('www/stx.gif', globals())
    except: pass
    try: blankIcon = ImageFile('www/blank.gif', globals())
    except: pass
    try: editIcon = ImageFile('www/edit.gif', globals())
    except: pass
    try: exEditIcon = ImageFile('www/exedit.gif', globals())
    except: pass

    security.declareProtected(VIEW, 'index_html')
    def index_html(self):
        """ """
        return self.contents()

    security.declareProtected('Manage properties', 'manage_editProperties')
    def manage_editProperties(self, REQUEST):
        """ """
        # this is awful.  we need to uncook all of our document subobjects
        # when there is a property state change.  This is to support
        # change of state in permit_comments, which causes us to need
        # to rerender our member documents.
        for ob in self.objectValues(BackTalkDocument.meta_type):
            ob._v_cooked = None
        return Folder.manage_editProperties(self, REQUEST)

    security.declarePublic('isCommentingPermitted')
    def isCommentingPermitted(self):
        return not not self.permit_comments

    security.declareProtected(CREATE_PDFS, 'manage_makePDF')
    def manage_makePDF(self, REQUEST):
        """ Once again, god I hate HTML """
        create_titlepage = REQUEST.has_key('create_titlepage') or 0
        create_toc = REQUEST.has_key('create_toc') or 0
        create_bookmarks = REQUEST.has_key('create_bookmarks') or 0
        suppress_comments_in_output = REQUEST.has_key(
            'suppress_comments_in_output') or 0
        titlepage_header = REQUEST.get('titlepage_header', '')
        titlepage_author = REQUEST.get('titlepage_author', '')
        titlepage_center = REQUEST.get('titlepage_center', '')
        titlepage_bottom = REQUEST.get('titlepage_bottom', '')
        save_to = REQUEST.get('save_to', 'browser')
        printable_filename = REQUEST.get('printable_filename', 'document.pdf')
        self.manage_changeProperties(locals())
        if bad_id(printable_filename) is not None:
            raise ValueError, ('Filename may only contain letters A-Z, digits'
                               ' 0-9, spaces, and the following characters: '
                               '-~,.$()')
        if save_to != 'browser' and printable_filename in self.objectIds():
            raise ValueError, ('Filename %s is already in use.  Please '
                               'choose a different filename.'
                               %  self.printable_filename)
        if self.save_to == 'browser':
            REQUEST.RESPONSE.setHeader('Content-Type', 'application/pdf')
            REQUEST.RESPONSE.setHeader('Content-Location',
                                       self.printable_filename)
            file = REQUEST.RESPONSE
        else:
            file = cStringIO.StringIO()
        self.makePDF(file, create_titlepage, create_toc, create_bookmarks,
                     suppress_comments_in_output, titlepage_header,
                     titlepage_author, titlepage_center,
                     titlepage_bottom)
        if self.save_to != 'browser':
            self.manage_addFile(self.printable_filename, file)
            me = string.join(self.getPhysicalPath(), '/')
            return Globals.MessageDialog(
                title="Saved",
                message=("The book's contents were saved in %s/%s"
                         % (me, self.printable_filename)),
                action="./manage_PDFOutput")
            

    def makePDF(self, file, create_titlepage=1, create_toc=1,
                create_bookmarks=1, suppress_comments_in_output=1,
                titlepage_header='', titlepage_author='', titlepage_center='',
                titlepage_bottom=''):
        import PDFClass # this will fail without reportlab
        finder = Retrievers.ZODBImageRetriever(self)
        if suppress_comments_in_output:
            pdf = PDFClass.PDFWithoutCommentsClass(
                file=file, title=self.title, imageretriever=finder,
                do_bookmarks=create_bookmarks, do_toc=create_toc
                )
        else:
            pdf = PDFClass.PDFClass(
                file=file, title=self.title, imageretriever=finder,
                do_bookmarks=create_bookmarks, do_toc=create_toc
                )
        if create_titlepage:
            pdf.makeTitlePage(titlepage_header, titlepage_author,
                              titlepage_center, titlepage_bottom)
        if create_toc:
            pdf.makeTOC()
        obs = self.getDocuments()
        docs = []
        for ob in obs:
            st = ob.raw_as_st()
            docs.append(CommentableDocument.CommentableDocument(st))
        pdf(docs)

    security.declareProtected(USE_MAILHOST, 'sendMail')
    def sendMail(self, text, subject=''):
        mfrom = '%s' % self.comment_mail_from_address
        if not subject:
            subject = 'BackTalk to Book %s' % escape(self.title)
        mh = getattr(self, 'BackTalkMailHost', None)
        if mh is None:
            pp = string.join(self.getPhysicalPath(), '/')
            zLOG.LOG('BackTalk', 100, ('Could not send mail due to '
                                       'missing BackTalkMailHost '
                                       'in %s' % pp))
        for mto in filter(None, self.comment_email_addresses):
            try:
                messageText = (
                    "From: %s\r\nTo: %s\r\n\r\n%s" % (mfrom, mto, text)
                    )
                mh.send(messageText, mto=mto, mfrom=mfrom,
                        subject=subject, encode=None)
            except:
                zLOG.LOG('BackTalk', 100, 'Could not send mail', '',
                         error=sys.exc_info())

    security.declareProtected(MGMT_SCREEN,'resetPersistentObs')
    def resetPersistentObs(self):
        """ """
        setUpBackTalkBook(self)
        zLOG.LOG('BackTalk', 100, 'BackTalk persistent objects reset')
        return Globals.MessageDialog(
            title="Reset",
            message="The book's persistent objects were reset",
            action="./manage_main")

    def getSummary(self):
        return self.summary
        
    def getUp(self):
        p = self.getParentBook()

        if p is None:
            p = self

        if hasattr(aq_base(p), 'contents'):
            return Redirector(p.absolute_url() + '/contents',
                              'Table Of Contents')
        else:
            return p

    def getNext(self):
        doc = self
        if filter(None, self.documents):
            name = string.strip(list(self.documents)[0])
            doc = self._getOb(name)
        else:
            ids = self.objectIds(self.meta_type)
            ids = ids + self.objectIds(BackTalkDocument.meta_type)
            if ids:
                ids.sort()
                doc = self._getOb(ids[0])
        return doc

    def getPrevious(self):
        doc = self
        p = self.getParentBook()
        if p is not None:
            if filter(None, p.documents):
                docs = map(string.strip, p.documents)
                pos = docs.index(self.getId()) - 1
                if pos > -1: doc = p._getOb(docs[pos])
                else: doc = self
            else:
                ids = p.objectIds(self.meta_type)
                ids = ids + p.objectIds(BackTalkDocument.meta_type)
                ids.sort()
                ids.reverse()
                for id in ids:
                    if id < self.getId():
                        doc = p._getOb(id)
                        break
        return doc

    def getDocuments(self):
        l = []
        if filter(None, self.documents):
            for doc in map(string.strip, self.documents):
                if not doc: continue
                d = {}
                ob = getattr(self, doc)
                l.append(ob)
        else:
            items = self.objectItems(self.meta_type)
            items = items + self.objectValues(BackTalkDocument.meta_type)
            items.sort()
            l.extend(items)
        return l

    def getContents(self):
        l = []
        docs = self.getDocuments()
        for doc in docs:
            d = {}
            d['url'] = doc.absolute_url()
            d['title'] = doc.title_or_id()
            d['path'] = doc.getPhysicalPath()
            l.append(d)
        return l

    def PUT_factory(self, name, typ, body):
        """ """
        if string.find(string.lower(typ), 'text') != -1:
            return BackTalkDocument(body, __name__=name)

    security.declarePublic('isExternalEditorInstalled')
    def isExternalEditorInstalled(self):
        """ """
        return hasattr(self.Control_Panel.Products, 'ExternalEditor')

    security.declarePublic('getCatalogName')
    def getCatalogName(self):
        """ Return the catalog name used to index our documents' contents """
        return self.catalog_name

class Redirector:
    def __init__(self, url, title):
        self.url = url
        self.title = title
        
    def absolute_url(self):
        return self.url

    def title_or_id(self):
        return self.title

Globals.InitializeClass(BackTalkBook)

00498 class BackTalkDocument(DTMLDocument, BTNavigableImpl):
    """ """
    _properties = DTMLDocument._properties + (
        {'id':'summary', 'type':'text', 'mode':'w'},
        {'id':'permit_comments', 'type':'boolean', 'mode':'w'},
        {'id':'comment_email_addresses', 'type':'lines', 'mode':'w'},
        {'id':'comment_mail_from_address', 'type':'string', 'mode':'w'},
        )
    manage_options =(
        ({'label':'Edit', 'action':'manage_main',
          'help':('BackTalk','Document-edit.stx')},) +
        DTMLDocument.manage_options[1:]
        )
    __implements__ = (CommentableDocumentInterface, BTNavigableInterface,
                      WriteLockInterface)
    summary = ''
    permit_comments = 1
    comment_email_addresses = []
    comment_mail_from_address = 'nobody@nowhere.com'
    security = ClassSecurityInfo()
    meta_type = 'BackTalk Document'
    template = Globals.HTML(template)
    comment_template = Globals.HTML(comment_template)
    # these try-except clauses are so the product doesnt blow up if
    # an image is deleted, or during unit testing
    try: commentImage = ImageFile('www/comment2.gif', globals())
    except: pass
    try: tri_down = ImageFile('www/tri_down.gif', globals())
    except: pass
    try: tri_up = ImageFile('www/tri_up.gif', globals())
    except: pass
    try: nextIcon = ImageFile('www/next.gif', globals())
    except: pass
    try: previousIcon = ImageFile('www/previous.gif', globals())
    except: pass
    try: upIcon = ImageFile('www/up.gif', globals())
    except: pass
    try: tocIcon = ImageFile('www/contents.gif', globals())
    except: pass
    try: indexIcon = ImageFile('www/index.gif', globals())
    except: pass
    try: commentsIcon = ImageFile('www/comments.gif', globals())
    except: pass
    try: commentsOnIcon = ImageFile('www/commentsOn.gif', globals())
    except: pass
    try: commentsOffIcon = ImageFile('www/commentsOff.gif', globals())
    except: pass
    try: stxIcon = ImageFile('www/stx.gif', globals())
    except: pass
    try: blankIcon = ImageFile('www/blank.gif', globals())
    except: pass
    try: editIcon = ImageFile('www/edit.gif', globals())
    except: pass
    try: exEditIcon = ImageFile('www/exedit.gif', globals())
    except: pass

    # make proxy declarations for methods declared in our base class
    security.declareProtected('Change BackTalk Documents',
    'manage_editForm', 'manage', 'manage_main', 'manage_historyCopy',
    'manage_beforeHistoryCopy', 'manage_afterHistoryCopy')

    security.setPermissionDefault(CHANGE, ['Manager'])
    security.setPermissionDefault(VIEW_SOURCE, ['Anonymous'])
    security.setPermissionDefault(COMMENT, ['Anonymous'])

    def manage_afterAdd(self, *arg, **kw):
        self.catalog_myself()

    def manage_beforeDelete(self, *arg, **kw):
        self.uncatalog_myself()

    security.declareProtected('Manage properties', 'manage_editProperties')
00570     def manage_editProperties(self, REQUEST):
        """ """
        # override property editing to uncook ourselves (to support
        # permit_comments property)
        self._v_cooked = None
        return DTMLDocument.manage_editProperties(self, REQUEST)

    security.declareProtected(CHANGE, 'manage_edit')
00578     def manage_edit(self,data,title,SUBMIT='Change',dtpref_cols='50',
                    dtpref_rows='20',REQUEST=None):
        """ """
        result = DTMLDocument.manage_edit(
            self, data, title, SUBMIT, dtpref_cols, dtpref_rows, REQUEST
            )
        self.catalog_myself()
        self._v_cooked = None
        return result

    security.declareProtected(CHANGE, 'manage_upload')
00589     def manage_upload(self, file, REQUEST=None):
        """ """
        result = DTMLDocument.manage_edit(self, file, REQUEST)
        self.catalog_myself()
        self._v_cooked = None
        return result

    # proxy security declaration
    security.declareProtected(VIEW_SOURCE,'document_src')

    security.declareProtected(USE_MAILHOST, 'sendMail')
    def sendMail(self, text, subject=''):
        mh = getattr(self, 'BackTalkMailHost', None)
        if mh is None:
            pp = string.join(self.getPhysicalPath(), '/')
            zLOG.LOG('BackTalk', 100, ('Could not send mail due to '
                                       'missing BackTalkMailHost '
                                       'in %s' % pp))
            return
        parent = self.getParentBook()
        if parent is not None:
            subject = "BackTalk to Document %s/%s" % (
                escape(parent.title), escape(self.title)
                )
            parent.sendMail(text, subject)
        else:
            title = escape(self.title)
        mfrom = '%s' % (self.comment_mail_from_address)
        if not subject:
            subject = 'BackTalk to Document %s' % escape(title)
        for mto in filter(None, self.comment_email_addresses):
            try:
                messageText = (
                    "From: %s\r\nTo: %s\r\n\r\n%s" % (mfrom, mto, text)
                    )
                mh.send(messageText, mto=mto, mfrom=mfrom,
                        subject=subject, encode=None)
            except:
                zLOG.LOG('BackTalk', 100, 'Could not send mail', '',
                         error=sys.exc_info())

    security.declareProtected(CHANGE, 'PUT')
00631     def PUT(self, REQUEST, RESPONSE):
        """ """
        self.dav__init(REQUEST, RESPONSE)
        try: self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
        except AttributeError: pass # Zope 2.3.X and before
        body=REQUEST.get('BODY', '')
        self.raw = body
        self._v_cooked = None
        self._validateProxy(REQUEST)
        self.ZCacheable_invalidate()
        self.catalog_myself()
        RESPONSE.setStatus(204)
        return RESPONSE

    security.declareProtected(VIEW, '__call__')
00646     def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
        """ """
        if RESPONSE is not None:
            RESPONSE.setHeader('Content-Type','text/html')
        data = self.ZCacheable_get(default=_marker)
        if data is not _marker: return data # return CacheManager data
        return apply(self.get_rendered_template, (REQUEST,), kw)

    security.declareProtected(VIEW, 'commentForm')
00655     def commentForm(self, client=None, REQUEST={}, RESPONSE=None, **kw):
        """ """
        if RESPONSE is not None:
            RESPONSE.setHeader('Content-Type', 'text/html')
        return apply(self.get_comment_template, (REQUEST,), kw)

    security.declarePublic('isCommentingPermitted')
    def isCommentingPermitted(self):
        # if we have a parent book, it controls our commenting policy.
        # permit comments based on own policy if we don't have a parent book.
        book = self.getParentBook()
        if book is None:
            return not not self.permit_comments
        return not not book.isCommentingPermitted()

    security.declarePublic('canUseCooked')
    def canUseCooked(self):
        is_cooked = hasattr(self, '_v_cooked') and self._v_cooked
        if not is_cooked: return 0

    security.declarePrivate('get_rendered_template')
    def get_rendered_template(self, REQUEST, **kw):
        if not self.canUseCooked():
            self.cook()
        POSITIVE = {'1':1, 'yes':1, 'y':1, 1:1}
        suppress = POSITIVE.get(REQUEST.get('suppress_comments'), 0)
        REQUEST.set('suppress_comments', suppress)
        kw['permit_comments'] = self.isCommentingPermitted()
        kw['body'] = self._v_raw_as_html # return locally cached data
        return apply(self.template, (self, REQUEST), kw)

    security.declarePrivate('get_comment_template')
    def get_comment_template(self, REQUEST, **kw):
        kw['level'] = REQUEST.get('level', -1)
        kw['index'] = REQUEST.get('index', -1)
        kw['title_or_id'] = self.title_or_id()
        kw['body'] = self.getRenderedParagraph(kw['level'], kw['index'])
        kw['username'] = getSecurityManager().getUser().getUserName() or ''
        return apply(self.comment_template, (self, REQUEST), kw)

    security.declarePrivate('cook')
    def cook(self):
        self._v_cooked = 1
        self._v_raw_as_st = self.raw_as_st()
        permit_comments = self.isCommentingPermitted()
        self._v_raw_as_html = self.raw_as_html(permit_comments=permit_comments)

    security.declarePrivate('catalog_myself')
    def catalog_myself(self):
        catalog = self.getCatalog()
        if catalog is None or catalog == '':
            # misery.  can't use if catalog: for whatever reason
            return
        pp = string.join(self.getPhysicalPath(), '/')
        try:
            catalog.catalog_object(self, pp)
        except:
            zLOG.LOG('BackTalk', 100, 'Error while cataloging "%s"' % pp,
                     error=sys.exc_info())

    security.declarePrivate('catalog_myself')
    def uncatalog_myself(self):
        catalog = self.getCatalog()
        if catalog is None or catalog == '':
            # misery.  can't use if catalog: for whatever reason
            return
        pp = string.join(self.getPhysicalPath(), '/')
        try:
            catalog.uncatalog_object(pp)
        except:
            zLOG.LOG('BackTalk', 100, 'Error while uncataloging "%s"' % pp,
                     error=sys.exc_info())

    security.declarePrivate('getCatalog')
    def getCatalog(self):
        if not hasattr(self, 'getCatalogName'):
            return # we dont have a book to tell us which Catalog to use
        try:
            catalog_name = self.getCatalogName()
        except:
            zLOG.LOG('BackTalk', 100, 'Error while calling getCatalogName',
                     error=sys.exc_info())
            return

        _marker = []
        catalog = getattr(self, catalog_name, _marker)
        if catalog is _marker: # we couldn't find the catalog
            catalog = None
            zLOG.LOG('BackTalk', 100,
                     'Could not find catalog named "%s"' % catalog_name)
        return catalog
        
    security.declarePrivate('raw_as_html')
    def raw_as_html(self, permit_comments=1):
        return BackTalkBase.stxAsHtml(text=self.read_raw(), level=1,
                                      base_url=self.absolute_url(),
                                      permit_comments=permit_comments)
    
    security.declarePrivate('raw_as_st')
    def raw_as_st(self):
        return BackTalkBase.BasicWithIds(self.read_raw())

    security.declarePublic('url_quote')
    def url_quote(self, s):
        return urllib.quote_plus(s)

    security.declarePublic('url_unquote')
    def url_unquote(self, s):
        return urllib.unquote_plus(s)

    security.declareProtected(COMMENT, 'addComment')
00766     def addComment(self, paragraph, comment):
        """ Add a comment to the paragraph that is referenced by the
        record 'paragraph' """
        index = paragraph.index
        level = paragraph.level
        comment = string.strip(comment)
        if not comment and hasattr(self, 'REQUEST'):
            self.REQUEST.RESPONSE.redirect(self.absolute_url() +
                                           '#%s-%s' % (level, index))
            return ''
        st = BackTalkBase.BasicWithIds(self.read_raw()) # this should be cached
        username = getSecurityManager().getUser().getUserName() or ''
        date = DateTime().pCommon()
        BackTalkBase.insertComment(st, comment, username, date, level, index)
        self.ZCacheable_invalidate()
        # calls "self.cook" and sets self.raw
        # we dont recatalog on a comment (this is on purpose)
        self.munge(BackTalkBase.SourceDocument(st))
        parent = self.getParentBook()
        if self.comment_email_addresses or parent:
            graph = BackTalkBase.getParagraphGraph(st)
            indexes = graph[level]
            paragraph = indexes[index]
            paragraph = BackTalkBase.flatten_basic(paragraph)
            url = self.absolute_url() + '#%s-%s' % (level, index)
            paragraph = (
                'A comment to the paragraph below was recently added '
                'via %s\n\n---------------\n\n%s' % (url, paragraph)
                )
            self.sendMail(paragraph)
        if hasattr(self, 'REQUEST'):
            self.REQUEST.RESPONSE.redirect(self.absolute_url() +
                                           '#%s-%s' % (level, index))

    security.declareProtected(VIEW, 'getRenderedParagraph')
00801     def getRenderedParagraph(self, level, index):
        """ Get HTML-rendered paragraph rendered by level and index """
        paragraph = self.getRawParagraph(level, index)
        st = ST.StructuredTextDocument(subs=[paragraph])
        doc = CommentableDocument.CommentableDocument(st)
        html = HTMLClass.HTML(doc, level=1, base_url=self.absolute_url(),
                                 commentoffers=0)
        return html

    security.declareProtected(VIEW, 'getRawParagraph')
00811     def getRawParagraph(self, level, index):
        """ Get raw paragraph text by level and index """
        st = BackTalkBase.BasicWithIds(self.read_raw()) # this should be cached
        if not st: return 'No source!'
        graph = BackTalkBase.getParagraphGraph(st)
        indexes = graph[level]
        paragraph = indexes[index]
        return paragraph

    def getSummary(self):
        return self.summary

    def getUp(self):
        p = self.getParentBook()
        if not (p and p.meta_type == BackTalkBook.meta_type):
            return self # not part of a collection

        if self.isFirstInBook():
            pp = p.getParentBook()
            if (pp and pp.meta_type == BackTalkBook.meta_type):
                p = pp
            else:
                p = p

        if hasattr(aq_base(p), 'contents'):
            return Redirector(p.absolute_url() + '/contents',
                              'Table Of Contents')
        else:
            return p

    def getNext(self):
        doc = self
        p = self.getParentBook()
        if not (p and p.meta_type == BackTalkBook.meta_type):
            return self # not part of a collection
        if filter(None, p.documents):
            ids = map(string.strip, p.documents)
            i = 0
            for id in ids:
                if id == self.getId():
                    try: doc = p._getOb(ids[i+1])
                    except (IndexError, KeyError, AttributeError): doc = self
                    break
                i = i + 1
        else:
            ids = p.objectIds(self.meta_type)
            ids = ids + p.objectIds(BackTalkDocument.meta_type)
            if ids:
                ids.sort()
                for id in ids:
                    if id > self.getId():
                        doc = p._getOb(id)
                        break
        return doc

    def getPrevious(self):
        doc = self
        p = self.getParentBook()
        if not (p and p.meta_type == BackTalkBook.meta_type):
            return self # not part of a collection
        if filter(None, p.documents):
            ids = map(string.strip, p.documents)
            i = 0
            for id in ids:
                if id == self.getId():
                    if i-1 >= 0:  doc = p._getOb(ids[i-1])
                    else: doc = self
                    break
                i = i + 1
        else:
            ids = p.objectIds(self.meta_type)
            ids = ids + p.objectIds(BackTalkDocument.meta_type)
            if ids:
                ids.sort()
                ids.reverse()
                for id in ids:
                    if id < self.getId():
                        doc = p._getOb(id)
                        break
        return doc

00892     def contents(self):
        """ """
        p = self.getParentBook()
        if p is None:
            return "No table of contents, not part of a collection"
        self.REQUEST.RESPONSE.redirect(p.absolute_url()+'/contents')

Globals.InitializeClass(BackTalkDocument)

def addDocument(self, id, title='', file='', url='', obtain_images=None,
                REQUEST=None, submit=None):
    """Add a BackTalk Document object with the contents of file. If
    'file' or URL is empty, default document text is used.
    """
    if type(file) is not type(''): file=file.read()
    if not file: file=''
    id=str(id)
    results = []
    if url:
        if url[:5] == 'http:' or url[:6] == 'https:':
            retriever = Retrievers.HTTPRetriever()
        else:
            retriever = Retrievers.ZODBRetriever(self)
        try:
            file = retriever(url)
        except:
            if REQUEST is not None:
                try: u=self.DestinationURL()
                except: u=REQUEST['URL1']
                if submit==" Add and Edit ": u="%s/%s" % (u, urllib.quote(id))
                msg = 'Could not obtain the page %s, please try again' % url
                return Globals.MessageDialog(title='Page retrieval problems',
                                             message = msg,
                                             action = "%s/manage_main" % u)
            else:
                raise
        if obtain_images is not None:
            try:
                self.manage_addFolder('images')
            except:
                zLOG.LOG('BackTalk',-100,'Couldnt add images folder', '',
                         error=sys.exc_info())
            images = getattr(self, 'images')
            r, file = installImages(images, file, id, url)
            results.extend(r)
    ob=BackTalkDocument(file, __name__=id)
    ob.title=str(title)
    id=self._setObject(id, ob)
    if REQUEST is not None:
        try: u=self.DestinationURL()
        except: u=REQUEST['URL1']
        if submit==" Add and Edit ": u="%s/%s" % (u, urllib.quote(id))
        if results:
            results = map(escape, results)
            results = string.join(results, '<br>')
            msg = 'Some items could not be installed:<br><br>%s' % results
            return Globals.MessageDialog(title='Image retrieval problems',
                                         message=msg,
                                         action='%s/manage_main' % u)
        REQUEST.RESPONSE.redirect(u+'/manage_main')
        return ''
    else:
        if results:
            return results

def addBook(self, id, title='',REQUEST=None):
    """Add a new Book object with id *id*."""
    ob=BackTalkBook()
    ob.id=str(id)
    ob.title=title
    self._setObject(id, ob)
    ob=self._getOb(id)
    setUpBackTalkBook(ob)
    if REQUEST is not None:
        return self.manage_main(self, REQUEST, update_menu=1)

def installImageFolder(context, id):
    true_context = getattr(context, 'aq_base', context)
    image_folder = getattr(true_context, id, None)
    if image_folder is not None:
        if not hasattr(image_folder, 'isPrincipiaFolderish'):
            zLOG.LOG('BackTalk',100,'image folder %s is not folderish' %id, '')
            return None
        return getattr(context, id)
    # we need to create one
    # we want context to be a folderish object if we add an image folder to it
    if not hasattr(true_context, 'isPrincipiaFolderish'):
        zLOG.LOG('BackTalk', 100,
                 'top-level image folder is not folderish %s' % id, '')
        return None
    try:
        context.manage_addFolder(id)
    except:
        zLOG.LOG('BackTalk', 100, 'could not add image folder %s' % id, '',
                 error=sys.exc_info())
        return None
    return getattr(context, id)

def installRemoteImage(context, id, name, url):
    folder = installImageFolder(context, id)
    if folder is None:
        zLOG.LOG('BackTalk', 100,
                 'Couldnt add image %s due to an earlier error' % name, '')
        return
    try:
        image_data = Retrievers.HTTPRetriever()(url)
        image_data = cStringIO.StringIO(image_data)
        folder.manage_addImage(name, image_data)
    except:
        return "%s from %s" % (name, url)

def installLocalImage(context, id, name, path):
    folder = installImageFolder(context, id)
    if folder is None:
        zLOG.LOG('BackTalk', 100,
                 'Couldnt add image %s due to an earlier error' % name, '')
        return
    try:
        image_data = Retrievers.ZODBImageRetriever(context)(path)[0]
        image_data = cStringIO.StringIO(image_data)
        folder.manage_addImage(name, image_data)
    except:
        return "%s from %s" % (name, path)
    
def installImages(context, text, id, path):
    # split the whole text by each "whole_img" regex and iterate
    # over the image tags, replacing them with the a reference to
    # an image in the "images" folder and along the way add an image
    # in the images folder for each tag
    results = []
    tried = {}
    if BackTalkBase.whole_stx_img_expr.search(text):
        l = BackTalkBase.whole_stx_img_expr.split(text)
        for x in range(1, len(l), 2):
            match = BackTalkBase.stx_img_expr.search(l[x])
            desc = match.group(1)
            href = match.group(2)
            fig = 'unknown'
            if ':' in href:
                fig, href = string.split(href, ':', 1)
            name = string.split(href, '/')[-1]
            if tried.has_key(name):
                continue
            tried[name] = 1
            if href[:5] == 'http:' or href[:6] == 'https:':
                installImage = installRemoteImage
            else:
                installImage = installLocalImage
            result = installImage(context, id, name, href)
            result and results.append(result)
            l[x] = '"%s":img:%s:images/%s/%s' % (desc, fig, id, name)
        text = string.join(l)
    # split on HTML img src= below (for html links embedded in
    # wiki pages, mostly)
    if BackTalkBase.whole_html_img_expr.search(text):
        l = BackTalkBase.whole_html_img_expr.split(text)
        for x in range(1, len(l), 2):
            match = BackTalkBase.html_img_expr.search(l[x])
            item = match.group(0)
            quote = match.group(2)
            href = match.group(3)
            name = string.split(href, '/')[-1]
            if tried.has_key(name):
                continue
            tried[name] = 1
            if href[:5] == 'http:' or href[:6] == 'https:':
                installImage = installRemoteImage
            else:
                installImage = installLocalImage
            result = installImage(context, id, name, href)
            result and results.append(result)
            l[x] = '<img src=%simages/%s/%s%s' % (quote, id, name, quote)
        text = string.join(l)
    # return the munged text and the result of the image installs
    return results, text

def setUpBackTalkBook(book):
    names = ['backtalk_html_header',
             'backtalk_html_footer',
             'backtalk_navbar',
             'backtalk_css',
             'backtalk_js']
    for name in names:
        try: book.manage_delObjects(name)
        except: pass
        f = open(os.path.join(package_home, name + '.dtml')).read()
        book.manage_addDTMLMethod(id=name, title='', file=f)
    try:
        book.manage_addProduct['MailHost'].manage_addMailHost(
            id='BackTalkMailHost', title='',
            smtp_host='127.0.0.1', localhost='localhost',
            smtp_port=25, timeout=1.0
            )
    except: # can't catch 'Bad Request' error only ;-)
        zLOG.LOG('BackTalk', 100, 'Couldnt set up mailhost', '',
                 error=sys.exc_info())

Generated by  Doxygen 1.6.0   Back to index