[gen] Bugfix in generation of back reference for predefined Refs from model.py; bugfix while editing XHTML fields from class model.py::Page; bugfixes in the XhtmlCleaner.

This commit is contained in:
Gaetan Delannay 2013-01-11 17:16:36 +01:00
parent b76af3e0c2
commit 27197f5b9d
4 changed files with 54 additions and 34 deletions

View file

@ -1245,7 +1245,7 @@ class String(Type):
# (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.
try: try:
value = XhtmlCleaner().clean(value, keepStyles=self.richText) value = XhtmlCleaner(keepStyles=self.richText).clean(value)
except XhtmlCleaner.Error, e: except XhtmlCleaner.Error, e:
# Errors while parsing p_value can't prevent the user from # Errors while parsing p_value can't prevent the user from
# storing it. # storing it.

View file

@ -40,7 +40,9 @@ class ModelClass:
those classes are part of the Appy machinery and are prefixed with _appy_ those classes are part of the Appy machinery and are prefixed with _appy_
in order to avoid name conflicts with user-defined parts of the in order to avoid name conflicts with user-defined parts of the
application model.''' application model.'''
_appy_attributes = [] # We need to keep track of attributes order. # In any ModelClass subclass we need to declare attributes in the following
# list (including back attributes), to keep track of attributes order.
_appy_attributes = []
folder = False folder = False
@classmethod @classmethod
def _appy_getTypeBody(klass, appyType, wrapperName): def _appy_getTypeBody(klass, appyType, wrapperName):
@ -128,18 +130,18 @@ class ModelClass:
pageShow = '%s.%s' % (wrapperName, pageShow.__name__) pageShow = '%s.%s' % (wrapperName, pageShow.__name__)
res += '"%s":Pge("%s", show=%s),'% (page.name, page.name, pageShow) res += '"%s":Pge("%s", show=%s),'% (page.name, page.name, pageShow)
res += '}\n' res += '}\n'
# Secondly, dump every attribute # Secondly, dump every (not Ref.isBack) attribute
for name in klass._appy_attributes: for name in klass._appy_attributes:
exec 'appyType = klass.%s' % name exec 'appyType = klass.%s' % name
if (appyType.type == 'Ref') and appyType.isBack: continue
typeBody = klass._appy_getTypeBody(appyType, wrapperName) typeBody = klass._appy_getTypeBody(appyType, wrapperName)
res += ' %s=%s\n' % (name, typeBody) res += ' %s=%s\n' % (name, typeBody)
return res return res
# The User class --------------------------------------------------------------- # The User class ---------------------------------------------------------------
class User(ModelClass): class User(ModelClass):
# In a ModelClass we need to declare attributes in the following list.
_appy_attributes = ['title', 'name', 'firstName', 'login', 'password1', _appy_attributes = ['title', 'name', 'firstName', 'login', 'password1',
'password2', 'email', 'roles'] 'password2', 'email', 'roles', 'groups', 'toTool']
# All methods defined below are fake. Real versions are in the wrapper. # All methods defined below are fake. Real versions are in the wrapper.
title = gen.String(show=False, indexed=True) title = gen.String(show=False, indexed=True)
gm = {'group': 'main', 'width': 25} gm = {'group': 'main', 'width': 25}
@ -165,8 +167,7 @@ class User(ModelClass):
# The Group class -------------------------------------------------------------- # The Group class --------------------------------------------------------------
class Group(ModelClass): class Group(ModelClass):
# In a ModelClass we need to declare attributes in the following list. _appy_attributes = ['title', 'login', 'roles', 'users', 'toTool2']
_appy_attributes = ['title', 'login', 'roles', 'users']
# All methods defined below are fake. Real versions are in the wrapper. # All methods defined below are fake. Real versions are in the wrapper.
m = {'group': 'main', 'width': 25, 'indexed': True} m = {'group': 'main', 'width': 25, 'indexed': True}
title = gen.String(multiplicity=(1,1), **m) title = gen.String(multiplicity=(1,1), **m)
@ -183,7 +184,7 @@ class Group(ModelClass):
# The Translation class -------------------------------------------------------- # The Translation class --------------------------------------------------------
class Translation(ModelClass): class Translation(ModelClass):
_appy_attributes = ['po', 'title', 'sourceLanguage'] _appy_attributes = ['po', 'title', 'sourceLanguage', 'trToTool']
# All methods defined below are fake. Real versions are in the wrapper. # All methods defined below are fake. Real versions are in the wrapper.
actionsPage = gen.Page('actions') actionsPage = gen.Page('actions')
def getPoFile(self): pass def getPoFile(self): pass
@ -195,9 +196,9 @@ class Translation(ModelClass):
# The Page class --------------------------------------------------------------- # The Page class ---------------------------------------------------------------
class Page(ModelClass): class Page(ModelClass):
_appy_attributes = ['title', 'content', 'pages'] _appy_attributes = ['title', 'content', 'pages', 'parent', 'toTool3']
folder = True folder = True
title = gen.String(show='edit', indexed=True) title = gen.String(show='edit', multiplicity=(1,1), indexed=True)
content = gen.String(format=gen.String.XHTML, layouts='f', richText=True) content = gen.String(format=gen.String.XHTML, layouts='f', richText=True)
# Pages can contain other pages. # Pages can contain other pages.
def showSubPages(self): pass def showSubPages(self): pass

