[bin] job.py: if the specified user does not exist in the DB, try with a user named 'admin'; [gen] Calendar: allow to insert additional, custom (XHTML or textual) info in any cell of the calendar, via new attribute Calendar.additionalInfo; [pod] some code refactoring; xhtml2odt: allow, when converting tables, to take into account attributes 'width' of tds; bugfix when converting 'href' attrs of 'a' tags.
This commit is contained in:
parent
ba148c51aa
commit
caca61516f
|
@ -56,6 +56,9 @@ else:
|
||||||
# Log as Zope admin
|
# Log as Zope admin
|
||||||
from AccessControl.SecurityManagement import newSecurityManager
|
from AccessControl.SecurityManagement import newSecurityManager
|
||||||
user = app.acl_users.getUserById(zopeUser)
|
user = app.acl_users.getUserById(zopeUser)
|
||||||
|
if not user:
|
||||||
|
# Try with user "admin"
|
||||||
|
user = app.acl_users.getUserById('admin')
|
||||||
if not hasattr(user, 'aq_base'):
|
if not hasattr(user, 'aq_base'):
|
||||||
user = user.__of__(app.acl_users)
|
user = user.__of__(app.acl_users)
|
||||||
newSecurityManager(None, user)
|
newSecurityManager(None, user)
|
||||||
|
|
|
@ -17,8 +17,8 @@ class Calendar(Type):
|
||||||
specificWritePermission=False, width=None, height=300,
|
specificWritePermission=False, width=None, height=300,
|
||||||
colspan=1, master=None, masterValue=None, focus=False,
|
colspan=1, master=None, masterValue=None, focus=False,
|
||||||
mapping=None, label=None, maxEventLength=50,
|
mapping=None, label=None, maxEventLength=50,
|
||||||
otherCalendars=None, startDate=None, endDate=None,
|
otherCalendars=None, additionalInfo=None, startDate=None,
|
||||||
defaultDate=None):
|
endDate=None, defaultDate=None):
|
||||||
Type.__init__(self, validator, (0,1), None, default, False, False,
|
Type.__init__(self, validator, (0,1), None, default, False, False,
|
||||||
show, page, group, layouts, move, False, False,
|
show, page, group, layouts, move, False, False,
|
||||||
specificReadPermission, specificWritePermission,
|
specificReadPermission, specificWritePermission,
|
||||||
|
@ -52,6 +52,12 @@ class Calendar(Type):
|
||||||
# leading "#" when relevant) into which events of the calendar must
|
# leading "#" when relevant) into which events of the calendar must
|
||||||
# appear.
|
# appear.
|
||||||
self.otherCalendars = otherCalendars
|
self.otherCalendars = otherCalendars
|
||||||
|
# One may want to add custom information in the calendar. When a method
|
||||||
|
# is given in p_additionalInfo, for every cell of the month view, this
|
||||||
|
# method will be called with a single arg (the cell's date). The
|
||||||
|
# method's result (a string that can hold text or a chunk of XHTML) will
|
||||||
|
# be inserted in the cell.
|
||||||
|
self.additionalInfo = additionalInfo
|
||||||
# One may limit event encoding and viewing to a limited period of time,
|
# One may limit event encoding and viewing to a limited period of time,
|
||||||
# via p_startDate and p_endDate. Those parameters, if given, must hold
|
# via p_startDate and p_endDate. Those parameters, if given, must hold
|
||||||
# methods accepting no arg and returning a Zope DateTime instance.
|
# methods accepting no arg and returning a Zope DateTime instance.
|
||||||
|
@ -131,6 +137,13 @@ class Calendar(Type):
|
||||||
res[i][1] = res[i][0].getField(res[i][1])
|
res[i][1] = res[i][0].getField(res[i][1])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def getAdditionalInfoAt(self, obj, date):
|
||||||
|
'''If the user has specified a method in self.additionalInfo, we call
|
||||||
|
it for displaying this additional info in the calendar, at some
|
||||||
|
p_date.'''
|
||||||
|
if not self.additionalInfo: return
|
||||||
|
return self.additionalInfo(obj.appy(), date)
|
||||||
|
|
||||||
def getEventTypes(self, obj):
|
def getEventTypes(self, obj):
|
||||||
'''Returns the (dynamic or static) event types as defined in
|
'''Returns the (dynamic or static) event types as defined in
|
||||||
self.eventTypes.'''
|
self.eventTypes.'''
|
||||||
|
@ -158,6 +171,13 @@ class Calendar(Type):
|
||||||
res = days[day]
|
res = days[day]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def getEventTypeAt(self, obj, date):
|
||||||
|
'''Returns the event type of the first event defined at p_day, or None
|
||||||
|
if unspecified.'''
|
||||||
|
events = self.getEventsAt(obj, date, asDict=False)
|
||||||
|
if not events: return
|
||||||
|
return events[0].eventType
|
||||||
|
|
||||||
def hasEventsAt(self, obj, date, otherEvents):
|
def hasEventsAt(self, obj, date, otherEvents):
|
||||||
'''Returns True if, at p_date, an event is found of the same type as
|
'''Returns True if, at p_date, an event is found of the same type as
|
||||||
p_otherEvents.'''
|
p_otherEvents.'''
|
||||||
|
@ -202,11 +222,20 @@ class Calendar(Type):
|
||||||
else:
|
else:
|
||||||
return DateTime() # Now
|
return DateTime() # Now
|
||||||
|
|
||||||
def createEvent(self, obj, date, handleEventSpan=True):
|
def createEvent(self, obj, date, eventType=None, eventSpan=None,
|
||||||
'''Create a new event in the calendar, at some p_date (day). If
|
handleEventSpan=True):
|
||||||
p_handleEventSpan is True, we will use rq["eventSpan"] and also
|
'''Create a new event in the calendar, at some p_date (day).
|
||||||
|
If p_eventType is given, it is used; else, rq['eventType'] is used.
|
||||||
|
If p_handleEventSpan is True, we will use p_eventSpan (or
|
||||||
|
rq["eventSpan"] if p_eventSpan is not given) and also
|
||||||
create the same event for successive days.'''
|
create the same event for successive days.'''
|
||||||
|
obj = obj.o # Ensure p_obj is not a wrapper.
|
||||||
rq = obj.REQUEST
|
rq = obj.REQUEST
|
||||||
|
# Get values from parameters
|
||||||
|
if not eventType: eventType = rq['eventType']
|
||||||
|
if handleEventSpan and not eventSpan:
|
||||||
|
eventSpan = rq.get('eventSpan', None)
|
||||||
|
# Split the p_date into separate parts
|
||||||
year, month, day = date.year(), date.month(), date.day()
|
year, month, day = date.year(), date.month(), date.day()
|
||||||
# Check that the "preferences" dict exists or not.
|
# Check that the "preferences" dict exists or not.
|
||||||
if not hasattr(obj.aq_base, self.name):
|
if not hasattr(obj.aq_base, self.name):
|
||||||
|
@ -230,11 +259,11 @@ class Calendar(Type):
|
||||||
daysDict[day] = events = PersistentList()
|
daysDict[day] = events = PersistentList()
|
||||||
# Create and store the event, excepted if an event already exists.
|
# Create and store the event, excepted if an event already exists.
|
||||||
if not events:
|
if not events:
|
||||||
event = Object(eventType=rq['eventType'])
|
event = Object(eventType=eventType)
|
||||||
events.append(event)
|
events.append(event)
|
||||||
# Span the event on the successive days if required
|
# Span the event on the successive days if required
|
||||||
if handleEventSpan and rq['eventSpan']:
|
if handleEventSpan and eventSpan:
|
||||||
nbOfDays = min(int(rq['eventSpan']), self.maxEventLength)
|
nbOfDays = min(int(eventSpan), self.maxEventLength)
|
||||||
for i in range(nbOfDays):
|
for i in range(nbOfDays):
|
||||||
date = date + 1
|
date = date + 1
|
||||||
self.createEvent(obj, date, handleEventSpan=False)
|
self.createEvent(obj, date, handleEventSpan=False)
|
||||||
|
@ -243,6 +272,7 @@ class Calendar(Type):
|
||||||
'''Deletes an event. It actually deletes all events at rq['day'].
|
'''Deletes an event. It actually deletes all events at rq['day'].
|
||||||
If p_handleEventSpan is True, we will use rq["deleteNext"] to
|
If p_handleEventSpan is True, we will use rq["deleteNext"] to
|
||||||
delete successive events, too.'''
|
delete successive events, too.'''
|
||||||
|
obj = obj.o # Ensure p_obj is not a wrapper.
|
||||||
rq = obj.REQUEST
|
rq = obj.REQUEST
|
||||||
if not self.getEventsAt(obj, date): return
|
if not self.getEventsAt(obj, date): return
|
||||||
daysDict = getattr(obj, self.name)[date.year()][date.month()]
|
daysDict = getattr(obj, self.name)[date.year()][date.month()]
|
||||||
|
|
|
@ -104,6 +104,9 @@
|
||||||
tal:attributes="style python: 'color: %s;; font-style: italic' % event['color']"></div>
|
tal:attributes="style python: 'color: %s;; font-style: italic' % event['color']"></div>
|
||||||
</tal:e>
|
</tal:e>
|
||||||
</tal:others>
|
</tal:others>
|
||||||
|
<tal:comment replace="nothing">Additional info</tal:comment>
|
||||||
|
<tal:info define="info python: contextObj.callField(fieldName,'getAdditionalInfoAt', contextObj, date)"
|
||||||
|
condition="info" replace="structure info"/>
|
||||||
</tal:day>
|
</tal:day>
|
||||||
</td>
|
</td>
|
||||||
</tal:td>
|
</tal:td>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import time
|
import time
|
||||||
from appy.shared.utils import Traceback
|
from appy.shared.utils import Traceback
|
||||||
|
from appy.shared.xml_parser import escapeXhtml
|
||||||
|
|
||||||
# Some POD-specific constants --------------------------------------------------
|
# Some POD-specific constants --------------------------------------------------
|
||||||
XHTML_HEADINGS = ('h1', 'h2', 'h3', 'h4', 'h5', 'h6')
|
XHTML_HEADINGS = ('h1', 'h2', 'h3', 'h4', 'h5', 'h6')
|
||||||
|
@ -27,8 +28,6 @@ XHTML_PARAGRAPH_TAGS = XHTML_HEADINGS + XHTML_LISTS + ('p',)
|
||||||
XHTML_PARAGRAPH_TAGS_NO_LISTS = XHTML_HEADINGS + ('p',)
|
XHTML_PARAGRAPH_TAGS_NO_LISTS = XHTML_HEADINGS + ('p',)
|
||||||
XHTML_INNER_TAGS = ('b', 'i', 'u', 'em')
|
XHTML_INNER_TAGS = ('b', 'i', 'u', 'em')
|
||||||
XHTML_UNSTYLABLE_TAGS = XHTML_LISTS + ('li', 'a')
|
XHTML_UNSTYLABLE_TAGS = XHTML_LISTS + ('li', 'a')
|
||||||
XML_SPECIAL_CHARS = {'<': '<', '>': '>', '&': '&', '"': '"',
|
|
||||||
"'": '''}
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class PodError(Exception):
|
class PodError(Exception):
|
||||||
|
@ -83,17 +82,6 @@ class PodError(Exception):
|
||||||
buffer.write('</%s>' % withinElement.OD.elem)
|
buffer.write('</%s>' % withinElement.OD.elem)
|
||||||
dump = staticmethod(dump)
|
dump = staticmethod(dump)
|
||||||
|
|
||||||
def convertToXhtml(s):
|
# XXX To remove, present for backward compatibility only.
|
||||||
'''Produces the XHTML-friendly version of p_s.'''
|
convertToXhtml = escapeXhtml
|
||||||
res = ''
|
|
||||||
for c in s:
|
|
||||||
if XML_SPECIAL_CHARS.has_key(c):
|
|
||||||
res += XML_SPECIAL_CHARS[c]
|
|
||||||
elif c == '\n':
|
|
||||||
res += '<br/>'
|
|
||||||
elif c == '\r':
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
res += c
|
|
||||||
return res
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -20,11 +20,11 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from xml.sax.saxutils import quoteattr
|
from xml.sax.saxutils import quoteattr
|
||||||
from appy.pod import PodError, XML_SPECIAL_CHARS
|
from appy.shared.xml_parser import xmlPrologue, escapeXml
|
||||||
|
from appy.pod import PodError
|
||||||
from appy.pod.elements import *
|
from appy.pod.elements import *
|
||||||
from appy.pod.actions import IfAction, ElseAction, ForAction, VariableAction, \
|
from appy.pod.actions import IfAction, ElseAction, ForAction, VariableAction, \
|
||||||
NullAction
|
NullAction
|
||||||
from appy.shared import xmlPrologue
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class ParsingError(Exception): pass
|
class ParsingError(Exception): pass
|
||||||
|
@ -157,11 +157,7 @@ class Buffer:
|
||||||
|
|
||||||
def dumpContent(self, content):
|
def dumpContent(self, content):
|
||||||
'''Dumps string p_content into the buffer.'''
|
'''Dumps string p_content into the buffer.'''
|
||||||
for c in content:
|
self.write(escapeXml(content))
|
||||||
if XML_SPECIAL_CHARS.has_key(c):
|
|
||||||
self.write(XML_SPECIAL_CHARS[c])
|
|
||||||
else:
|
|
||||||
self.write(c)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class FileBuffer(Buffer):
|
class FileBuffer(Buffer):
|
||||||
|
|
|
@ -10,8 +10,9 @@ class OdtTable:
|
||||||
tns = 'table:'
|
tns = 'table:'
|
||||||
txns = 'text:'
|
txns = 'text:'
|
||||||
|
|
||||||
def __init__(self, name, paraStyle, cellStyle, nbOfCols,
|
def __init__(self, name, paraStyle='podTablePara', cellStyle='podTableCell',
|
||||||
paraHeaderStyle=None, cellHeaderStyle=None, html=False):
|
nbOfCols=1, paraHeaderStyle=None, cellHeaderStyle=None,
|
||||||
|
html=False):
|
||||||
# An ODT table must have a name. In the case of an HTML table, p_name
|
# An ODT table must have a name. In the case of an HTML table, p_name
|
||||||
# represents the CSS class for the whole table.
|
# represents the CSS class for the whole table.
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -24,7 +25,7 @@ class OdtTable:
|
||||||
# The default style of every paragraph within a header cell
|
# The default style of every paragraph within a header cell
|
||||||
self.paraHeaderStyle = paraHeaderStyle or paraStyle
|
self.paraHeaderStyle = paraHeaderStyle or paraStyle
|
||||||
# The default style of every header cell
|
# The default style of every header cell
|
||||||
self.cellHeaderStyle = cellHeaderStyle or cellStyle
|
self.cellHeaderStyle = cellHeaderStyle or 'podTableHeaderCell'
|
||||||
# The buffer where the resulting table will be rendered
|
# The buffer where the resulting table will be rendered
|
||||||
self.res = ''
|
self.res = ''
|
||||||
# Do we need to generate an HTML table instead of an ODT table ?
|
# Do we need to generate an HTML table instead of an ODT table ?
|
||||||
|
|
|
@ -21,9 +21,9 @@ import os, os.path, sys, zipfile, re, shutil
|
||||||
import appy.shared.test
|
import appy.shared.test
|
||||||
from appy.shared.test import TesterError
|
from appy.shared.test import TesterError
|
||||||
from appy.shared.utils import FolderDeleter
|
from appy.shared.utils import FolderDeleter
|
||||||
|
from appy.shared.xml_parser import escapeXml
|
||||||
from appy.pod.odf_parser import OdfEnvironment, OdfParser
|
from appy.pod.odf_parser import OdfEnvironment, OdfParser
|
||||||
from appy.pod.renderer import Renderer
|
from appy.pod.renderer import Renderer
|
||||||
from appy.pod import XML_SPECIAL_CHARS
|
|
||||||
|
|
||||||
# TesterError-related constants ------------------------------------------------
|
# TesterError-related constants ------------------------------------------------
|
||||||
TEMPLATE_NOT_FOUND = 'Template file "%s" was not found.'
|
TEMPLATE_NOT_FOUND = 'Template file "%s" was not found.'
|
||||||
|
@ -70,12 +70,7 @@ class AnnotationsRemover(OdfParser):
|
||||||
self.res += '</%s>' % elem
|
self.res += '</%s>' % elem
|
||||||
def characters(self, content):
|
def characters(self, content):
|
||||||
e = OdfParser.characters(self, content)
|
e = OdfParser.characters(self, content)
|
||||||
if not self.ignore:
|
if not self.ignore: self.res += escapeXml(content)
|
||||||
for c in content:
|
|
||||||
if XML_SPECIAL_CHARS.has_key(c):
|
|
||||||
self.res += XML_SPECIAL_CHARS[c]
|
|
||||||
else:
|
|
||||||
self.res += c
|
|
||||||
def getResult(self):
|
def getResult(self):
|
||||||
return self.res
|
return self.res
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import xml.sax, time, random
|
import xml.sax, time, random
|
||||||
from appy.shared.xml_parser import XmlEnvironment, XmlParser
|
from appy.shared.xml_parser import XmlEnvironment, XmlParser, escapeXml
|
||||||
from appy.pod.odf_parser import OdfEnvironment
|
from appy.pod.odf_parser import OdfEnvironment
|
||||||
from appy.pod.styles_manager import Style
|
from appy.pod.styles_manager import Style
|
||||||
from appy.pod import *
|
from appy.pod import *
|
||||||
|
@ -235,6 +235,10 @@ class HtmlTable:
|
||||||
# The following list stores, for every column, the size of the biggest
|
# The following list stores, for every column, the size of the biggest
|
||||||
# content of all its cells.
|
# content of all its cells.
|
||||||
self.columnContentSizes = []
|
self.columnContentSizes = []
|
||||||
|
# The following list stores, for every column, its width, if specified.
|
||||||
|
# If widths are found, self.columnContentSizes will not be used:
|
||||||
|
# self.columnWidths will be used instead.
|
||||||
|
self.columnWidths = []
|
||||||
|
|
||||||
def computeColumnStyles(self, renderer):
|
def computeColumnStyles(self, renderer):
|
||||||
'''Once the table has been completely parsed, self.columnContentSizes
|
'''Once the table has been completely parsed, self.columnContentSizes
|
||||||
|
@ -242,22 +246,34 @@ class HtmlTable:
|
||||||
of every column and create the corresponding style declarations, in
|
of every column and create the corresponding style declarations, in
|
||||||
p_renderer.dynamicStyles.'''
|
p_renderer.dynamicStyles.'''
|
||||||
total = 65000.0 # A number representing the total width of the table
|
total = 65000.0 # A number representing the total width of the table
|
||||||
# Ensure first that self.columnContentSizes is correct
|
# Use (a) self.columnWidths if complete, or
|
||||||
if (len(self.columnContentSizes) != self.nbOfColumns) or \
|
# (b) self.columnContentSizes if complete, or
|
||||||
(None in self.columnContentSizes):
|
# (c) a fixed width else.
|
||||||
# There was a problem while parsing the table. Set every column
|
if self.columnWidths and (len(self.columnWidths) == self.nbOfColumns) \
|
||||||
# with the same width.
|
and (None not in self.columnWidths):
|
||||||
widths = [int(total/self.nbOfColumns)] * self.nbOfColumns
|
# Use self.columnWidths
|
||||||
|
toUse = self.columnWidths
|
||||||
|
# Use self.columnContentSizes if complete
|
||||||
|
elif (len(self.columnContentSizes) == self.nbOfColumns) and \
|
||||||
|
(None not in self.columnContentSizes):
|
||||||
|
# Use self.columnContentSizes
|
||||||
|
toUse = self.columnContentSizes
|
||||||
else:
|
else:
|
||||||
|
toUse = None
|
||||||
|
if toUse:
|
||||||
widths = []
|
widths = []
|
||||||
# Compute the sum of all column content sizes
|
# Compute the sum of all column content sizes
|
||||||
contentTotal = 0
|
contentTotal = 0
|
||||||
for size in self.columnContentSizes: contentTotal += size
|
for size in toUse: contentTotal += size
|
||||||
contentTotal = float(contentTotal)
|
contentTotal = float(contentTotal)
|
||||||
for size in self.columnContentSizes:
|
for size in toUse:
|
||||||
width = int((size/contentTotal) * total)
|
width = int((size/contentTotal) * total)
|
||||||
widths.append(width)
|
widths.append(width)
|
||||||
# Compute style declatation corresponding to every column.
|
else:
|
||||||
|
# There was a problem while parsing the table. Set every column
|
||||||
|
# with the same width.
|
||||||
|
widths = [int(total/self.nbOfColumns)] * self.nbOfColumns
|
||||||
|
# Compute style declaration corresponding to every column.
|
||||||
s = self.styleNs
|
s = self.styleNs
|
||||||
i = 0
|
i = 0
|
||||||
for width in widths:
|
for width in widths:
|
||||||
|
@ -321,14 +337,10 @@ class XhtmlEnvironment(XmlEnvironment):
|
||||||
currentElem.addInnerParagraph(self)
|
currentElem.addInnerParagraph(self)
|
||||||
# Dump and reinitialize the current content
|
# Dump and reinitialize the current content
|
||||||
content = self.currentContent.strip('\n\t')
|
content = self.currentContent.strip('\n\t')
|
||||||
contentSize = len(content)
|
|
||||||
for c in content:
|
|
||||||
# We remove leading and trailing carriage returns, but not
|
# We remove leading and trailing carriage returns, but not
|
||||||
# whitespace because whitespace may be part of the text to dump.
|
# whitespace because whitespace may be part of the text to dump.
|
||||||
if XML_SPECIAL_CHARS.has_key(c):
|
contentSize = len(content)
|
||||||
self.dumpString(XML_SPECIAL_CHARS[c])
|
self.dumpString(escapeXml(content))
|
||||||
else:
|
|
||||||
self.dumpString(c)
|
|
||||||
self.currentContent = u''
|
self.currentContent = u''
|
||||||
# If we are within a table cell, update the total size of cell content.
|
# If we are within a table cell, update the total size of cell content.
|
||||||
if self.currentTables and self.currentTables[-1].inCell:
|
if self.currentTables and self.currentTables[-1].inCell:
|
||||||
|
@ -444,6 +456,16 @@ class XhtmlEnvironment(XmlEnvironment):
|
||||||
# If we are in the first row of a table, update columns count
|
# If we are in the first row of a table, update columns count
|
||||||
if not table.firstRowParsed:
|
if not table.firstRowParsed:
|
||||||
table.nbOfColumns += colspan
|
table.nbOfColumns += colspan
|
||||||
|
if attrs.has_key('width') and (colspan == 1):
|
||||||
|
# Get the width, keep figures only.
|
||||||
|
width = ''
|
||||||
|
for c in attrs['width']:
|
||||||
|
if c.isdigit(): width += c
|
||||||
|
width = int(width)
|
||||||
|
# Ensure self.columnWidths is long enough
|
||||||
|
while (len(table.columnWidths)-1) < table.cellIndex:
|
||||||
|
table.columnWidths.append(None)
|
||||||
|
table.columnWidths[table.cellIndex] = width
|
||||||
return currentElem
|
return currentElem
|
||||||
|
|
||||||
def onElementEnd(self, elem):
|
def onElementEnd(self, elem):
|
||||||
|
@ -525,7 +547,8 @@ class XhtmlParser(XmlParser):
|
||||||
elif elem == 'a':
|
elif elem == 'a':
|
||||||
e.dumpString('<%s %s:type="simple"' % (odfTag, e.linkNs))
|
e.dumpString('<%s %s:type="simple"' % (odfTag, e.linkNs))
|
||||||
if attrs.has_key('href'):
|
if attrs.has_key('href'):
|
||||||
e.dumpString(' %s:href="%s"' % (e.linkNs, attrs['href']))
|
e.dumpString(' %s:href="%s"' % (e.linkNs,
|
||||||
|
escapeXml(attrs['href'])))
|
||||||
e.dumpString('>')
|
e.dumpString('>')
|
||||||
elif elem in XHTML_LISTS:
|
elif elem in XHTML_LISTS:
|
||||||
prologue = ''
|
prologue = ''
|
||||||
|
|
|
@ -19,7 +19,6 @@ mimeTypesExts = {
|
||||||
'image/pjpeg' : 'jpg',
|
'image/pjpeg' : 'jpg',
|
||||||
'image/gif' : 'gif'
|
'image/gif' : 'gif'
|
||||||
}
|
}
|
||||||
xmlPrologue = '<?xml version="1.0" encoding="utf-8" ?>\n'
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class UnmarshalledFile:
|
class UnmarshalledFile:
|
||||||
|
|
|
@ -22,16 +22,19 @@ import xml.sax, difflib, types, cgi
|
||||||
from xml.sax.handler import ContentHandler, ErrorHandler, feature_external_ges
|
from xml.sax.handler import ContentHandler, ErrorHandler, feature_external_ges
|
||||||
from xml.sax.xmlreader import InputSource
|
from xml.sax.xmlreader import InputSource
|
||||||
from xml.sax import SAXParseException
|
from xml.sax import SAXParseException
|
||||||
from appy.shared import UnicodeBuffer, xmlPrologue
|
from appy.shared import UnicodeBuffer
|
||||||
from appy.shared.errors import AppyError
|
from appy.shared.errors import AppyError
|
||||||
from appy.shared.utils import sequenceTypes
|
from appy.shared.utils import sequenceTypes
|
||||||
from appy.shared.css import parseStyleAttribute
|
from appy.shared.css import parseStyleAttribute
|
||||||
|
|
||||||
# Constants --------------------------------------------------------------------
|
# Constants --------------------------------------------------------------------
|
||||||
|
xmlPrologue = '<?xml version="1.0" encoding="utf-8" ?>\n'
|
||||||
CONVERSION_ERROR = '"%s" value "%s" could not be converted by the XML ' \
|
CONVERSION_ERROR = '"%s" value "%s" could not be converted by the XML ' \
|
||||||
'unmarshaller.'
|
'unmarshaller.'
|
||||||
CUSTOM_CONVERSION_ERROR = 'Custom converter for "%s" values produced an ' \
|
CUSTOM_CONVERSION_ERROR = 'Custom converter for "%s" values produced an ' \
|
||||||
'error while converting value "%s". %s'
|
'error while converting value "%s". %s'
|
||||||
|
XML_SPECIAL_CHARS = {'<': '<', '>': '>', '&': '&', '"': '"',
|
||||||
|
"'": '''}
|
||||||
XML_ENTITIES = {'lt': '<', 'gt': '>', 'amp': '&', 'quot': "'", 'apos': "'"}
|
XML_ENTITIES = {'lt': '<', 'gt': '>', 'amp': '&', 'quot': "'", 'apos': "'"}
|
||||||
HTML_ENTITIES = {
|
HTML_ENTITIES = {
|
||||||
'iexcl': '¡', 'cent': '¢', 'pound': '£', 'curren': '€', 'yen': '¥',
|
'iexcl': '¡', 'cent': '¢', 'pound': '£', 'curren': '€', 'yen': '¥',
|
||||||
|
@ -60,6 +63,38 @@ for k, v in htmlentitydefs.entitydefs.iteritems():
|
||||||
if not HTML_ENTITIES.has_key(k) and not XML_ENTITIES.has_key(k):
|
if not HTML_ENTITIES.has_key(k) and not XML_ENTITIES.has_key(k):
|
||||||
HTML_ENTITIES[k] = ''
|
HTML_ENTITIES[k] = ''
|
||||||
|
|
||||||
|
def escapeXml(s):
|
||||||
|
'''Returns p_s, whose XML special chars have been replaced with escaped XML
|
||||||
|
entities.'''
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
res = u''
|
||||||
|
else:
|
||||||
|
res = ''
|
||||||
|
for c in s:
|
||||||
|
if XML_SPECIAL_CHARS.has_key(c):
|
||||||
|
res += XML_SPECIAL_CHARS[c]
|
||||||
|
else:
|
||||||
|
res += c
|
||||||
|
return res
|
||||||
|
|
||||||
|
def escapeXhtml(s):
|
||||||
|
'''Return p_s, whose XHTML special chars and carriage return chars have
|
||||||
|
been replaced with corresponding XHTML entities.'''
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
res = u''
|
||||||
|
else:
|
||||||
|
res = ''
|
||||||
|
for c in s:
|
||||||
|
if XML_SPECIAL_CHARS.has_key(c):
|
||||||
|
res += XML_SPECIAL_CHARS[c]
|
||||||
|
elif c == '\n':
|
||||||
|
res += '<br/>'
|
||||||
|
elif c == '\r':
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
res += c
|
||||||
|
return res
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class XmlElement:
|
class XmlElement:
|
||||||
'''Represents an XML tag.'''
|
'''Represents an XML tag.'''
|
||||||
|
|
Loading…
Reference in a new issue