Improvement in xhtml->odt conversion (pod) + new search param for appy root classes and param 'indexed' for fields.

This commit is contained in:
Gaetan Delannay 2009-10-30 21:31:39 +01:00
parent cbd6fbbec5
commit 2b907fee32
11 changed files with 1742 additions and 1643 deletions

View file

@ -28,11 +28,20 @@ class Import:
self.columnHeaders = columnHeaders
self.sortMethod = sortMethod
class Search:
'''Used for specifying a search for a given type.'''
def __init__(self, name, sortBy='title', limit=None, **fields):
self.name = name
self.sortBy = sortBy
self.limit = limit
self.fields = fields # This is a dict whose keys are indexed field
# names and whosse values are search values.
# ------------------------------------------------------------------------------
class Type:
'''Basic abstract class for defining any appy type.'''
def __init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, move, searchable,
editDefault, show, page, group, move, indexed, searchable,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus):
# The validator restricts which values may be defined. It can be an
@ -48,7 +57,8 @@ class Type:
# strings (thus creating a dictionary of values instead of a list),
# specify a type specification for the index, like Integer() or
# String(). Note that this concept of "index" has nothing to do with
# the concept of "database index".
# the concept of "database index" (see fields "indexed" and
# "searchable" below). self.index is not yet used.
self.index = index
# Default value
self.default = default
@ -68,8 +78,11 @@ class Type:
# The following attribute allows to move a field back to a previous
# position (useful for content types that inherit from others).
self.move = move
# If specified "searchable", the field will be referenced in low-level
# indexing mechanisms for fast access and search functionalities.
# If indexed is True, a database index will be set on the field for
# fast access.
self.indexed = indexed
# If specified "searchable", the field will be added to some global
# index allowing to perform application-wide, keyword searches.
self.searchable = searchable
# Normally, permissions to read or write every attribute in a type are
# granted if the user has the global permission to read or
@ -108,12 +121,12 @@ class Type:
class Integer(Type):
def __init__(self, validator=None, multiplicity=(0,1), index=None,
default=None, optional=False, editDefault=False, show=True,
page='main', group=None, move=0, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, master=None, masterValue=None,
focus=False):
page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
master=None, masterValue=None, focus=False):
Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, move, False,
editDefault, show, page, group, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.pythonType = long
@ -121,12 +134,12 @@ class Integer(Type):
class Float(Type):
def __init__(self, validator=None, multiplicity=(0,1), index=None,
default=None, optional=False, editDefault=False, show=True,
page='main', group=None, move=0, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, master=None, masterValue=None,
focus=False):
page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
master=None, masterValue=None, focus=False):
Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, move, False,
editDefault, show, page, group, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.pythonType = float
@ -178,12 +191,12 @@ class String(Type):
XHTML = 2
def __init__(self, validator=None, multiplicity=(0,1), index=None,
default=None, optional=False, editDefault=False, format=LINE,
show=True, page='main', group=None, move=0, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, master=None, masterValue=None,
focus=False):
show=True, page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
master=None, masterValue=None, focus=False):
Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, move, searchable,
editDefault, show, page, group, move, indexed, searchable,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.format = format
@ -204,12 +217,12 @@ class String(Type):
class Boolean(Type):
def __init__(self, validator=None, multiplicity=(0,1), index=None,
default=None, optional=False, editDefault=False, show=True,
page='main', group=None, move=0, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, master=None, masterValue=None,
focus=False):
page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
master=None, masterValue=None, focus=False):
Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, move, searchable,
editDefault, show, page, group, move, indexed, searchable,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.pythonType = bool
@ -221,13 +234,13 @@ class Date(Type):
def __init__(self, validator=None, multiplicity=(0,1), index=None,
default=None, optional=False, editDefault=False,
format=WITH_HOUR, startYear=time.localtime()[0]-10,
endYear=time.localtime()[0]+10,
show=True, page='main', group=None, move=0, searchable=False,
endYear=time.localtime()[0]+10, show=True, page='main',
group=None, move=0, indexed=False, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, master=None, masterValue=None,
focus=False):
Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, move, searchable,
editDefault, show, page, group, move, indexed, searchable,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.format = format
@ -237,12 +250,12 @@ class Date(Type):
class File(Type):
def __init__(self, validator=None, multiplicity=(0,1), index=None,
default=None, optional=False, editDefault=False, show=True,
page='main', group=None, move=0, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, master=None, masterValue=None,
focus=False, isImage=False):
page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
master=None, masterValue=None, focus=False, isImage=False):
Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, move, False,
editDefault, show, page, group, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.isImage = isImage
@ -253,12 +266,12 @@ class Ref(Type):
editDefault=False, add=False, link=True, unlink=False,
back=None, isBack=False, show=True, page='main', group=None,
showHeaders=False, shownInfo=(), wide=False, select=None,
maxPerPage=30, move=0, searchable=False,
maxPerPage=30, move=0, indexed=False, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, master=None, masterValue=None,
focus=False):
Type.__init__(self, validator, multiplicity, index, default, optional,
editDefault, show, page, group, move, False,
editDefault, show, page, group, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.klass = klass
@ -283,12 +296,13 @@ class Ref(Type):
class Computed(Type):
def __init__(self, validator=None, multiplicity=(0,1), index=None,
default=None, optional=False, editDefault=False, show=True,
page='main', group=None, move=0, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, method=None, plainText=True,
master=None, masterValue=None, focus=False):
page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
method=None, plainText=True, master=None, masterValue=None,
focus=False):
Type.__init__(self, None, multiplicity, index, default, optional,
False, show, page, group, move, False,
False, show, page, group, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.method = method # The method used for computing the field value
@ -302,12 +316,13 @@ class Action(Type):
tool class. An action is rendered as a button.'''
def __init__(self, validator=None, multiplicity=(1,1), index=None,
default=None, optional=False, editDefault=False, show=True,
page='main', group=None, move=0, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, action=None, result='computation',
master=None, masterValue=None, focus=False):
page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
action=None, result='computation', master=None,
masterValue=None, focus=False):
Type.__init__(self, None, (0,1), index, default, optional,
False, show, page, group, move, False,
False, show, page, group, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)
self.action = action # Can be a single method or a list/tuple of methods
@ -351,12 +366,12 @@ class Info(Type):
(text, html...) to the user.'''
def __init__(self, validator=None, multiplicity=(1,1), index=None,
default=None, optional=False, editDefault=False, show=True,
page='main', group=None, move=0, searchable=False,
specificReadPermission=False, specificWritePermission=False,
width=None, height=None, master=None, masterValue=None,
focus=False):
page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
master=None, masterValue=None, focus=False):
Type.__init__(self, None, (0,1), index, default, optional,
False, show, page, group, move, False,
False, show, page, group, move, indexed, False,
specificReadPermission, specificWritePermission, width,
height, master, masterValue, focus)

