appy.gen: improved cleaning and formatting of XHTML content; appy.pod: added some default appy-related table styles for producing cells with text in bold/normal, aligned right/left, etc.
This commit is contained in:
parent
d3a2b85a10
commit
028040351c
|
@ -11,8 +11,9 @@ from appy.gen.utils import GroupDescr, Keywords, getClassName, SomeObjects
|
||||||
import appy.pod
|
import appy.pod
|
||||||
from appy.pod.renderer import Renderer
|
from appy.pod.renderer import Renderer
|
||||||
from appy.shared.data import countries
|
from appy.shared.data import countries
|
||||||
|
from appy.shared.xml_parser import XhtmlCleaner
|
||||||
from appy.shared.utils import Traceback, getOsTempFolder, formatNumber, \
|
from appy.shared.utils import Traceback, getOsTempFolder, formatNumber, \
|
||||||
XhtmlCleaner, FileWrapper, sequenceTypes
|
FileWrapper, sequenceTypes
|
||||||
|
|
||||||
# Default Appy permissions -----------------------------------------------------
|
# Default Appy permissions -----------------------------------------------------
|
||||||
r, w, d = ('read', 'write', 'delete')
|
r, w, d = ('read', 'write', 'delete')
|
||||||
|
@ -1238,8 +1239,7 @@ class String(Type):
|
||||||
# When image upload is allowed, ckeditor inserts some "style" attrs
|
# When image upload is allowed, ckeditor inserts some "style" attrs
|
||||||
# (ie for image size when images are resized). So in this case we
|
# (ie for image size when images are resized). So in this case we
|
||||||
# can't remove style-related information.
|
# can't remove style-related information.
|
||||||
keepStyles = self.allowImageUpload or self.richText
|
value = XhtmlCleaner().clean(value, keepStyles=self.richText)
|
||||||
value = XhtmlCleaner.clean(value, keepStyles=keepStyles)
|
|
||||||
Type.store(self, obj, value)
|
Type.store(self, obj, value)
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value):
|
def getFormattedValue(self, obj, value):
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
body { font: 75% Helvetica,Arial,sans-serif; background-color: #EAEAEA;
|
body { font: 75% Helvetica,Arial,sans-serif; background-color: #EAEAEA;
|
||||||
margin-top: 18px}
|
margin-top: 18px}
|
||||||
pre { font: 100% Helvetica,Arial,sans-serif; margin: 0}
|
pre { font: 100% Helvetica,Arial,sans-serif; margin: 0}
|
||||||
h1 { font-size: 11pt; margin:0;}
|
h1 { font-size: 14pt; margin:0;}
|
||||||
h2 { font-size: 10pt; margin:0; font-style: italic; font-weight: normal;
|
h2 { font-size: 13pt; margin:0; font-style: italic; font-weight: normal;
|
||||||
background-color: #d7dee4}
|
background-color: #d7dee4}
|
||||||
h3 { font-size: 9pt; margin:0; font-weight: bold;}
|
h3 { font-size: 12pt; margin:0; font-weight: bold;}
|
||||||
|
h4 { font-size: 11pt; margin:0;}
|
||||||
|
h5 { font-size: 10pt; margin:0; font-style: italic; font-weight: normal;
|
||||||
|
background-color: #d7dee4}
|
||||||
|
h6 { font-size: 9pt; margin:0; font-weight: bold;}
|
||||||
a { text-decoration: none; color: #503737;}
|
a { text-decoration: none; color: #503737;}
|
||||||
a:visited { color: #503737;}
|
a:visited { color: #503737;}
|
||||||
table { font-size: 100%; border-spacing: 0px; border-collapse:collapse;}
|
table { font-size: 100%; border-spacing: 0px; border-collapse:collapse;}
|
||||||
|
@ -34,10 +38,15 @@ label { font-weight: 600; font-style: italic; line-height: 1.4em;}
|
||||||
legend { padding-bottom: 2px; padding-right: 3px; color: black;}
|
legend { padding-bottom: 2px; padding-right: 3px; color: black;}
|
||||||
ul { line-height: 1.2em; margin: 0 0 0.2em 0.6em; padding: 0;
|
ul { line-height: 1.2em; margin: 0 0 0.2em 0.6em; padding: 0;
|
||||||
list-style: none outside none;}
|
list-style: none outside none;}
|
||||||
li { margin: 0; background-image: url("ui/li.gif"); padding-left: 10px;
|
ul li { margin: 0; background-image: url("ui/li.gif"); padding-left: 10px;
|
||||||
background-repeat: no-repeat; background-position: 0 4px;}
|
background-repeat: no-repeat; background-position: 0 4px;}
|
||||||
img {border: 0}
|
img {border: 0}
|
||||||
|
|
||||||
|
/* Styles that apply when viewing content of XHTML fields, that mimic styles
|
||||||
|
that ckeditor uses for displaying XHTML content in the edit view. */
|
||||||
|
.xhtml { margin-top: 10px }
|
||||||
.xhtml img { margin-right: 5px }
|
.xhtml img { margin-right: 5px }
|
||||||
|
.xhtml p { margin: 3px 0 7px 0}
|
||||||
|
|
||||||
.main { width: 900px; background-color: white; box-shadow: 3px 3px 3px #A9A9A9;
|
.main { width: 900px; background-color: white; box-shadow: 3px 3px 3px #A9A9A9;
|
||||||
border-style: solid; border-width: 1px; border-color: grey}
|
border-style: solid; border-width: 1px; border-color: grey}
|
||||||
|
|
|
@ -30,4 +30,8 @@ CKEDITOR.editorConfig = function( config )
|
||||||
config.format_h2 = { element:'h2', attributes:{'style':'margin:0;padding:0'}};
|
config.format_h2 = { element:'h2', attributes:{'style':'margin:0;padding:0'}};
|
||||||
config.format_h3 = { element:'h3', attributes:{'style':'margin:0;padding:0'}};
|
config.format_h3 = { element:'h3', attributes:{'style':'margin:0;padding:0'}};
|
||||||
config.format_h4 = { element:'h4', attributes:{'style':'margin:0;padding:0'}};
|
config.format_h4 = { element:'h4', attributes:{'style':'margin:0;padding:0'}};
|
||||||
|
config.entities = false;
|
||||||
|
config.entities_greek = false;
|
||||||
|
config.entities_latin = false;
|
||||||
|
config.fillEmptyBlocks = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,3 +12,4 @@ ol,ul,dl {
|
||||||
padding:0 40px;
|
padding:0 40px;
|
||||||
}
|
}
|
||||||
img { margin-right: 5px}
|
img { margin-right: 5px}
|
||||||
|
table { border-collapse: collapse; border-spacing: 0 }
|
||||||
|
|
|
@ -149,7 +149,7 @@ class ToolWrapper(AbstractWrapper):
|
||||||
'''Reindex all Appy objects. For some unknown reason, method
|
'''Reindex all Appy objects. For some unknown reason, method
|
||||||
catalog.refreshCatalog is not able to recatalog Appy objects.'''
|
catalog.refreshCatalog is not able to recatalog Appy objects.'''
|
||||||
if not startObject:
|
if not startObject:
|
||||||
# This is a global refresh. Clear the catallog completely, and then
|
# This is a global refresh. Clear the catalog completely, and then
|
||||||
# reindex all Appy-managed objects, ie those in folders "config"
|
# reindex all Appy-managed objects, ie those in folders "config"
|
||||||
# and "data".
|
# and "data".
|
||||||
# First, clear the catalog.
|
# First, clear the catalog.
|
||||||
|
|
|
@ -78,7 +78,7 @@ CONTENT_POD_STYLES = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
# Default font added by pod in content.xml
|
# Default font added by pod in content.xml
|
||||||
CONTENT_POD_FONTS = '<@style@:font-face style:name="PodStarSymbol" ' \
|
CONTENT_POD_FONTS = '<@style@:font-face @style@:name="PodStarSymbol" ' \
|
||||||
'@svg@:font-family="StarSymbol"/>'
|
'@svg@:font-family="StarSymbol"/>'
|
||||||
|
|
||||||
# Default text styles added by pod in styles.xml
|
# Default text styles added by pod in styles.xml
|
||||||
|
@ -213,7 +213,8 @@ class Renderer:
|
||||||
nsUris={'style': pe.NS_STYLE, 'svg': pe.NS_SVG}),
|
nsUris={'style': pe.NS_STYLE, 'svg': pe.NS_SVG}),
|
||||||
OdInsert(STYLES_POD_STYLES,
|
OdInsert(STYLES_POD_STYLES,
|
||||||
XmlElement('styles', nsUri=pe.NS_OFFICE),
|
XmlElement('styles', nsUri=pe.NS_OFFICE),
|
||||||
nsUris={'style': pe.NS_STYLE, 'fo': pe.NS_FO}))
|
nsUris={'style': pe.NS_STYLE, 'fo': pe.NS_FO,
|
||||||
|
'text': pe.NS_TEXT}))
|
||||||
self.stylesParser = self.createPodParser('styles.xml', context,
|
self.stylesParser = self.createPodParser('styles.xml', context,
|
||||||
stylesInserts)
|
stylesInserts)
|
||||||
# Stores the styles mapping
|
# Stores the styles mapping
|
||||||
|
|
|
@ -114,8 +114,30 @@
|
||||||
</@text@:list-level-style-number>
|
</@text@:list-level-style-number>
|
||||||
</@text@:list-style>
|
</@text@:list-style>
|
||||||
<@style@:style @style@:name="podImageLeft" @style@:family="graphic" @style@:parent-style-name="Graphics">
|
<@style@:style @style@:name="podImageLeft" @style@:family="graphic" @style@:parent-style-name="Graphics">
|
||||||
<@style@:graphic-properties @style@:run-through="foreground" @style@:wrap="parallel" @style@:number-wrapped-paragraphs="no-limit" @style@:wrap-contour="false" @style@:vertical-pos="top" @style@:vertical-rel="paragraph" @style@:horizontal-pos="left" @style@:horizontal-rel="paragraph" @style@:mirror="none" @fo@:clip="rect(0cm, 0cm, 0cm, 0cm)" @fo@:margin-right="0.3cm" @fo@:margin-bottom="0.2cm"/>
|
<@style@:graphic-properties @style@:run-through="foreground" @style@:wrap="parallel" @style@:number-wrapped-paragraphs="no-limit" @style@:wrap-contour="false" @style@:vertical-pos="top" @style@:vertical-rel="paragraph" @style@:horizontal-pos="left" @style@:horizontal-rel="paragraph" @style@:mirror="none" @fo@:clip="rect(0cm, 0cm, 0cm, 0cm)" @fo@:margin-right="0.3cm" @fo@:margin-bottom="0.2cm"/>
|
||||||
</@style@:style>
|
</@style@:style>
|
||||||
<@style@:style @style@:name="podImageRight" @style@:family="graphic" @style@:parent-style-name="Graphics">
|
<@style@:style @style@:name="podImageRight" @style@:family="graphic" @style@:parent-style-name="Graphics">
|
||||||
<@style@:graphic-properties @style@:run-through="foreground" @style@:wrap="parallel" @style@:number-wrapped-paragraphs="no-limit" @style@:wrap-contour="false" @style@:vertical-pos="top" @style@:vertical-rel="paragraph" @style@:horizontal-pos="right" @style@:horizontal-rel="paragraph" @style@:mirror="none" @fo@:clip="rect(0cm, 0cm, 0cm, 0cm)" @fo@:margin-left="0.3cm" @fo@:margin-bottom="0.2cm"/>
|
<@style@:graphic-properties @style@:run-through="foreground" @style@:wrap="parallel" @style@:number-wrapped-paragraphs="no-limit" @style@:wrap-contour="false" @style@:vertical-pos="top" @style@:vertical-rel="paragraph" @style@:horizontal-pos="right" @style@:horizontal-rel="paragraph" @style@:mirror="none" @fo@:clip="rect(0cm, 0cm, 0cm, 0cm)" @fo@:margin-left="0.3cm" @fo@:margin-bottom="0.2cm"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style @style@:name="podTablePara" @style@:family="paragraph" @style@:parent-style-name="Appy_Table_Content">
|
||||||
|
<@style@:text-properties @fo@:font-size="8pt" @fo@:font-weight="normal" @style@:font-weight-asian="normal" @style@:font-weight-complex="normal"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style @style@:name="podTableParaBold" @style@:family="paragraph" @style@:parent-style-name="Appy_Table_Content">
|
||||||
|
<@style@:text-properties @fo@:font-size="8pt" @fo@:font-weight="bold" @style@:font-weight-asian="bold" @style@:font-weight-complex="bold"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style @style@:name="podTableParaRight" @style@:family="paragraph" @style@:parent-style-name="Appy_Table_Content">
|
||||||
|
<@style@:paragraph-properties @fo@:text-align="end" @style@:justify-single-word="false"/>
|
||||||
|
<@style@:text-properties @fo@:font-size="8pt" @fo@:font-weight="normal" @style@:font-weight-asian="normal" @style@:font-weight-complex="normal"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style @style@:name="podTableParaBoldRight" @style@:family="paragraph" @style@:parent-style-name="Appy_Table_Content">
|
||||||
|
<@style@:paragraph-properties @fo@:text-align="end" @style@:justify-single-word="false"/>
|
||||||
|
<@style@:text-properties @fo@:font-size="8pt" @fo@:font-weight="bold" @style@:font-weight-asian="bold" @style@:font-weight-complex="bold"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style @style@:name="podTableCell" @style@:family="table-cell">
|
||||||
|
<@style@:table-cell-properties @fo@:padding="0.097cm" @fo@:border="0.018cm solid #000000"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style @style@:name="podTableHeaderCell" @style@:family="table-cell">
|
||||||
|
<@style@:table-cell-properties @fo@:background-color="#e6e6e6" @fo@:padding="0.097cm" @fo@:border="0.018cm solid #000000">
|
||||||
|
<@style@:background-image/>
|
||||||
|
</@style@:table-cell-properties>
|
||||||
</@style@:style>
|
</@style@:style>
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
<@style@:style @style@:name="podNumberStyle" @style@:display-name="POD Numbering Symbols" @style@:family="text"/>
|
<@style@:style @style@:name="podNumberStyle" @style@:display-name="POD Numbering Symbols" @style@:family="text"/>
|
||||||
<@style@:style @style@:name="podBulletStyle" @style@:display-name="POD Bullet Symbols" @style@:family="text">
|
<@style@:style @style@:name="podBulletStyle" @style@:display-name="POD Bullet Symbols" @style@:family="text">
|
||||||
<@style@:text-properties @style@:font-name="PodStarSymbol" @fo@:font-size="9pt"
|
<@style@:text-properties @style@:font-name="PodStarSymbol" @fo@:font-size="9pt"
|
||||||
@style@:font-name-asian="PodStarSymbol" @style@:font-size-asian="9pt"
|
@style@:font-name-asian="PodStarSymbol" @style@:font-size-asian="9pt"
|
||||||
@style@:font-name-complex="PodStarSymbol" @style@:font-size-complex="9pt"/>
|
@style@:font-name-complex="PodStarSymbol" @style@:font-size-complex="9pt"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style style:name="AppyStandard" style:family="paragraph" style:class="text" style:master-page-name="">
|
||||||
|
<@style@:paragraph-properties fo:margin-left="0cm" fo:margin-right="0cm" fo:margin-top="0.101cm" fo:margin-bottom="0.169cm" fo:text-indent="0cm" style:auto-text-indent="false" style:page-number="auto"/>
|
||||||
|
<@style@:text-properties style:font-name="DejaVu Sans" fo:font-size="10pt"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style @style@:name="Appy_Table_Content" @style@:display-name="Appy Table Contents" @style@:family="paragraph"
|
||||||
|
@style@:parent-style-name="AppyStandard" @style@:class="extra">
|
||||||
|
<@style@:paragraph-properties @fo@:margin-top="0cm" @fo@:margin-bottom="0cm" @text@:number-lines="false" @text@:line-number="0"/>
|
||||||
|
<@style@:text-properties @fo@:font-size="8pt"/>
|
||||||
|
</@style@:style>
|
||||||
|
<@style@:style @style@:name="Appy_Table_Heading" @style@:display-name="Appy Table Heading" @style@:family="paragraph"
|
||||||
|
@style@:parent-style-name="Appy_Table_Contents" @style@:class="extra">
|
||||||
|
<@style@:paragraph-properties @fo@:text-align="center" @style@:justify-single-word="false" @text@:number-lines="false"
|
||||||
|
@text@:line-number="0"/>
|
||||||
|
<@style@:text-properties @fo@:font-weight="bold" @style@:font-weight-asian="bold" @style@:font-weight-complex="bold"/>
|
||||||
</@style@:style>
|
</@style@:style>
|
|
@ -106,7 +106,8 @@ class Debianizer:
|
||||||
|
|
||||||
def __init__(self, app, out, appVersion='0.1.0',
|
def __init__(self, app, out, appVersion='0.1.0',
|
||||||
pythonVersions=('2.6',), zopePort=8080,
|
pythonVersions=('2.6',), zopePort=8080,
|
||||||
depends=('openoffice.org', 'imagemagick'), sign=False):
|
depends=('zope2.12', 'openoffice.org', 'imagemagick'),
|
||||||
|
sign=False):
|
||||||
# app is the path to the Python package to Debianize.
|
# app is the path to the Python package to Debianize.
|
||||||
self.app = app
|
self.app = app
|
||||||
self.appName = os.path.basename(app)
|
self.appName = os.path.basename(app)
|
||||||
|
@ -261,10 +262,6 @@ class Debianizer:
|
||||||
# Create postinst, a script that will:
|
# Create postinst, a script that will:
|
||||||
# - bytecompile Python files after the Debian install
|
# - bytecompile Python files after the Debian install
|
||||||
# - change ownership of some files if required
|
# - change ownership of some files if required
|
||||||
# - [in the case of a app-package] execute:
|
|
||||||
# apt-get -t squeeze-backports install zope2.12
|
|
||||||
# (if zope2.12 is defined as a simple dependency in field "Depends:"
|
|
||||||
# it will fail because it will not be searched in squeeze-backports).
|
|
||||||
# - [in the case of an app-package] call update-rc.d for starting it at
|
# - [in the case of an app-package] call update-rc.d for starting it at
|
||||||
# boot time.
|
# boot time.
|
||||||
f = file('postinst', 'w')
|
f = file('postinst', 'w')
|
||||||
|
@ -276,8 +273,6 @@ class Debianizer:
|
||||||
self.appName)
|
self.appName)
|
||||||
content += 'if [ -e %s ]\nthen\n%sfi\n' % (bin, cmds)
|
content += 'if [ -e %s ]\nthen\n%sfi\n' % (bin, cmds)
|
||||||
if self.appName != 'appy':
|
if self.appName != 'appy':
|
||||||
# Install zope2.12 from squeeze-backports
|
|
||||||
content += 'apt-get -t squeeze-backports install zope2.12\n'
|
|
||||||
# Allow user "zope", that runs the Zope instance, to write the
|
# Allow user "zope", that runs the Zope instance, to write the
|
||||||
# database and log files.
|
# database and log files.
|
||||||
content += 'chown -R zope:root /var/lib/%s\n' % self.appNameLower
|
content += 'chown -R zope:root /var/lib/%s\n' % self.appNameLower
|
||||||
|
|
|
@ -263,35 +263,6 @@ def formatNumber(n, sep=',', precision=2, tsep=' '):
|
||||||
res += sep + splitted[1]
|
res += sep + splitted[1]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
class XhtmlCleaner:
|
|
||||||
# Regular expressions used for cleaning.
|
|
||||||
classAttr = re.compile('class\s*=\s*".*?"')
|
|
||||||
comment = re.compile('<!--.*?-->', re.S)
|
|
||||||
|
|
||||||
'''This class has 2 objectives:
|
|
||||||
|
|
||||||
1. The main objective is to format XHTML p_s to be storable in the ZODB
|
|
||||||
according to Appy rules.
|
|
||||||
a. Every <p> or <li> must be on a single line (ending with a carriage
|
|
||||||
return); else, appy.shared.diff will not be able to compute XHTML
|
|
||||||
diffs;
|
|
||||||
b. Optimize size: HTML comments are removed.
|
|
||||||
|
|
||||||
2. If p_keepStyles (or m_clean) is False, some style-related information
|
|
||||||
will be removed, in order to get a standardized content that can be
|
|
||||||
dumped in an elegant and systematic manner into a POD template.
|
|
||||||
'''
|
|
||||||
@classmethod
|
|
||||||
def clean(klass, s, keepStyles=False):
|
|
||||||
'''Returns the cleaned variant of p_s.'''
|
|
||||||
if not keepStyles:
|
|
||||||
# Format p_s according to objective 2.
|
|
||||||
s = klass.classAttr.sub('', s)
|
|
||||||
# Format p_s according to objective 1.
|
|
||||||
s = klass.comment.sub('', s)
|
|
||||||
return s
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
def lower(s):
|
def lower(s):
|
||||||
'''French-accents-aware variant of string.lower.'''
|
'''French-accents-aware variant of string.lower.'''
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import xml.sax, difflib, types
|
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,\
|
||||||
property_interning_dict
|
property_interning_dict
|
||||||
from xml.sax.xmlreader import InputSource
|
from xml.sax.xmlreader import InputSource
|
||||||
|
@ -887,4 +887,127 @@ class XmlComparator:
|
||||||
else:
|
else:
|
||||||
lastLinePrinted = False
|
lastLinePrinted = False
|
||||||
return not atLeastOneDiff
|
return not atLeastOneDiff
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
class XhtmlCleaner(XmlParser):
|
||||||
|
|
||||||
|
# Tags that will not be in the result, content included, if keepStyles is
|
||||||
|
# False.
|
||||||
|
tagsToIgnoreWithContent = ('style', 'colgroup')
|
||||||
|
# Tags that will be removed from the result, but whose content will be kept,
|
||||||
|
# if keepStyles is False.
|
||||||
|
tagsToIgnoreKeepContent= ('x', 'font')
|
||||||
|
# All tags to ignore
|
||||||
|
tagsToIgnore = tagsToIgnoreWithContent + tagsToIgnoreKeepContent
|
||||||
|
# Attributes to ignore, if keepStyles if False.
|
||||||
|
attrsToIgnore = ('align', 'valign', 'cellpadding', 'cellspacing', 'width',
|
||||||
|
'height', 'bgcolor', 'lang', 'border', 'class')
|
||||||
|
# Attrs to add, if not present, to ensure good formatting, be it at the web
|
||||||
|
# or ODT levels.
|
||||||
|
attrsToAdd = {'table': {'cellspacing':'0', 'cellpadding':'6', 'border':'1'},
|
||||||
|
'tr': {'valign': 'top'}}
|
||||||
|
|
||||||
|
# Tags that required a line break to be inserted after them.
|
||||||
|
lineBreakTags = ('p', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td')
|
||||||
|
'''This class has 2 objectives:
|
||||||
|
|
||||||
|
1. The main objective is to format XHTML p_s to be storable in the ZODB
|
||||||
|
according to Appy rules.
|
||||||
|
a. Every <p> or <li> must be on a single line (ending with a carriage
|
||||||
|
return); else, appy.shared.diff will not be able to compute XHTML
|
||||||
|
diffs;
|
||||||
|
b. Optimize size: HTML comments are removed.
|
||||||
|
|
||||||
|
2. If p_keepStyles (or m_clean) is False, some style-related information
|
||||||
|
will be removed, in order to get a standardized content that can be
|
||||||
|
dumped in an elegant and systematic manner into a POD template.
|
||||||
|
'''
|
||||||
|
def clean(self, s, keepStyles=True):
|
||||||
|
# Must we keep style-related information or not?
|
||||||
|
self.env.keepStyles = keepStyles
|
||||||
|
self.env.currentContent = ''
|
||||||
|
# The stack of currently parsed elements (will contain only ignored
|
||||||
|
# ones).
|
||||||
|
self.env.currentElems = []
|
||||||
|
# 'ignoreTag' is True if we must ignore the currently walked tag.
|
||||||
|
self.env.ignoreTag = False
|
||||||
|
# 'ignoreContent' is True if, within the currently ignored tag, we must
|
||||||
|
# also ignore its content.
|
||||||
|
self.env.ignoreContent = False
|
||||||
|
return self.parse('<x>%s</x>' % s)
|
||||||
|
|
||||||
|
def startDocument(self):
|
||||||
|
# The result will be cleaned XHTML, joined from self.res.
|
||||||
|
self.res = []
|
||||||
|
|
||||||
|
def endDocument(self):
|
||||||
|
self.res = ''.join(self.res)
|
||||||
|
|
||||||
|
def startElement(self, elem, attrs):
|
||||||
|
e = self.env
|
||||||
|
# Dump any previously gathered content if any
|
||||||
|
if e.currentContent:
|
||||||
|
self.res.append(e.currentContent)
|
||||||
|
e.currentContent = ''
|
||||||
|
if e.ignoreTag and e.ignoreContent: return
|
||||||
|
if not e.keepStyles and (elem in self.tagsToIgnore):
|
||||||
|
e.ignoreTag = True
|
||||||
|
if elem in self.tagsToIgnoreWithContent:
|
||||||
|
e.ignoreContent = True
|
||||||
|
else:
|
||||||
|
e.ignoreContent = False
|
||||||
|
e.currentElems.append( (elem, e.ignoreContent) )
|
||||||
|
return
|
||||||
|
# Add a line break before the start tag if required (ie: xhtml differ
|
||||||
|
# needs to get paragraphs and other elements on separate lines).
|
||||||
|
if (elem in self.lineBreakTags) and self.res and \
|
||||||
|
(self.res[-1][-1] != '\n'):
|
||||||
|
prefix = '\n'
|
||||||
|
else:
|
||||||
|
prefix = ''
|
||||||
|
res = '%s<%s' % (prefix, elem)
|
||||||
|
# Include the found attributes, excepted those that must be ignored.
|
||||||
|
for name, value in attrs.items():
|
||||||
|
if not e.keepStyles and (name in self.attrsToIgnore): continue
|
||||||
|
res += ' %s="%s"' % (name, value)
|
||||||
|
# Include additional attributes if required.
|
||||||
|
if elem in self.attrsToAdd:
|
||||||
|
for name, value in self.attrsToAdd[elem].iteritems():
|
||||||
|
res += ' %s="%s"' % (name, value)
|
||||||
|
self.res.append('%s>' % res)
|
||||||
|
|
||||||
|
def endElement(self, elem):
|
||||||
|
e = self.env
|
||||||
|
if e.ignoreTag and (elem in self.tagsToIgnore):
|
||||||
|
# Pop the currently ignored tag
|
||||||
|
e.currentElems.pop()
|
||||||
|
if e.currentElems:
|
||||||
|
# Keep ignoring tags.
|
||||||
|
e.ignoreContent = e.currentElems[-1][1]
|
||||||
|
else:
|
||||||
|
# Stop ignoring elems
|
||||||
|
e.ignoreTag = e.ignoreContent = False
|
||||||
|
elif e.ignoreTag and e.ignoreContent:
|
||||||
|
# This is the end of a sub-tag within a region that we must ignore.
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.res.append(self.env.currentContent)
|
||||||
|
# Add a line break after the end tag if required (ie: xhtml differ
|
||||||
|
# needs to get paragraphs and other elements on separate lines).
|
||||||
|
if elem in self.lineBreakTags:
|
||||||
|
suffix = '\n'
|
||||||
|
else:
|
||||||
|
suffix = ''
|
||||||
|
self.res.append('</%s>%s' % (elem, suffix))
|
||||||
|
self.env.currentContent = ''
|
||||||
|
|
||||||
|
def characters(self, content):
|
||||||
|
if self.env.ignoreContent: return
|
||||||
|
# Remove blanks that ckeditor may add just after a start tag
|
||||||
|
if not self.env.currentContent or (self.env.currentContent == ' '):
|
||||||
|
toAdd = ' ' + content.lstrip()
|
||||||
|
else:
|
||||||
|
toAdd = content
|
||||||
|
# Re-transform XML special chars to entities.
|
||||||
|
self.env.currentContent += cgi.escape(content)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue