# Copyright (c) 2002-2005 Infrae. All rights reserved.
# See also LICENSE.txt
# $Revision: 1.10 $
# Python
from __future__ import nested_scopes
import re
from xml.sax.saxutils import escape, unescape, quoteattr
# Zope
import Acquisition
from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
from OFS.SimpleItem import SimpleItem
# Silva
from Products.Silva import SilvaPermissions
from Products.Silva import mangle
from Products.SilvaDocument import interfaces
URL_PATTERN = r'(((http|https|ftp|news)://([A-Za-z0-9%\-_]+(:[A-Za-z0-9%\-_]+)?@)?([A-Za-z0-9\-]+\.)+[A-Za-z0-9]+)(:[0-9]+)?(/([A-Za-z0-9\-_\?!@#$%^&*/=\.]+[^\.\),;\|])?)?|(mailto:[A-Za-z0-9_\-\.]+@([A-Za-z0-9\-]+\.)+[A-Za-z0-9]+))'
_url_match = re.compile(URL_PATTERN)
class SupportRegistry:
def __init__(self, default_class):
self._mapping = {(None, None): default_class}
def register(self, meta_type, element_name, class_):
self._mapping[(meta_type, element_name)] = class_
def registerDefault(self, class_):
self.register(None, None, class_)
def registerElementDefault(self, element_name, class_):
self.register(None, element_name, class_)
def registerMetaTypeDefault(self, meta_type, class_):
self.register(meta_type, None, class_)
def lookupByName(self, meta_type, element_name):
mapping = self._mapping
result = mapping.get((meta_type, element_name))
if result is not None:
return result
result = mapping.get((meta_type, None))
if result is not None:
return result
result = mapping.get((None, element_name))
if result is not None:
return result
return mapping.get((None, None))
def lookup(self, context=None, node=None):
if context is None:
meta_type = None
else:
meta_type = context.meta_type
if node is None:
nodename = None
else:
nodename = node.nodeName
return self.lookupByName(meta_type, nodename)
class MixedContentSupport(Acquisition.Explicit):
""" Abstract base class for mixed content support.
"""
__implements__ = interfaces.IMixedContentSupport,
def __init__(self, node):
self._node = node
def _editableToDOM(self, editablestr, parser):
"""convert editable to dom"""
parser = parser(editablestr)
parser.run()
return parser.getResult().parsed.dom
def _insertDOM(self, doc, node, newdoc):
"""Method to recursively add all children of newdoc to node. Used by
replace_text and replace_heading
"""
for child in newdoc.childNodes:
if child.nodeType == child.TEXT_NODE:
newnode = doc.createTextNode(child.nodeValue)
node.appendChild(newnode)
elif child.nodeType == child.ELEMENT_NODE:
newnode = doc.createElement(child.nodeName)
for i in range(child.attributes.length):
newnode.setAttribute(child.attributes.item(i).name,
child.attributes.item(i).value)
node.appendChild(newnode)
self._insertDOM(doc, newnode, child)
class ParagraphSupport(MixedContentSupport):
""" Mixed content support for SilvaDocument paragraphs.
"""
security = ClassSecurityInfo()
security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'parse')
def parse(self, inputstr):
""" Parse input string to a DOM
"""
from sprout.silvasubset import PARAGRAPH_SUBSET
inputstr = unicode(inputstr, 'UTF-8')
node = self._node
# remove all old subnodes of node
while node.hasChildNodes():
node.removeChild(node.firstChild)
PARAGRAPH_SUBSET.filteredParse(inputstr, node)
security.declareProtected(
SilvaPermissions.AccessContentsInformation, 'renderHTML')
def renderHTML(self, view_type='public'):
""" Render textual content as HTML.
"""
return self._renderHtmlHelper(self._node, view_type)
security.declareProtected(
SilvaPermissions.ChangeSilvaContent, 'renderEditable')
def renderEditable(self):
""" Render textual content as editable text.
"""
return self._renderEditableHelper(self._node)
def _renderHtmlHelper(self, node, view_type):
result = []
for child in node.childNodes:
if child.nodeType == child.TEXT_NODE:
result.append(mangle.entities(child.nodeValue))
continue
if child.nodeType != child.ELEMENT_NODE:
continue
if child.nodeName == 'strong':
result.append('')
result.append(self._renderHtmlHelper(child, view_type))
result.append('')
elif child.nodeName == 'em':
result.append('')
result.append(self._renderHtmlHelper(child, view_type))
result.append('')
elif child.nodeName == 'super':
result.append('')
result.append(self._renderHtmlHelper(child, view_type))
result.append('')
elif child.nodeName == 'sub':
result.append('')
result.append(self._renderHtmlHelper(child, view_type))
result.append('')
elif child.nodeName == 'abbr':
result.append('')
result.append(self._renderHtmlHelper(child, view_type))
result.append('')
elif child.nodeName == 'acronym':
result.append('')
result.append(self._renderHtmlHelper(child, view_type))
result.append('')
elif child.nodeName == 'link':
path = child.getAttribute('url')
url = self._linkHelper(node, path)
result.append('')
result.append(self._renderHtmlHelper(child, view_type))
result.append('')
elif child.nodeName == 'underline':
result.append('')
result.append(self._renderHtmlHelper(child, view_type))
result.append('')
elif child.nodeName == 'index':
name = child.getAttribute('name')
anchorid = mangle.generateAnchorName(name)
result.append('' % anchorid)
# In nested lists and tables we cannot rely on the
# correct view_type passed in, so check for edit_mode
# from the request.
edit_mode = node.REQUEST.get('edit_mode', 0)
if edit_mode:
result.append('[[%s]]' % name)
result.append('')
elif child.nodeName == 'br':
result.append('
')
else:
result.append('%s ' %
self._renderHtmlHelper(child, view_type))
return ''.join(result)
def _renderEditableHelper(self, node):
"""Render textual content as editable text.
"""
result = []
for child in node.childNodes:
if child.nodeType == child.TEXT_NODE:
result.append(child.nodeValue)
continue
if child.nodeType != child.ELEMENT_NODE:
continue
if child.nodeName == 'strong':
result.append('')
result.append(self._renderEditableHelper(child))
result.append('')
elif child.nodeName == 'em':
result.append('')
result.append(self._renderEditableHelper(child))
result.append('')
elif child.nodeName == 'super':
result.append('')
result.append(self._renderEditableHelper(child))
result.append('')
elif child.nodeName == 'sub':
result.append('')
result.append(self._renderEditableHelper(child))
result.append('')
elif child.nodeName == 'abbr':
if child.getAttribute('title'):
result.append(
'' % child.getAttribute('title'))
else:
result.append('')
result.append(self._renderEditableHelper(child))
result.append('')
elif child.nodeName == 'link':
url = mangle.entities(child.getAttribute('url'))
url_comparator = mangle.entities(
self._linkHelper(node, child.getAttribute('url')))
if child.hasAttribute('target'):
target = child.getAttribute('target')
if target == '': target = '_blank'
else:
target = mangle.entities(child.getAttribute('target'))
linktext = self._renderEditableHelper(child)
if target:
tag = '' % (url, target)
else:
tag = '' % url
result.append(tag)
result.append(linktext)
result.append('')
elif child.nodeName == 'underline':
result.append('')
result.append(self._renderEditableHelper(child))
result.append('')
elif child.nodeName == 'index':
result.append('')
result.append(mangle.entities(child.getAttribute('name')))
result.append('')
elif child.nodeName == 'acronym':
if child.getAttribute('title'):
result.append(
'' % child.getAttribute('title'))
else:
result.append('')
result.append(self._renderEditableHelper(child))
result.append('')
elif child.nodeName == 'abbr':
result.append('')
if child.getAttribute('title'):
result.append(
' title="%s"' % child.getAttribute('title'))
result.append(self._renderEditableHelper(child))
result.append('')
elif child.nodeName == 'br':
result.append('\n')
else:
result.append(
'ERROR %s ERROR ' % self._renderEditableHelper(child))
return ''.join(result)
def _linkHelper(self, context, path):
# If path is empty (can it be?), just return it
if path == '':
return path
# If it is a url already, return it:
if _url_match.match(path):
return path
# Is it a query of anchor fragment? If so, return it
if path[0] in ['?', '#']:
return path
# It is not an URL, query or anchor, so treat it as a path.
# If it is a relative path, treat is as such:
if not path.startswith('/'):
container = context.get_container()
return container.absolute_url() + '/' + path
# If it is an absolute path, try to traverse it to
# a Zope/Silva object and get the URL for that.
splitpath = [p.encode('ascii','ignore') for p in path.split('/') ]
obj = context.restrictedTraverse(splitpath, None)
if obj is None:
# Was not found, maybe the link is broken, but maybe it's just
# due to virtual hosting situations or whatever.
return path
if hasattr(obj.aq_base, 'absolute_url'):
# There are some cases where the object we find
# does not have the absolute_url method.
return obj.absolute_url()
# In all other cases:
return path
InitializeClass(ParagraphSupport)
class HeadingSupport(ParagraphSupport):
""" Mixed content support for SilvaDocument headings
"""
security = ClassSecurityInfo()
security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'parse')
def parse(self, inputstr):
""" Parse input string to a DOM
"""
from sprout.silvasubset import HEADING_SUBSET
inputstr = unicode(inputstr, 'UTF-8')
node = self._node
# remove all old subnodes of node
while node.hasChildNodes():
node.removeChild(node.firstChild)
HEADING_SUBSET.filteredParse(inputstr, node)
InitializeClass(HeadingSupport)
class PreSupport(ParagraphSupport):
""" Mixed content support for SilvaDocument pre elements
"""
security = ClassSecurityInfo()
security.declareProtected(SilvaPermissions.ChangeSilvaContent, 'parse')
def parse(self, inputstr):
"""Add the pre element to the dom
Don't have to much work here, since no markup is allowed
"""
# Since we don't use Formulator we get UTF8 from the forms,
# so decode to unicode manually here.
inputstr = unicode(inputstr, 'utf-8')
inputstr = self._unifyLineBreak(inputstr)
node = self._node
doc = node.ownerDocument
# remove all old subnodes of node
while node.hasChildNodes():
node.removeChild(node.firstChild)
node.appendChild(doc.createTextNode(inputstr))
security.declareProtected(
SilvaPermissions.ChangeSilvaContent, 'renderEditable')
def renderEditable(self):
result = ""
node = self._node
for child in node.childNodes:
assert child.nodeType == child.TEXT_NODE
assert child.firstChild is None
result += child.nodeValue
return result
def _unifyLineBreak(self, inputstr):
""" Returns data with unambigous line breaks, i.e only \n.
"""
if inputstr.find('\n') == -1 and inputstr.find('\r') > -1:
# looks like mac
return inputstr.replace('\r', '\n')
else:
# looks like windows
return inputstr.replace('\r', '')
# looks like unix :)
return inputstr
InitializeClass(PreSupport)