View file

@ -11,7 +11,7 @@ from utils import stringify
import appy.gen
import appy.gen.descriptors
from appy.gen.po import PoMessage
from appy.gen import Date, String, State, Transition, Type
from appy.gen import Date, String, State, Transition, Type, Search
from appy.gen.utils import GroupDescr, PageDescr, produceNiceMessage
TABS = 4 # Number of blanks in a Python indentation.
@ -433,10 +433,10 @@ class ArchetypesClassDescriptor(ClassDescriptor):
self.name = self.getClassName(klass)
self.generateSchema()
@staticmethod
def getClassName(klass):
'''Generates the name of the corresponding Archetypes class.'''
return klass.__module__.replace('.', '_') + '_' + klass.__name__
getClassName = staticmethod(getClassName)
def isAbstract(self):
'''Is self.klass abstract?'''
@ -475,6 +475,21 @@ class ArchetypesClassDescriptor(ClassDescriptor):
res = self.isFolder(theClass.__bases__[0])
return res
@staticmethod
def getSearches(klass):
'''Returns the list of searches that are defined on this class.'''
res = []
if klass.__dict__.has_key('search'):
searches = klass.__dict__['search']
if isinstance(searches, basestring): res.append(Search(searches))
elif isinstance(searches, Search): res.append(searches)
else:
# It must be a list of searches.
for search in searches:
if isinstance(search, basestring):res.append(Search(search))
else: res.append(search)
return res
def addGenerateDocMethod(self):
m = self.methods
spaces = TABS

View file

@ -675,6 +675,16 @@ class Generator(AbstractGenerator):
poMsgDescr = PoMessage('%s_%d_edit_descr' % (classDescr.name, i),
'', ' ')
self.labels.append(poMsgDescr)
# Remember i18n labels for searches
for search in classDescr.getSearches(classDescr.klass):
searchLabelId = '%s_search_%s' % (classDescr.name, search.name)
searchDescrId = '%s_descr' % searchLabelId
for label in (searchLabelId, searchDescrId):
default = ' '
if label == searchLabelId: default = search.name
poMsg = PoMessage(label, '', default)
poMsg.produceNiceDefault()
self.labels.append(poMsg)
# Generate the resulting Archetypes class and schema.
self.copyFile('ArchetypesTemplate.py', repls, destName=fileName)

View file