View file

@ -50,7 +50,7 @@
layout The layout object that will dictate how object content layout The layout object that will dictate how object content
will be rendered. will be rendered.
</tal:comment> </tal:comment>
<metal:show define-macro="show" tal:define="tagId python: 'content'"> <metal:show define-macro="show" tal:define="tagId python: 'pageLayout'">
<metal:layout use-macro="context/ui/widgets/show/macros/layout"/> <metal:layout use-macro="context/ui/widgets/show/macros/layout"/>
</metal:show> </metal:show>

View file

@ -961,19 +961,21 @@ class XhtmlCleaner(XmlParser):
Appy-compliant format.''' Appy-compliant format.'''
class Error(Exception): pass class Error(Exception): pass
# Tags that will not be in the result, content included, if keepStyles is # Tags that will never be in the result, content included.
# False.
tagsToIgnoreWithContent = ('style', 'colgroup', 'head') tagsToIgnoreWithContent = ('style', 'colgroup', 'head')
# Tags that will be removed from the result, but whose content will be kept, # Tags that will be removed from the result, but whose content will be kept,
# if keepStyles is False. tagsToIgnoreKeepContent = ('x', 'html', 'body')
tagsToIgnoreKeepContent= ('x', 'font', 'center', 'html', 'body') allTagsToIgnore = tagsToIgnoreWithContent + tagsToIgnoreKeepContent
# All tags to ignore
tagsToIgnore = tagsToIgnoreWithContent + tagsToIgnoreKeepContent # Additional tags that will be removed, but content kept, if keepStyles is
# False.
tagsToIgnoreKeepContentDropStyles = ('font', 'center')
# Attributes to ignore, if keepStyles if False. # Attributes to ignore, if keepStyles if False.
attrsToIgnore = ('align', 'valign', 'cellpadding', 'cellspacing', 'width', attrsToIgnore = ('align', 'valign', 'cellpadding', 'cellspacing', 'width',
'height', 'bgcolor', 'lang', 'border', 'class', 'rules') 'height', 'bgcolor', 'lang', 'border', 'class', 'rules')
# CSS attributes to keep, if keepStyles if False. These attributes can be # CSS attributes to keep even if keepStyles if False. These attributes can
# used by appy.pod (to align a paragraph, center/resize an image...). # be used by pod (to align a paragraph, center/resize an image...).
cssAttrsToKeep = ('width', 'height', 'float', 'text-align', cssAttrsToKeep = ('width', 'height', 'float', 'text-align',
'font-style', 'font-weight') 'font-style', 'font-weight')
# Attrs to add, if not present, to ensure good formatting, be it at the web # Attrs to add, if not present, to ensure good formatting, be it at the web
@ -981,10 +983,21 @@ class XhtmlCleaner(XmlParser):
attrsToAdd = {'table': {'cellspacing':'0', 'cellpadding':'6', 'border':'1'}, attrsToAdd = {'table': {'cellspacing':'0', 'cellpadding':'6', 'border':'1'},
'tr': {'valign': 'top'}} 'tr': {'valign': 'top'}}
# Tags that required a line break to be inserted after them. # Tags that require a line break to be inserted after them.
lineBreakTags = ('p', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td') lineBreakTags = ('p', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'td')
def clean(self, s, keepStyles=True): # No-end tags
noEndTags = ('br', 'img')
def __init__(self, keepStyles=True):
XmlParser.__init__(self)
self.keepStyles = keepStyles
# Compute tags to ignore, which may vary according to p_keepStyles.
self.tagsToIgnore = self.allTagsToIgnore
if not keepStyles:
self.tagsToIgnore += self.tagsToIgnoreKeepContentDropStyles
def clean(self, s):
'''Cleaning XHTML code is done for 2 reasons: '''Cleaning XHTML code is done for 2 reasons:
1. The main objective is to format XHTML p_s to be storable in the 1. The main objective is to format XHTML p_s to be storable in the
@ -999,8 +1012,6 @@ class XhtmlCleaner(XmlParser):
content that can be dumped in an elegant and systematic manner content that can be dumped in an elegant and systematic manner
into a POD template. into a POD template.
''' '''
# Must we keep style-related information or not?
self.env.keepStyles = keepStyles
self.env.currentContent = '' self.env.currentContent = ''
# The stack of currently parsed elements (will contain only ignored # The stack of currently parsed elements (will contain only ignored
# ones). # ones).
@ -1040,7 +1051,7 @@ class XhtmlCleaner(XmlParser):
self.res.append(e.currentContent) self.res.append(e.currentContent)
e.currentContent = '' e.currentContent = ''
if e.ignoreTag and e.ignoreContent: return if e.ignoreTag and e.ignoreContent: return
if not e.keepStyles and (elem in self.tagsToIgnore): if elem in self.tagsToIgnore:
e.ignoreTag = True e.ignoreTag = True
if elem in self.tagsToIgnoreWithContent: if elem in self.tagsToIgnoreWithContent:
e.ignoreContent = True e.ignoreContent = True
@ -1058,7 +1069,7 @@ class XhtmlCleaner(XmlParser):
res = '%s<%s' % (prefix, elem) res = '%s<%s' % (prefix, elem)
# Include the found attributes, excepted those that must be ignored. # Include the found attributes, excepted those that must be ignored.
for name, value in attrs.items(): for name, value in attrs.items():
if not e.keepStyles: if not self.keepStyles:
if name in self.attrsToIgnore: continue if name in self.attrsToIgnore: continue
elif name == 'style': elif name == 'style':
value = self.cleanStyleAttribute(value) value = self.cleanStyleAttribute(value)
@ -1068,7 +1079,12 @@ class XhtmlCleaner(XmlParser):
if elem in self.attrsToAdd: if elem in self.attrsToAdd:
for name, value in self.attrsToAdd[elem].iteritems(): for name, value in self.attrsToAdd[elem].iteritems():
res += ' %s="%s"' % (name, value) res += ' %s="%s"' % (name, value)
self.res.append('%s>' % res) # Close the tag if it is a no-end tag
if elem in self.noEndTags:
suffix = '/>'
else:
suffix = '>'
self.res.append('%s%s' % (res, suffix))
def endElement(self, elem): def endElement(self, elem):
e = self.env e = self.env
@ -1087,14 +1103,17 @@ class XhtmlCleaner(XmlParser):
else: else:
if self.env.currentContent: if self.env.currentContent:
self.res.append(self.env.currentContent) self.res.append(self.env.currentContent)
# Add a line break after the end tag if required (ie: xhtml differ # Close the tag only if it is a no-end tag.
# needs to get paragraphs and other elements on separate lines). if elem not in self.noEndTags:
if (elem in self.lineBreakTags) and self.res and \ # Add a line break after the end tag if required (ie: xhtml
(self.res[-1][-1] != '\n'): # differ needs to get paragraphs and other elements on separate
suffix = '\n' # lines).
else: if (elem in self.lineBreakTags) and self.res and \
suffix = '' (self.res[-1][-1] != '\n'):
self.res.append('</%s>%s' % (elem, suffix)) suffix = '\n'
else:
suffix = ''
self.res.append('</%s>%s' % (elem, suffix))
self.env.currentContent = '' self.env.currentContent = ''
def characters(self, content): def characters(self, content):