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

BackTalkBase.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: BackTalkBase.py,v 1.10 2002/08/09 16:34:59 chrism Exp $
"""

import string, re
from StructuredText import ST, Basic
from TextBlockFormatter import format
from HTMLClass import HTML
from CommentableDocument import CommentableDocument

#################################################################
# Regexes and constants
#################################################################

para_delim = r'(\n\s*\n|\r\n\s*\r\n)' # UNIX or DOS line endings, respectively
para_delim_expr = re.compile(para_delim)

# these are imported by BackTalk.py
whole_stx_img_expr=re.compile(
    '(\"[ _a-zA-Z0-9*.:/;,\-\n\~]+\":img:[a-zA-Z0-9\_\-.:/;,\~]+)'
    )
stx_img_expr=re.compile(
    '\"([ _a-zA-Z0-9*.:/;,\-\n\~]+)\":img:([a-zA-Z0-9\_\-.:/;,\~]+)'
    )
whole_html_img_expr=re.compile(
    r"""(<img[^>]*?src\s*=\s*[""']?[^'"" >]+[ '""]?)""", re.IGNORECASE
    )
html_img_expr=re.compile(
    r"""(<img[^>]*?src\s*=\s*([""']?))([^'"" >]+)[ '""]?""", re.IGNORECASE
    )



################################################################
# classes and functions that deal with converting raw text to
# a "basic" stx document that knows about paragraph ids
################################################################

def flatten_basic(st, l=None):
    if l is None:
        l = []
    if st.getColorizableTexts():
        texts = st.getColorizableTexts()
        for text in texts:
            comment_texts = string.split(text, chr(11))
            l.append(string.join(comment_texts, '\n'))
    if st.getSubparagraphs():
        for sub in st.getSubparagraphs():
            flatten_basic(sub, l)
    return string.join(l, '\n\n')

def BasicWithIds(raw_text):
    st = Basic(raw_text)
    # run the st doc through the getParagraphGraphs function in order
    # to attach an id to each paragraph
    getParagraphGraph(st)
    return st

def getParagraphGraph(doc, map=None, level=0):
    """ Given st basic document doc, return a graph of the document in the
    form of list "l".  l[0] will be all of doc's paragraphs in order that are
    defined at doc's first indent level, l[1] will be all of doc's paragraphs
    in order that are defined at doc's second indent level, etc.  Attach
    an attribute "id" to each paragraph which consists of its level number
    and index number in a tuple, e.g. (0,1). """
    if map is None: map = {}
    paragraphs = doc.getSubparagraphs()
    if paragraphs:
        try: m = map[level]
        except KeyError: m = map[level] = []
        for paragraph in paragraphs:
            index = len(m)
            m.append(paragraph)
            paragraph.id = (level, index)
            attrs = getattr(paragraph, '_attributes', [])
            attrs.append('id')
            getParagraphGraph(paragraph, map, level+1)
    l = []
    keys = map.keys()
    keys.sort()
    for key in keys:
        l.append(map[key])
    return l

################################################################
# Functions used to output HTML
################################################################

def stxAsHtml(text, level, base_url, permit_comments=1):
    st = BasicWithIds(text)
    if not st: return ''
    doc = CommentableDocument(st)
    return HTML(doc, level=level, base_url=base_url,
                commentoffers=permit_comments)

################################################################
# Functions used to write comments into the stx dom
################################################################

def findNode(doc, level, index):
    node = None
    for p in doc.getSubparagraphs():
        try: p_level, p_index = p.id
        except AttributeError: continue
        if not (p_level == level and p_index == index):
            node = findNode(p, comment, username, date, level, index)
            continue
    return node

def insertComment(doc, comment, username, date, level, index, spaces=2):
    for p in doc.getSubparagraphs():
        try: p_level, p_index = p.id
        except AttributeError: continue
        if not (p_level == level and p_index == index):
            insertComment(p, comment, username, date, level, index)
            continue
        comment = '%% %s - %s:\n%s' % (username, date, comment)
        t = p.getColorizableTexts()[0]
        # expand tabs into spaces in the paragraph being commented
        # upon
        t = string.expandtabs(t)
        # figure out the indent level of the paragraph being commented
        # upon
        spaces = indentation_plus(t, plus=spaces)
        # join comment paragraphs together with "vertical tabs" which
        # represent newlines when rendered.  This is necessary to
        # escape the semantics of structured text wherein a set
        # of consective carriage returns is equivalent to a paragraph
        # delimiter.  We want to allow folks to submit a multiline comment
        # that is a "paragraph" in the eyes of structured text, which means
        # that it can't actually contain any blank lines or lines with
        # only whitespace.  In order to service this requirement, we transform
        # the blank lines into vertical tabs which are then rendered into
        # newlines as necessary by markupComment when the comment is sent to
        # the browser.   This is a hack but it saves a lot of think time
        # on my end, because if comments can really be honest-to-god
        # multiparagraph things in stx, walking the document graph
        # and collecting them becomes a massive nightmare.
        paragraphs = string.expandtabs(comment)
        paragraphs = para_delim_expr.split(paragraphs)
        paragraphs = filter(string.strip, paragraphs)
        comment = string.join(paragraphs, '%s\n' % chr(11))
        # we want the inline comment to be readable in a textarea
        # when we go to edit the document, so we run it through
        # the text block formatter.  This may screw up code, but
        # it's better than the alternative of needing to edit
        # 1000-character lines in the textarea. ;-)
        comment = format(comment, max_width=110, indent=spaces,
                         trailing_lines_indent_more=1)
        new = ST.StructuredTextParagraph(comment, indent=p.indent+1)
        subs = p.getSubparagraphs()
        p.setSubparagraphs(subs + [new])
        break

################################################################
# utility functions
################################################################

def SourceDocument(st):
    """ returns raw text given an ST Basic instance """
    nodes = st.getChildren()
    t = []
    append=t.append
    memo={}
    buildText(nodes, append, memo)
    return string.join(t, '\n\n') + '\n'

def buildText(nodes, append, memo):
    memoized=memo.has_key
    for node in nodes:
        if not memoized(node):
            append(node.getColorizableTexts()[0])
            memo[node] = 1
        buildText(node.getSubparagraphs(), append, memo)

def indentation_plus(t, plus=0, spaces_expr=re.compile(r'^(\s*)').match):
    m = spaces_expr(t)
    if m:
        start, end = m.span()
        plus = end-start + plus
    return plus

Generated by  Doxygen 1.6.0   Back to index