@ -319,11 +319,10 @@ class AbstractMixin:
if isinstance(fieldDescr, FieldDescr):
fieldDescr = fieldDescr.__dict__
appyType = fieldDescr['appyType']
if isEdit and (appyType['type']=='Ref') and appyType['add']:
return False
if isEdit and (appyType['type']=='Ref') and appyType['add']:return False
if isEdit and appyType['type']=='Action': return False
if (fieldDescr['widgetType'] == 'backField') and \
not self.getBRefs(fieldDescr['fieldRel']):
return False
not self.getBRefs(fieldDescr['fieldRel']): return False
# Do not show field if it is optional and not selected in flavour
if appyType['optional']:
tool = self.getTool()

View file

@ -26,6 +26,7 @@
function toggleViewableElements() {
var rows = cssQuery('#importedElem');
var newDisplay = 'table-row';
if (isIe) newDisplay = 'block';
if (importedElemsShown) newDisplay = 'none';
for (var i=0; i<rows.length; i++) {
rows[i].style.display = newDisplay;

View file

@ -103,7 +103,8 @@
</div>
<div metal:define-macro="showActionField">
<form name="executeAppyAction" action="skyn/do" method="POST">
<form name="executeAppyAction"
tal:attributes="action python: contextObj.absolute_url()+'/skyn/do'">
<input type="hidden" name="action" value="ExecuteAppyAction"/>
<input type="hidden" name="objectUid" tal:attributes="value contextObj/UID"/>
<input type="hidden" name="fieldName" tal:attributes="value field/getName"/>
@ -175,7 +176,7 @@
<tal:computedField condition="python: (not isEdit) and (appyType['type'] == 'Computed')">
<metal:cf use-macro="here/skyn/macros/macros/showComputedField" />
</tal:computedField>
<tal:actionField condition="python: (not isEdit) and (appyType['type'] == 'Action')">
<tal:actionField condition="python: appyType['type'] == 'Action'">
<metal:af use-macro="here/skyn/macros/macros/showActionField" />
</tal:actionField>
<tal:masterString condition="python: isEdit and (appyType['type'] in ('String', 'Boolean')) and (appyType['slaves'])">
@ -311,6 +312,7 @@
<tal:comment replace="nothing">"Static" javascripts</tal:comment>
<script language="javascript">
<!--
var isIe = (navigator.appName == "Microsoft Internet Explorer");
// AJAX machinery
var xhrObjects = new Array(); // An array of XMLHttpRequest objects
function XhrObject() { // Wraps a XmlHttpRequest object

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
xhtmlInput = '''
<p>Champ FCK</p>
<ol>
<li>aaaa
<ol>
<li>Azerty</li>
<li>aaaa</li>
</ol>
</li>
<li>edzfrgh</li>
<li>Kupu</li>
</ol>
<table cellspacing="1" cellpadding="1" border="1" style="width: 210px; height: 66px;">
<tbody>
<tr>
<td>a</td>
<td>b</td>
</tr>
<tr>
<td>x</td>
<td>vvv</td>
</tr>
<tr>
<td>bbb</td>
<td>vvvvv</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p style="margin-left: 40px;">hghdghghgh</p>
<ul>
<li>aaa</li>
<li>&nbsp;</li>
<li>bvbb</li>
</ul>
<ol>
<li>regrg</li>
<li>&nbsp;</li>
</ol>
<p>vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù&nbsp; vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù</p>
<p>&nbsp;</p>
<p>vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù&nbsp; vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@èù vghriqghrghgfd&nbsp; hgkll hgjkf lghjfkd slhgjfd klhgjfds klghjfds s&amp;é@</p>
'''

Binary file not shown.

Binary file not shown.

View file

@ -33,8 +33,8 @@ DEFAULT_ODT_STYLES = {'b': 'podBold', 'strong':'podBold', 'i': 'podItalic',
INNER_TAGS = ('b', 'strong', 'i', 'em', 'sup', 'sub', 'span', 'div')
TABLE_CELL_TAGS = ('td', 'th')
OUTER_TAGS = TABLE_CELL_TAGS + ('li',)
NOT_INSIDE_P = ('table', 'ol', 'ul') # Those elements can't be rendered inside
# paragraphs.
NOT_INSIDE_P = XHTML_HEADINGS + XHTML_LISTS + ('table',) # Those elements
# can't be rendered inside paragraphs.
NOT_INSIDE_LIST = ('table',)
IGNORABLE_TAGS = ('meta', 'title', 'style')
HTML_ENTITIES = {
@ -247,7 +247,7 @@ class XhtmlEnvironment(XmlEnvironment):
mustEndParagraph = True
if self.creatingRootParagraph:
mustStartParagraph = False
if currentElem and not (currentElem in XHTML_PARAGRAPH_TAGS):
if currentElem and (currentElem not in NOT_INSIDE_P+('p',)):
mustEndParagraph = False
if mustStartParagraph and mustEndParagraph and \
not self.currentContent.strip():