[gen] Added param Search.default allowing to define a default Search. The default search, if present, will be triggered when clicking on the main link for a class, instead of the query that collects all instances of this class; appy.gen.Type: removed 3 obsolete params: 'index', 'editDefault' and 'optional'. For achieving the same result than using 'editDefault', one may define 'by hand' an attribute on the Tool for storing the editable default value, and define, on the appropriate field in param 'default', a method that returns the value of the tool attribute; Added Type.defaultForSearch, allowing, for some sub-types, to define a default value when displaying the corresponding widget on the search screen; added a default 'state' field allowing to include workflow state among search criteria in the search screens; removed obsolete test applications.
This commit is contained in:
parent
1505264887
commit
7240561f7f
301
gen/__init__.py
301
gen/__init__.py
|
@ -300,12 +300,15 @@ class Import:
|
||||||
class Search:
|
class Search:
|
||||||
'''Used for specifying a search for a given type.'''
|
'''Used for specifying a search for a given type.'''
|
||||||
def __init__(self, name, group=None, sortBy='', sortOrder='asc', limit=None,
|
def __init__(self, name, group=None, sortBy='', sortOrder='asc', limit=None,
|
||||||
**fields):
|
default=False, **fields):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.group = group # Searches may be visually grouped in the portlet
|
self.group = group # Searches may be visually grouped in the portlet
|
||||||
self.sortBy = sortBy
|
self.sortBy = sortBy
|
||||||
self.sortOrder = sortOrder
|
self.sortOrder = sortOrder
|
||||||
self.limit = limit
|
self.limit = limit
|
||||||
|
# If this search is the default one, it will be triggered by clicking
|
||||||
|
# on main link.
|
||||||
|
self.default = default
|
||||||
# In the dict below, keys are indexed field names and values are
|
# In the dict below, keys are indexed field names and values are
|
||||||
# search values.
|
# search values.
|
||||||
self.fields = fields
|
self.fields = fields
|
||||||
|
@ -368,46 +371,32 @@ class Type:
|
||||||
jsFiles = {}
|
jsFiles = {}
|
||||||
dLayouts = 'lrv-d-f'
|
dLayouts = 'lrv-d-f'
|
||||||
|
|
||||||
def __init__(self, validator, multiplicity, index, default, optional,
|
def __init__(self, validator, multiplicity, default, show, page, group,
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
layouts, move, indexed, searchable, specificReadPermission,
|
||||||
searchable, specificReadPermission, specificWritePermission,
|
specificWritePermission, width, height, maxChars, colspan,
|
||||||
width, height, maxChars, colspan, master, masterValue, focus,
|
master, masterValue, focus, historized, sync, mapping, label,
|
||||||
historized, sync, mapping, label):
|
defaultForSearch):
|
||||||
# The validator restricts which values may be defined. It can be an
|
# The validator restricts which values may be defined. It can be an
|
||||||
# interval (1,None), a list of string values ['choice1', 'choice2'],
|
# interval (1,None), a list of string values ['choice1', 'choice2'],
|
||||||
# a regular expression, a custom function, a Selection instance, etc.
|
# a regular expression, a custom function, a Selection instance, etc.
|
||||||
self.validator = validator
|
self.validator = validator
|
||||||
# Multiplicity is a tuple indicating the minimum and maximum
|
# Multiplicity is a 2-tuple indicating the minimum and maximum
|
||||||
# occurrences of values.
|
# occurrences of values.
|
||||||
self.multiplicity = multiplicity
|
self.multiplicity = multiplicity
|
||||||
# Type of the index on the values. If you want to represent a simple
|
|
||||||
# (ordered) list of values, specify None. If you want to
|
|
||||||
# index your values with unordered integers or with other types like
|
|
||||||
# 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" (see fields "indexed" and
|
|
||||||
# "searchable" below). self.index is not yet used.
|
|
||||||
self.index = index
|
|
||||||
# Default value
|
|
||||||
self.default = default
|
|
||||||
# Is the field optional or not ?
|
|
||||||
self.optional = optional
|
|
||||||
# Is the field required or not ? (derived from multiplicity)
|
# Is the field required or not ? (derived from multiplicity)
|
||||||
self.required = self.multiplicity[0] > 0
|
self.required = self.multiplicity[0] > 0
|
||||||
# May the user configure a default value ?
|
# Default value
|
||||||
self.editDefault = editDefault
|
self.default = default
|
||||||
# Must the field be visible or not?
|
# Must the field be visible or not?
|
||||||
self.show = show
|
self.show = show
|
||||||
# When displaying/editing the whole object, on what page and phase must
|
# When displaying/editing the whole object, on what page and phase must
|
||||||
# this field value appear?
|
# this field value appear?
|
||||||
self.page = Page.get(page)
|
self.page = Page.get(page)
|
||||||
self.pageName = self.page.name
|
self.pageName = self.page.name
|
||||||
# Within self.page, in what group of fields must this field value
|
# Within self.page, in what group of fields must this one appear?
|
||||||
# appear?
|
|
||||||
self.group = Group.get(group)
|
self.group = Group.get(group)
|
||||||
# The following attribute allows to move a field back to a previous
|
# The following attribute allows to move a field back to a previous
|
||||||
# position (useful for content types that inherit from others).
|
# position (useful for moving fields above predefined ones).
|
||||||
self.move = move
|
self.move = move
|
||||||
# If indexed is True, a database index will be set on the field for
|
# If indexed is True, a database index will be set on the field for
|
||||||
# fast access.
|
# fast access.
|
||||||
|
@ -448,8 +437,7 @@ class Type:
|
||||||
self.slaves = []
|
self.slaves = []
|
||||||
# The behaviour of this field may depend on another, "master" field
|
# The behaviour of this field may depend on another, "master" field
|
||||||
self.master = master
|
self.master = master
|
||||||
if master:
|
if master: self.master.slaves.append(self)
|
||||||
self.master.slaves.append(self)
|
|
||||||
# When master has some value(s), there is impact on this field.
|
# When master has some value(s), there is impact on this field.
|
||||||
self.masterValue = initMasterValue(masterValue)
|
self.masterValue = initMasterValue(masterValue)
|
||||||
# If a field must retain attention in a particular way, set focus=True.
|
# If a field must retain attention in a particular way, set focus=True.
|
||||||
|
@ -481,6 +469,10 @@ class Type:
|
||||||
# prefix and another name. If you want to specify a new name only, and
|
# prefix and another name. If you want to specify a new name only, and
|
||||||
# not a prefix, write (None, newName).
|
# not a prefix, write (None, newName).
|
||||||
self.label = label
|
self.label = label
|
||||||
|
# When you specify a default value "for search", on a search screen, in
|
||||||
|
# the search field corresponding to this field, this default value will
|
||||||
|
# be present.
|
||||||
|
self.defaultForSearch = defaultForSearch
|
||||||
|
|
||||||
def init(self, name, klass, appName):
|
def init(self, name, klass, appName):
|
||||||
'''When the application server starts, this secondary constructor is
|
'''When the application server starts, this secondary constructor is
|
||||||
|
@ -569,13 +561,6 @@ class Type:
|
||||||
def isShowable(self, obj, layoutType):
|
def isShowable(self, obj, layoutType):
|
||||||
'''When displaying p_obj on a given p_layoutType, must we show this
|
'''When displaying p_obj on a given p_layoutType, must we show this
|
||||||
field?'''
|
field?'''
|
||||||
# Do not show field if it is optional and not selected in tool
|
|
||||||
if self.optional:
|
|
||||||
tool = obj.getTool().appy()
|
|
||||||
fieldName = 'optionalFieldsFor%s' % obj.meta_type
|
|
||||||
fieldValue = getattr(tool, fieldName, ())
|
|
||||||
if self.name not in fieldValue:
|
|
||||||
return False
|
|
||||||
# Check if the user has the permission to view or edit the field
|
# Check if the user has the permission to view or edit the field
|
||||||
if layoutType == 'edit': perm = self.writePermission
|
if layoutType == 'edit': perm = self.writePermission
|
||||||
else: perm = self.readPermission
|
else: perm = self.readPermission
|
||||||
|
@ -759,9 +744,8 @@ class Type:
|
||||||
'''Gets, on_obj, the value conforming to self's type definition.'''
|
'''Gets, on_obj, the value conforming to self's type definition.'''
|
||||||
value = getattr(obj.aq_base, self.name, None)
|
value = getattr(obj.aq_base, self.name, None)
|
||||||
if self.isEmptyValue(value):
|
if self.isEmptyValue(value):
|
||||||
# If there is no value, get the default value if any
|
# If there is no value, get the default value if any: return
|
||||||
if not self.editDefault:
|
# self.default, of self.default() if it is a method.
|
||||||
# Return self.default, of self.default() if it is a method
|
|
||||||
if callable(self.default):
|
if callable(self.default):
|
||||||
try:
|
try:
|
||||||
return self.callMethod(obj, self.default)
|
return self.callMethod(obj, self.default)
|
||||||
|
@ -773,10 +757,6 @@ class Type:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return self.default
|
return self.default
|
||||||
# If value is editable, get the default value from the tool
|
|
||||||
portalTypeName = obj._appy_getPortalType(obj.REQUEST)
|
|
||||||
tool = obj.getTool().appy()
|
|
||||||
return getattr(tool, 'defaultValueFor%s' % self.labelId)
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value):
|
def getFormattedValue(self, obj, value):
|
||||||
|
@ -932,10 +912,6 @@ class Type:
|
||||||
res.group = copy.copy(self.group)
|
res.group = copy.copy(self.group)
|
||||||
res.page = copy.copy(self.page)
|
res.page = copy.copy(self.page)
|
||||||
if not forTool: return res
|
if not forTool: return res
|
||||||
# A field added to the tool can't have parameters that would lead to the
|
|
||||||
# creation of new fields in the tool.
|
|
||||||
res.editDefault = False
|
|
||||||
res.optional = False
|
|
||||||
res.show = True
|
res.show = True
|
||||||
# Set default layouts for all Tool fields
|
# Set default layouts for all Tool fields
|
||||||
res.layouts = res.formatLayouts(None)
|
res.layouts = res.formatLayouts(None)
|
||||||
|
@ -984,19 +960,18 @@ class Type:
|
||||||
return obj.goto(obj.absolute_url())
|
return obj.goto(obj.absolute_url())
|
||||||
|
|
||||||
class Integer(Type):
|
class Integer(Type):
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, show=True,
|
show=True, page='main', group=None, layouts=None, move=0,
|
||||||
page='main', group=None, layouts=None, move=0, indexed=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
searchable=False, specificReadPermission=False,
|
|
||||||
specificWritePermission=False, width=6, height=None,
|
specificWritePermission=False, width=6, height=None,
|
||||||
maxChars=13, colspan=1, master=None, masterValue=None,
|
maxChars=13, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None):
|
focus=False, historized=False, mapping=None, label=None,
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
defaultForSearch=('','')):
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
Type.__init__(self, validator, multiplicity, default, show, page, group,
|
||||||
searchable, specificReadPermission,
|
layouts, move, indexed, searchable,specificReadPermission,
|
||||||
specificWritePermission, width, height, maxChars, colspan,
|
specificWritePermission, width, height, maxChars, colspan,
|
||||||
master, masterValue, focus, historized, True, mapping,
|
master, masterValue, focus, historized, True, mapping,
|
||||||
label)
|
label, defaultForSearch)
|
||||||
self.pythonType = long
|
self.pythonType = long
|
||||||
|
|
||||||
def validateValue(self, obj, value):
|
def validateValue(self, obj, value):
|
||||||
|
@ -1015,14 +990,14 @@ class Integer(Type):
|
||||||
class Float(Type):
|
class Float(Type):
|
||||||
allowedDecimalSeps = (',', '.')
|
allowedDecimalSeps = (',', '.')
|
||||||
allowedThousandsSeps = (' ', '')
|
allowedThousandsSeps = (' ', '')
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, show=True,
|
show=True, page='main', group=None, layouts=None, move=0,
|
||||||
page='main', group=None, layouts=None, move=0, indexed=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
searchable=False, specificReadPermission=False,
|
|
||||||
specificWritePermission=False, width=6, height=None,
|
specificWritePermission=False, width=6, height=None,
|
||||||
maxChars=13, colspan=1, master=None, masterValue=None,
|
maxChars=13, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None,
|
focus=False, historized=False, mapping=None, label=None,
|
||||||
precision=None, sep=(',', '.'), tsep=' '):
|
defaultForSearch=('',''), precision=None, sep=(',', '.'),
|
||||||
|
tsep=' '):
|
||||||
# The precision is the number of decimal digits. This number is used
|
# The precision is the number of decimal digits. This number is used
|
||||||
# for rendering the float, but the internal float representation is not
|
# for rendering the float, but the internal float representation is not
|
||||||
# rounded.
|
# rounded.
|
||||||
|
@ -1036,13 +1011,14 @@ class Float(Type):
|
||||||
# Check that the separator(s) are among allowed decimal separators
|
# Check that the separator(s) are among allowed decimal separators
|
||||||
for sep in self.sep:
|
for sep in self.sep:
|
||||||
if sep not in Float.allowedDecimalSeps:
|
if sep not in Float.allowedDecimalSeps:
|
||||||
raise 'Char "%s" is not allowed as decimal separator.' % sep
|
raise Exception('Char "%s" is not allowed as decimal ' \
|
||||||
|
'separator.' % sep)
|
||||||
self.tsep = tsep
|
self.tsep = tsep
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
Type.__init__(self, validator, multiplicity, default, show, page, group,
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
layouts, move, indexed, False, specificReadPermission,
|
||||||
False, specificReadPermission, specificWritePermission,
|
specificWritePermission, width, height, maxChars, colspan,
|
||||||
width, height, maxChars, colspan, master, masterValue,
|
master, masterValue, focus, historized, True, mapping,
|
||||||
focus, historized, True, mapping, label)
|
label, defaultForSearch)
|
||||||
self.pythonType = float
|
self.pythonType = float
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value):
|
def getFormattedValue(self, obj, value):
|
||||||
|
@ -1167,15 +1143,15 @@ class String(Type):
|
||||||
XHTML = 2
|
XHTML = 2
|
||||||
PASSWORD = 3
|
PASSWORD = 3
|
||||||
CAPTCHA = 4
|
CAPTCHA = 4
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, format=LINE,
|
format=LINE, show=True, page='main', group=None, layouts=None,
|
||||||
show=True, page='main', group=None, layouts=None, move=0,
|
move=0, indexed=False, searchable=False,
|
||||||
indexed=False, searchable=False, specificReadPermission=False,
|
specificReadPermission=False, specificWritePermission=False,
|
||||||
specificWritePermission=False, width=None, height=None,
|
width=None, height=None, maxChars=None, colspan=1, master=None,
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
masterValue=None, focus=False, historized=False, mapping=None,
|
||||||
focus=False, historized=False, mapping=None, label=None,
|
label=None, defaultForSearch='', transform='none',
|
||||||
transform='none', styles=('p','h1','h2','h3','h4'),
|
styles=('p','h1','h2','h3','h4'), allowImageUpload=True,
|
||||||
allowImageUpload=True, richText=False):
|
richText=False):
|
||||||
# According to format, the widget will be different: input field,
|
# According to format, the widget will be different: input field,
|
||||||
# textarea, inline editor... Note that there can be only one String
|
# textarea, inline editor... Note that there can be only one String
|
||||||
# field of format CAPTCHA by page, because the captcha challenge is
|
# field of format CAPTCHA by page, because the captcha challenge is
|
||||||
|
@ -1197,13 +1173,15 @@ class String(Type):
|
||||||
# CSS property: "none" (default), "uppercase", "capitalize" or
|
# CSS property: "none" (default), "uppercase", "capitalize" or
|
||||||
# "lowercase".
|
# "lowercase".
|
||||||
self.transform = transform
|
self.transform = transform
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
Type.__init__(self, validator, multiplicity, default, show, page, group,
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
layouts, move, indexed, searchable,specificReadPermission,
|
||||||
searchable, specificReadPermission,
|
|
||||||
specificWritePermission, width, height, maxChars, colspan,
|
specificWritePermission, width, height, maxChars, colspan,
|
||||||
master, masterValue, focus, historized, True, mapping,
|
master, masterValue, focus, historized, True, mapping,
|
||||||
label)
|
label, defaultForSearch)
|
||||||
self.isSelect = self.isSelection()
|
self.isSelect = self.isSelection()
|
||||||
|
# If self.isSelect, self.defaultForSearch must be a list of value(s).
|
||||||
|
if self.isSelect and not defaultForSearch:
|
||||||
|
self.defaultForSearch = []
|
||||||
# Default width, height and maxChars vary according to String format
|
# Default width, height and maxChars vary according to String format
|
||||||
if width == None:
|
if width == None:
|
||||||
if format == String.TEXT: self.width = 60
|
if format == String.TEXT: self.width = 60
|
||||||
|
@ -1473,20 +1451,19 @@ class String(Type):
|
||||||
return self.getCaptchaChallenge({})['text']
|
return self.getCaptchaChallenge({})['text']
|
||||||
|
|
||||||
class Boolean(Type):
|
class Boolean(Type):
|
||||||
|
'''Field for storing boolean values.'''
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, show=True,
|
show=True, page='main', group=None, layouts = None, move=0,
|
||||||
page='main', group=None, layouts = None, move=0, indexed=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
searchable=False, specificReadPermission=False,
|
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None):
|
focus=False, historized=False, mapping=None, label=None,
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
defaultForSearch=False):
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
Type.__init__(self, validator, multiplicity, default, show, page, group,
|
||||||
searchable, specificReadPermission,
|
layouts, move, indexed, searchable,specificReadPermission,
|
||||||
specificWritePermission, width, height, None, colspan,
|
specificWritePermission, width, height, None, colspan,
|
||||||
master, masterValue, focus, historized, True, mapping,
|
master, masterValue, focus, historized, True, mapping,
|
||||||
label)
|
label, defaultForSearch)
|
||||||
self.pythonType = bool
|
self.pythonType = bool
|
||||||
|
|
||||||
dLayouts = {'view': 'lf', 'edit': Table('flrv;=d', width=None)}
|
dLayouts = {'view': 'lf', 'edit': Table('flrv;=d', width=None)}
|
||||||
|
@ -1521,8 +1498,7 @@ class Date(Type):
|
||||||
WITHOUT_HOUR = 1
|
WITHOUT_HOUR = 1
|
||||||
dateParts = ('year', 'month', 'day')
|
dateParts = ('year', 'month', 'day')
|
||||||
hourParts = ('hour', 'minute')
|
hourParts = ('hour', 'minute')
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
||||||
default=None, optional=False, editDefault=False,
|
|
||||||
format=WITH_HOUR, calendar=True,
|
format=WITH_HOUR, calendar=True,
|
||||||
startYear=time.localtime()[0]-10,
|
startYear=time.localtime()[0]-10,
|
||||||
endYear=time.localtime()[0]+10, reverseYears=False,
|
endYear=time.localtime()[0]+10, reverseYears=False,
|
||||||
|
@ -1530,7 +1506,8 @@ class Date(Type):
|
||||||
indexed=False, searchable=False, specificReadPermission=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None):
|
focus=False, historized=False, mapping=None, label=None,
|
||||||
|
defaultForSearch=None):
|
||||||
self.format = format
|
self.format = format
|
||||||
self.calendar = calendar
|
self.calendar = calendar
|
||||||
self.startYear = startYear
|
self.startYear = startYear
|
||||||
|
@ -1538,12 +1515,11 @@ class Date(Type):
|
||||||
# If reverseYears is True, in the selection box, available years, from
|
# If reverseYears is True, in the selection box, available years, from
|
||||||
# self.startYear to self.endYear will be listed in reverse order.
|
# self.startYear to self.endYear will be listed in reverse order.
|
||||||
self.reverseYears = reverseYears
|
self.reverseYears = reverseYears
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
Type.__init__(self, validator, multiplicity, default, show, page, group,
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
layouts, move, indexed, searchable,specificReadPermission,
|
||||||
searchable, specificReadPermission,
|
|
||||||
specificWritePermission, width, height, None, colspan,
|
specificWritePermission, width, height, None, colspan,
|
||||||
master, masterValue, focus, historized, True, mapping,
|
master, masterValue, focus, historized, True, mapping,
|
||||||
label)
|
label, defaultForSearch)
|
||||||
|
|
||||||
def getCss(self, layoutType, res):
|
def getCss(self, layoutType, res):
|
||||||
# CSS files are only required if the calendar must be shown.
|
# CSS files are only required if the calendar must be shown.
|
||||||
|
@ -1598,20 +1574,19 @@ class Date(Type):
|
||||||
return DateTime.DateTime(value)
|
return DateTime.DateTime(value)
|
||||||
|
|
||||||
class File(Type):
|
class File(Type):
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, show=True,
|
show=True, page='main', group=None, layouts=None, move=0,
|
||||||
page='main', group=None, layouts=None, move=0, indexed=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
searchable=False, specificReadPermission=False,
|
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None,
|
focus=False, historized=False, mapping=None, label=None,
|
||||||
isImage=False):
|
isImage=False, defaultForSearch=''):
|
||||||
self.isImage = isImage
|
self.isImage = isImage
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
Type.__init__(self, validator, multiplicity, default, show, page, group,
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
layouts, move, indexed, False, specificReadPermission,
|
||||||
False, specificReadPermission, specificWritePermission,
|
specificWritePermission, width, height, None, colspan,
|
||||||
width, height, None, colspan, master, masterValue, focus,
|
master, masterValue, focus, historized, True, mapping,
|
||||||
historized, True, mapping, label)
|
label, defaultForSearch)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getFileObject(filePath, fileName=None, zope=False):
|
def getFileObject(filePath, fileName=None, zope=False):
|
||||||
|
@ -1756,17 +1731,17 @@ class Ref(Type):
|
||||||
wdLayouts = {'view': Table('l-d-f', width='100%')}
|
wdLayouts = {'view': Table('l-d-f', width='100%')}
|
||||||
|
|
||||||
def __init__(self, klass=None, attribute=None, validator=None,
|
def __init__(self, klass=None, attribute=None, validator=None,
|
||||||
multiplicity=(0,1), index=None, default=None, optional=False,
|
multiplicity=(0,1), default=None, add=False, addConfirm=False,
|
||||||
editDefault=False, add=False, addConfirm=False, delete=None,
|
delete=None, noForm=False, link=True, unlink=None, back=None,
|
||||||
noForm=False, link=True, unlink=None, back=None, show=True,
|
show=True, page='main', group=None, layouts=None,
|
||||||
page='main', group=None, layouts=None, showHeaders=False,
|
showHeaders=False, shownInfo=(), select=None, maxPerPage=30,
|
||||||
shownInfo=(), select=None, maxPerPage=30, move=0,
|
move=0, indexed=False, searchable=False,
|
||||||
indexed=False, searchable=False, specificReadPermission=False,
|
specificReadPermission=False, specificWritePermission=False,
|
||||||
specificWritePermission=False, width=None, height=5,
|
width=None, height=5, maxChars=None, colspan=1, master=None,
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
masterValue=None, focus=False, historized=False, mapping=None,
|
||||||
focus=False, historized=False, mapping=None, label=None,
|
label=None, queryable=False, queryFields=None, queryNbCols=1,
|
||||||
queryable=False, queryFields=None, queryNbCols=1,
|
navigable=False, searchSelect=None, changeOrder=True,
|
||||||
navigable=False, searchSelect=None, changeOrder=True):
|
defaultForSearch=''):
|
||||||
self.klass = klass
|
self.klass = klass
|
||||||
self.attribute = attribute
|
self.attribute = attribute
|
||||||
# May the user add new objects through this ref ?
|
# May the user add new objects through this ref ?
|
||||||
|
@ -1852,11 +1827,11 @@ class Ref(Type):
|
||||||
# If changeOrder is False, it even if the user has the right to modify
|
# If changeOrder is False, it even if the user has the right to modify
|
||||||
# the field, it will not be possible to move objects or sort them.
|
# the field, it will not be possible to move objects or sort them.
|
||||||
self.changeOrder = changeOrder
|
self.changeOrder = changeOrder
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
Type.__init__(self, validator, multiplicity, default, show, page, group,
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
layouts, move, indexed, False, specificReadPermission,
|
||||||
False, specificReadPermission, specificWritePermission,
|
specificWritePermission, width, height, None, colspan,
|
||||||
width, height, None, colspan, master, masterValue, focus,
|
master, masterValue, focus, historized, sync, mapping,
|
||||||
historized, sync, mapping, label)
|
label, defaultForSearch)
|
||||||
self.validable = self.link
|
self.validable = self.link
|
||||||
|
|
||||||
def getDefaultLayouts(self):
|
def getDefaultLayouts(self):
|
||||||
|
@ -2125,14 +2100,14 @@ def autoref(klass, field):
|
||||||
setattr(klass, field.back.attribute, field.back)
|
setattr(klass, field.back.attribute, field.back)
|
||||||
|
|
||||||
class Computed(Type):
|
class Computed(Type):
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, show='view',
|
show='view', page='main', group=None, layouts=None, move=0,
|
||||||
page='main', group=None, layouts=None, move=0, indexed=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
searchable=False, specificReadPermission=False,
|
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, method=None, plainText=True,
|
maxChars=None, colspan=1, method=None, plainText=True,
|
||||||
master=None, masterValue=None, focus=False, historized=False,
|
master=None, masterValue=None, focus=False, historized=False,
|
||||||
sync=True, mapping=None, label=None, context={}):
|
sync=True, mapping=None, label=None, defaultForSearch='',
|
||||||
|
context={}):
|
||||||
# The Python method used for computing the field value
|
# The Python method used for computing the field value
|
||||||
self.method = method
|
self.method = method
|
||||||
# Does field computation produce plain text or XHTML?
|
# Does field computation produce plain text or XHTML?
|
||||||
|
@ -2145,11 +2120,11 @@ class Computed(Type):
|
||||||
# to the macro specified in self.method. If the dict contains key
|
# to the macro specified in self.method. If the dict contains key
|
||||||
# "someKey", it will be available to the macro as "options/someKey".
|
# "someKey", it will be available to the macro as "options/someKey".
|
||||||
self.context = context
|
self.context = context
|
||||||
Type.__init__(self, None, multiplicity, index, default, optional,
|
Type.__init__(self, None, multiplicity, default, show, page, group,
|
||||||
False, show, page, group, layouts, move, indexed, False,
|
layouts, move, indexed, False, specificReadPermission,
|
||||||
specificReadPermission, specificWritePermission, width,
|
specificWritePermission, width, height, None, colspan,
|
||||||
height, None, colspan, master, masterValue, focus,
|
master, masterValue, focus, historized, sync, mapping,
|
||||||
historized, sync, mapping, label)
|
label, defaultForSearch)
|
||||||
self.validable = False
|
self.validable = False
|
||||||
|
|
||||||
def callMacro(self, obj, macroPath):
|
def callMacro(self, obj, macroPath):
|
||||||
|
@ -2192,10 +2167,9 @@ class Action(Type):
|
||||||
by the user on a given gen-class. For example, the custom installation
|
by the user on a given gen-class. For example, the custom installation
|
||||||
procedure of a gen-application is implemented by an action on the custom
|
procedure of a gen-application is implemented by an action on the custom
|
||||||
tool class. An action is rendered as a button.'''
|
tool class. An action is rendered as a button.'''
|
||||||
def __init__(self, validator=None, multiplicity=(1,1), index=None,
|
def __init__(self, validator=None, multiplicity=(1,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, show=True,
|
show=True, page='main', group=None, layouts=None, move=0,
|
||||||
page='main', group=None, layouts=None, move=0, indexed=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
searchable=False, specificReadPermission=False,
|
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, action=None, result='computation',
|
maxChars=None, colspan=1, action=None, result='computation',
|
||||||
confirm=False, master=None, masterValue=None, focus=False,
|
confirm=False, master=None, masterValue=None, focus=False,
|
||||||
|
@ -2216,11 +2190,11 @@ class Action(Type):
|
||||||
# If following field "confirm" is True, a popup will ask the user if
|
# If following field "confirm" is True, a popup will ask the user if
|
||||||
# she is really sure about triggering this action.
|
# she is really sure about triggering this action.
|
||||||
self.confirm = confirm
|
self.confirm = confirm
|
||||||
Type.__init__(self, None, (0,1), index, default, optional,
|
Type.__init__(self, None, (0,1), default, show, page, group, layouts,
|
||||||
False, show, page, group, layouts, move, indexed, False,
|
move, indexed, False, specificReadPermission,
|
||||||
specificReadPermission, specificWritePermission, width,
|
specificWritePermission, width, height, None, colspan,
|
||||||
height, None, colspan, master, masterValue, focus,
|
master, masterValue, focus, historized, False, mapping,
|
||||||
historized, False, mapping, label)
|
label, None)
|
||||||
self.validable = False
|
self.validable = False
|
||||||
|
|
||||||
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
||||||
|
@ -2258,18 +2232,17 @@ class Action(Type):
|
||||||
class Info(Type):
|
class Info(Type):
|
||||||
'''An info is a field whose purpose is to present information
|
'''An info is a field whose purpose is to present information
|
||||||
(text, html...) to the user.'''
|
(text, html...) to the user.'''
|
||||||
def __init__(self, validator=None, multiplicity=(1,1), index=None,
|
def __init__(self, validator=None, multiplicity=(1,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, show='view',
|
show='view', page='main', group=None, layouts=None, move=0,
|
||||||
page='main', group=None, layouts=None, move=0, indexed=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
searchable=False, specificReadPermission=False,
|
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None):
|
focus=False, historized=False, mapping=None, label=None):
|
||||||
Type.__init__(self, None, (0,1), index, default, optional,
|
Type.__init__(self, None, (0,1), default, show, page, group, layouts,
|
||||||
False, show, page, group, layouts, move, indexed, False,
|
move, indexed, False, specificReadPermission,
|
||||||
specificReadPermission, specificWritePermission, width,
|
specificWritePermission, width, height, None, colspan,
|
||||||
height, None, colspan, master, masterValue, focus,
|
master, masterValue, focus, historized, False, mapping,
|
||||||
historized, False, mapping, label)
|
label, None)
|
||||||
self.validable = False
|
self.validable = False
|
||||||
|
|
||||||
class Pod(Type):
|
class Pod(Type):
|
||||||
|
@ -2279,8 +2252,7 @@ class Pod(Type):
|
||||||
POD_ERROR = 'An error occurred while generating the document. Please ' \
|
POD_ERROR = 'An error occurred while generating the document. Please ' \
|
||||||
'contact the system administrator.'
|
'contact the system administrator.'
|
||||||
DELETE_TEMP_DOC_ERROR = 'A temporary document could not be removed. %s.'
|
DELETE_TEMP_DOC_ERROR = 'A temporary document could not be removed. %s.'
|
||||||
def __init__(self, validator=None, index=None, default=None,
|
def __init__(self, validator=None, default=None, show=('view', 'result'),
|
||||||
optional=False, editDefault=False, show=('view', 'result'),
|
|
||||||
page='main', group=None, layouts=None, move=0, indexed=False,
|
page='main', group=None, layouts=None, move=0, indexed=False,
|
||||||
searchable=False, specificReadPermission=False,
|
searchable=False, specificReadPermission=False,
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
|
@ -2303,12 +2275,11 @@ class Pod(Type):
|
||||||
self.stylesMapping = stylesMapping
|
self.stylesMapping = stylesMapping
|
||||||
# Freeze format is by PDF by default
|
# Freeze format is by PDF by default
|
||||||
self.freezeFormat = freezeFormat
|
self.freezeFormat = freezeFormat
|
||||||
Type.__init__(self, None, (0,1), index, default, optional,
|
Type.__init__(self, None, (0,1), default, show, page, group, layouts,
|
||||||
False, show, page, group, layouts, move, indexed,
|
move, indexed, searchable, specificReadPermission,
|
||||||
searchable, specificReadPermission,
|
|
||||||
specificWritePermission, width, height, None, colspan,
|
specificWritePermission, width, height, None, colspan,
|
||||||
master, masterValue, focus, historized, False, mapping,
|
master, masterValue, focus, historized, False, mapping,
|
||||||
label)
|
label, None)
|
||||||
self.validable = False
|
self.validable = False
|
||||||
|
|
||||||
def isFrozen(self, obj):
|
def isFrozen(self, obj):
|
||||||
|
@ -2441,19 +2412,18 @@ class Pod(Type):
|
||||||
|
|
||||||
class List(Type):
|
class List(Type):
|
||||||
'''A list.'''
|
'''A list.'''
|
||||||
def __init__(self, fields, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, fields, validator=None, multiplicity=(0,1), default=None,
|
||||||
default=None, optional=False, editDefault=False, show=True,
|
show=True, page='main', group=None, layouts=None, move=0,
|
||||||
page='main', group=None, layouts=None, move=0, indexed=False,
|
indexed=False, searchable=False, specificReadPermission=False,
|
||||||
searchable=False, specificReadPermission=False,
|
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
maxChars=None, colspan=1, master=None, masterValue=None,
|
maxChars=None, colspan=1, master=None, masterValue=None,
|
||||||
focus=False, historized=False, mapping=None, label=None,
|
focus=False, historized=False, mapping=None, label=None,
|
||||||
subLayouts=Table('fv', width=None)):
|
subLayouts=Table('fv', width=None)):
|
||||||
Type.__init__(self, validator, multiplicity, index, default, optional,
|
Type.__init__(self, validator, multiplicity, default, show, page, group,
|
||||||
editDefault, show, page, group, layouts, move, indexed,
|
layouts, move, indexed, False, specificReadPermission,
|
||||||
False, specificReadPermission, specificWritePermission,
|
specificWritePermission, width, height, None, colspan,
|
||||||
width, height, None, colspan, master, masterValue, focus,
|
master, masterValue, focus, historized, True, mapping,
|
||||||
historized, True, mapping, label)
|
label, None)
|
||||||
self.validable = True
|
self.validable = True
|
||||||
# Tuples of (names, Type instances) determining the format of every
|
# Tuples of (names, Type instances) determining the format of every
|
||||||
# element in the list.
|
# element in the list.
|
||||||
|
@ -2941,8 +2911,7 @@ class Selection:
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Model: pass
|
class Model: pass
|
||||||
class Tool(Model):
|
class Tool(Model):
|
||||||
'''If you want so define a custom tool class, she must inherit from this
|
'''If you want to extend or modify the Tool class, subclass me.'''
|
||||||
class.'''
|
|
||||||
class User(Model):
|
class User(Model):
|
||||||
'''If you want to extend or modify the User class, subclass me.'''
|
'''If you want to extend or modify the User class, subclass me.'''
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,11 @@ class Calendar(Type):
|
||||||
otherCalendars=None, additionalInfo=None, startDate=None,
|
otherCalendars=None, additionalInfo=None, startDate=None,
|
||||||
endDate=None, defaultDate=None, preCompute=None,
|
endDate=None, defaultDate=None, preCompute=None,
|
||||||
applicableEvents=None):
|
applicableEvents=None):
|
||||||
Type.__init__(self, validator, (0,1), None, default, False, False,
|
Type.__init__(self, validator, (0,1), default, show, page, group,
|
||||||
show, page, group, layouts, move, False, False,
|
layouts, move, False, False, specificReadPermission,
|
||||||
specificReadPermission, specificWritePermission,
|
specificWritePermission, width, height, None, colspan,
|
||||||
width, height, None, colspan, master, masterValue, focus,
|
master, masterValue, focus, False, True, mapping, label,
|
||||||
False, True, mapping, label)
|
None)
|
||||||
# eventTypes can be a "static" list or tuple of strings that identify
|
# eventTypes can be a "static" list or tuple of strings that identify
|
||||||
# the types of events that are supported by this calendar. It can also
|
# the types of events that are supported by this calendar. It can also
|
||||||
# be a method that computes such a "dynamic" list or tuple. When
|
# be a method that computes such a "dynamic" list or tuple. When
|
||||||
|
|
|
@ -131,21 +131,14 @@ class ClassDescriptor(Descriptor):
|
||||||
parentWrapper = '%s_Wrapper' % k.name
|
parentWrapper = '%s_Wrapper' % k.name
|
||||||
return (parentWrapper, parentClass)
|
return (parentWrapper, parentClass)
|
||||||
|
|
||||||
def generateSchema(self, configClass=False):
|
def generateSchema(self):
|
||||||
'''Generates i18n and other related stuff for this class. If this class
|
'''Generates i18n and other related stuff for this class.'''
|
||||||
is in the configuration (tool, user, etc) we must avoid having
|
|
||||||
attributes that rely on the configuration (ie attributes that are
|
|
||||||
optional, with editDefault=True, etc).'''
|
|
||||||
for attrName in self.orderedAttributes:
|
for attrName in self.orderedAttributes:
|
||||||
try:
|
try:
|
||||||
attrValue = getattr(self.klass, attrName)
|
attrValue = getattr(self.klass, attrName)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
attrValue = getattr(self.modelClass, attrName)
|
attrValue = getattr(self.modelClass, attrName)
|
||||||
if isinstance(attrValue, gen.Type):
|
if not isinstance(attrValue, gen.Type): continue
|
||||||
if configClass:
|
|
||||||
attrValue = copy.copy(attrValue)
|
|
||||||
attrValue.optional = False
|
|
||||||
attrValue.editDefault = False
|
|
||||||
FieldDescriptor(attrName, attrValue, self).generate()
|
FieldDescriptor(attrName, attrValue, self).generate()
|
||||||
|
|
||||||
def isAbstract(self):
|
def isAbstract(self):
|
||||||
|
@ -392,13 +385,7 @@ class FieldDescriptor:
|
||||||
'''Walks into the Appy type definition and gathers data about the
|
'''Walks into the Appy type definition and gathers data about the
|
||||||
i18n labels.'''
|
i18n labels.'''
|
||||||
# Manage things common to all Appy types
|
# Manage things common to all Appy types
|
||||||
# - optional ?
|
# Put an index on this field?
|
||||||
if self.appyType.optional:
|
|
||||||
self.generator.tool.addOptionalField(self)
|
|
||||||
# - edit default value ?
|
|
||||||
if self.appyType.editDefault:
|
|
||||||
self.generator.tool.addDefaultField(self)
|
|
||||||
# - put an index on this field?
|
|
||||||
if self.appyType.indexed and (self.fieldName != 'title'):
|
if self.appyType.indexed and (self.fieldName != 'title'):
|
||||||
self.classDescr.addIndexMethod(self)
|
self.classDescr.addIndexMethod(self)
|
||||||
# i18n labels
|
# i18n labels
|
||||||
|
@ -477,28 +464,6 @@ class ToolClassDescriptor(ClassDescriptor):
|
||||||
|
|
||||||
def isFolder(self, klass=None): return True
|
def isFolder(self, klass=None): return True
|
||||||
def isRoot(self): return False
|
def isRoot(self): return False
|
||||||
def generateSchema(self):
|
|
||||||
ClassDescriptor.generateSchema(self, configClass=True)
|
|
||||||
|
|
||||||
def addOptionalField(self, fieldDescr):
|
|
||||||
className = fieldDescr.classDescr.name
|
|
||||||
fieldName = 'optionalFieldsFor%s' % className
|
|
||||||
fieldType = getattr(self.modelClass, fieldName, None)
|
|
||||||
if not fieldType:
|
|
||||||
fieldType = String(multiplicity=(0,None))
|
|
||||||
fieldType.validator = []
|
|
||||||
self.addField(fieldName, fieldType)
|
|
||||||
fieldType.validator.append(fieldDescr.fieldName)
|
|
||||||
fieldType.page.name = 'data'
|
|
||||||
fieldType.group = gen.Group(fieldDescr.classDescr.klass.__name__)
|
|
||||||
|
|
||||||
def addDefaultField(self, fieldDescr):
|
|
||||||
className = fieldDescr.classDescr.name
|
|
||||||
fieldName = 'defaultValueFor%s_%s' % (className, fieldDescr.fieldName)
|
|
||||||
fieldType = fieldDescr.appyType.clone()
|
|
||||||
self.addField(fieldName, fieldType)
|
|
||||||
fieldType.page.name = 'data'
|
|
||||||
fieldType.group = gen.Group(fieldDescr.classDescr.klass.__name__)
|
|
||||||
|
|
||||||
def addPodRelatedFields(self, fieldDescr):
|
def addPodRelatedFields(self, fieldDescr):
|
||||||
'''Adds the fields needed in the Tool for configuring a Pod field.'''
|
'''Adds the fields needed in the Tool for configuring a Pod field.'''
|
||||||
|
@ -607,8 +572,6 @@ class UserClassDescriptor(ClassDescriptor):
|
||||||
self.klass = klass
|
self.klass = klass
|
||||||
self.customized = True
|
self.customized = True
|
||||||
def isFolder(self, klass=None): return False
|
def isFolder(self, klass=None): return False
|
||||||
def generateSchema(self):
|
|
||||||
ClassDescriptor.generateSchema(self, configClass=True)
|
|
||||||
|
|
||||||
class GroupClassDescriptor(ClassDescriptor):
|
class GroupClassDescriptor(ClassDescriptor):
|
||||||
'''Represents the class that corresponds to the Group for the generated
|
'''Represents the class that corresponds to the Group for the generated
|
||||||
|
@ -634,8 +597,6 @@ class GroupClassDescriptor(ClassDescriptor):
|
||||||
self.klass = klass
|
self.klass = klass
|
||||||
self.customized = True
|
self.customized = True
|
||||||
def isFolder(self, klass=None): return False
|
def isFolder(self, klass=None): return False
|
||||||
def generateSchema(self):
|
|
||||||
ClassDescriptor.generateSchema(self, configClass=True)
|
|
||||||
|
|
||||||
class TranslationClassDescriptor(ClassDescriptor):
|
class TranslationClassDescriptor(ClassDescriptor):
|
||||||
'''Represents the set of translation ids for a gen-application.'''
|
'''Represents the set of translation ids for a gen-application.'''
|
||||||
|
@ -648,8 +609,6 @@ class TranslationClassDescriptor(ClassDescriptor):
|
||||||
|
|
||||||
def getParents(self, allClasses=()): return ('Translation',)
|
def getParents(self, allClasses=()): return ('Translation',)
|
||||||
def isFolder(self, klass=None): return False
|
def isFolder(self, klass=None): return False
|
||||||
def generateSchema(self):
|
|
||||||
ClassDescriptor.generateSchema(self, configClass=True)
|
|
||||||
|
|
||||||
def addLabelField(self, messageId, page):
|
def addLabelField(self, messageId, page):
|
||||||
'''Adds a Computed field that will display, in the source language, the
|
'''Adds a Computed field that will display, in the source language, the
|
||||||
|
@ -714,6 +673,4 @@ class PageClassDescriptor(ClassDescriptor):
|
||||||
self.klass = klass
|
self.klass = klass
|
||||||
self.customized = True
|
self.customized = True
|
||||||
def isFolder(self, klass=None): return True
|
def isFolder(self, klass=None): return True
|
||||||
def generateSchema(self):
|
|
||||||
ClassDescriptor.generateSchema(self, configClass=True)
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -762,6 +762,8 @@ class ZopeGenerator(Generator):
|
||||||
if classDescr.name in self.referers:
|
if classDescr.name in self.referers:
|
||||||
for field in self.referers[classDescr.name]:
|
for field in self.referers[classDescr.name]:
|
||||||
names.append(field.appyType.back.attribute)
|
names.append(field.appyType.back.attribute)
|
||||||
|
# Add the 'state' attribute
|
||||||
|
names.append('state')
|
||||||
qNames = ['"%s"' % name for name in names]
|
qNames = ['"%s"' % name for name in names]
|
||||||
attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames)))
|
attributes.append('"%s":[%s]' % (classDescr.name, ','.join(qNames)))
|
||||||
repls['attributes'] = ',\n '.join(attributes)
|
repls['attributes'] = ',\n '.join(attributes)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from appy.shared.utils import normalizeText
|
||||||
|
|
||||||
# Default Appy indexes ---------------------------------------------------------
|
# Default Appy indexes ---------------------------------------------------------
|
||||||
defaultIndexes = {
|
defaultIndexes = {
|
||||||
'State': 'FieldIndex', 'UID': 'FieldIndex', 'Title': 'TextIndex',
|
'State': 'ListIndex', 'UID': 'FieldIndex', 'Title': 'TextIndex',
|
||||||
'SortableTitle': 'FieldIndex', 'SearchableText': 'TextIndex',
|
'SortableTitle': 'FieldIndex', 'SearchableText': 'TextIndex',
|
||||||
'Creator': 'FieldIndex', 'Created': 'DateIndex', 'ClassName': 'FieldIndex',
|
'Creator': 'FieldIndex', 'Created': 'DateIndex', 'ClassName': 'FieldIndex',
|
||||||
'Allowed': 'KeywordIndex'}
|
'Allowed': 'KeywordIndex'}
|
||||||
|
|
|
@ -354,9 +354,14 @@ class ZopeInstaller:
|
||||||
wrapperClass = klass.wrapperClass
|
wrapperClass = klass.wrapperClass
|
||||||
if not hasattr(wrapperClass, 'title'):
|
if not hasattr(wrapperClass, 'title'):
|
||||||
# Special field "type" is mandatory for every class.
|
# Special field "type" is mandatory for every class.
|
||||||
title = gen.String(multiplicity=(1,1), show='edit',indexed=True)
|
title = gen.String(multiplicity=(1,1),show='edit',indexed=True)
|
||||||
title.init('title', None, 'appy')
|
title.init('title', None, 'appy')
|
||||||
setattr(wrapperClass, 'title', title)
|
setattr(wrapperClass, 'title', title)
|
||||||
|
# Special field "state" must be added for every class
|
||||||
|
state = gen.String(validator=gen.Selection('_appy_listStates'),
|
||||||
|
show='result')
|
||||||
|
state.init('state', None, 'workflow')
|
||||||
|
setattr(wrapperClass, 'state', state)
|
||||||
names = self.config.attributes[wrapperClass.__name__[:-8]]
|
names = self.config.attributes[wrapperClass.__name__[:-8]]
|
||||||
wrapperClass.__fields__ = [getattr(wrapperClass, n) for n in names]
|
wrapperClass.__fields__ = [getattr(wrapperClass, n) for n in names]
|
||||||
# Post-initialise every Appy type
|
# Post-initialise every Appy type
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, os.path, sys, re, time, random, types, base64, urllib
|
import os, os.path, sys, re, time, random, types, base64, urllib
|
||||||
from appy.shared import mimeTypes
|
from appy import Object
|
||||||
from appy.shared.utils import getOsTempFolder, sequenceTypes
|
|
||||||
from appy.shared.data import languages
|
|
||||||
import appy.gen
|
import appy.gen
|
||||||
from appy.gen import Type, Search, Selection, String
|
from appy.gen import Type, Search, Selection, String
|
||||||
from appy.gen.utils import SomeObjects, getClassName
|
from appy.gen.utils import SomeObjects, getClassName
|
||||||
|
@ -10,6 +8,9 @@ from appy.gen.mixins import BaseMixin
|
||||||
from appy.gen.wrappers import AbstractWrapper
|
from appy.gen.wrappers import AbstractWrapper
|
||||||
from appy.gen.descriptors import ClassDescriptor
|
from appy.gen.descriptors import ClassDescriptor
|
||||||
from appy.gen.mail import sendMail
|
from appy.gen.mail import sendMail
|
||||||
|
from appy.shared import mimeTypes
|
||||||
|
from appy.shared.utils import getOsTempFolder, sequenceTypes
|
||||||
|
from appy.shared.data import languages
|
||||||
try:
|
try:
|
||||||
from AccessControl.ZopeSecurityPolicy import _noroles
|
from AccessControl.ZopeSecurityPolicy import _noroles
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -681,11 +682,15 @@ class ToolMixin(BaseMixin):
|
||||||
return obj, fieldName
|
return obj, fieldName
|
||||||
|
|
||||||
def getSearches(self, contentType):
|
def getSearches(self, contentType):
|
||||||
'''Returns the list of searches that are defined for p_contentType.
|
'''Returns an object with 2 attributes:
|
||||||
Every list item is a dict that contains info about a search or about
|
* "searches" stores the searches that are defined for p_contentType;
|
||||||
a group of searches.'''
|
* "default" stores the search defined as default one.
|
||||||
|
Every item representing a search is a dict containing info about a
|
||||||
|
search or about a group of searches.
|
||||||
|
'''
|
||||||
appyClass = self.getAppyClass(contentType)
|
appyClass = self.getAppyClass(contentType)
|
||||||
res = []
|
searches = []
|
||||||
|
default = None # Also retrieve the default one here.
|
||||||
visitedGroups = {} # Names of already visited search groups
|
visitedGroups = {} # Names of already visited search groups
|
||||||
for search in ClassDescriptor.getSearches(appyClass):
|
for search in ClassDescriptor.getSearches(appyClass):
|
||||||
# Determine first group label, we will need it.
|
# Determine first group label, we will need it.
|
||||||
|
@ -699,7 +704,7 @@ class ToolMixin(BaseMixin):
|
||||||
'label': self.translate(groupLabel),
|
'label': self.translate(groupLabel),
|
||||||
'descr': self.translate('%s_descr' % groupLabel),
|
'descr': self.translate('%s_descr' % groupLabel),
|
||||||
}
|
}
|
||||||
res.append(group)
|
searches.append(group)
|
||||||
visitedGroups[search.group] = group
|
visitedGroups[search.group] = group
|
||||||
# Add the search itself
|
# Add the search itself
|
||||||
searchLabel = '%s_search_%s' % (contentType, search.name)
|
searchLabel = '%s_search_%s' % (contentType, search.name)
|
||||||
|
@ -709,8 +714,10 @@ class ToolMixin(BaseMixin):
|
||||||
if search.group:
|
if search.group:
|
||||||
visitedGroups[search.group]['searches'].append(dSearch)
|
visitedGroups[search.group]['searches'].append(dSearch)
|
||||||
else:
|
else:
|
||||||
res.append(dSearch)
|
searches.append(dSearch)
|
||||||
return res
|
if search.default:
|
||||||
|
default = dSearch
|
||||||
|
return Object(searches=searches, default=default).__dict__
|
||||||
|
|
||||||
def getQueryUrl(self, contentType, searchName, startNumber=None):
|
def getQueryUrl(self, contentType, searchName, startNumber=None):
|
||||||
'''This method creates the URL that allows to perform a (non-Ajax)
|
'''This method creates the URL that allows to perform a (non-Ajax)
|
||||||
|
|
|
@ -1285,6 +1285,15 @@ class BaseMixin:
|
||||||
return stateShow(workflow, self.appy())
|
return stateShow(workflow, self.appy())
|
||||||
else: return stateShow
|
else: return stateShow
|
||||||
|
|
||||||
|
def _appy_listStates(self):
|
||||||
|
'''Lists the possible states for this object.'''
|
||||||
|
res = []
|
||||||
|
workflow = self.getWorkflow()
|
||||||
|
for elem in dir(workflow):
|
||||||
|
if getattr(workflow, elem).__class__.__name__ != 'State': continue
|
||||||
|
res.append((elem, self.translate(self.getWorkflowLabel(elem))))
|
||||||
|
return res
|
||||||
|
|
||||||
def _appy_managePermissions(self):
|
def _appy_managePermissions(self):
|
||||||
'''When an object is created or updated, we must update "add"
|
'''When an object is created or updated, we must update "add"
|
||||||
permissions accordingly: if the object is a folder, we must set on
|
permissions accordingly: if the object is a folder, we must set on
|
||||||
|
|
|
@ -209,10 +209,9 @@ setattr(Page, Page.pages.back.attribute, Page.pages.back)
|
||||||
|
|
||||||
# The Tool class ---------------------------------------------------------------
|
# The Tool class ---------------------------------------------------------------
|
||||||
# Prefixes of the fields generated on the Tool.
|
# Prefixes of the fields generated on the Tool.
|
||||||
toolFieldPrefixes = ('defaultValue', 'podTemplate', 'formats', 'resultColumns',
|
toolFieldPrefixes = ('podTemplate', 'formats', 'resultColumns',
|
||||||
'enableAdvancedSearch', 'numberOfSearchColumns',
|
'enableAdvancedSearch', 'numberOfSearchColumns',
|
||||||
'searchFields', 'optionalFields', 'showWorkflow',
|
'searchFields', 'showWorkflow', 'showAllStatesInPhase')
|
||||||
'showAllStatesInPhase')
|
|
||||||
defaultToolFields = ('title', 'mailHost', 'mailEnabled', 'mailFrom',
|
defaultToolFields = ('title', 'mailHost', 'mailEnabled', 'mailFrom',
|
||||||
'appyVersion', 'dateFormat', 'hourFormat', 'users',
|
'appyVersion', 'dateFormat', 'hourFormat', 'users',
|
||||||
'connectedUsers', 'groups', 'translations',
|
'connectedUsers', 'groups', 'translations',
|
||||||
|
|
|
@ -35,10 +35,11 @@ class Ogone(Type):
|
||||||
specificWritePermission=False, width=None, height=None,
|
specificWritePermission=False, width=None, height=None,
|
||||||
colspan=1, master=None, masterValue=None, focus=False,
|
colspan=1, master=None, masterValue=None, focus=False,
|
||||||
mapping=None, label=None):
|
mapping=None, label=None):
|
||||||
Type.__init__(self, None, (0,1), None, None, False, False, show, page,
|
Type.__init__(self, None, (0,1), None, show, page, group, layouts, move,
|
||||||
group, layouts, move, False, False,specificReadPermission,
|
False, False,specificReadPermission,
|
||||||
specificWritePermission, width, height, None, colspan,
|
specificWritePermission, width, height, None, colspan,
|
||||||
master, masterValue, focus, False, True, mapping, label)
|
master, masterValue, focus, False, True, mapping, label,
|
||||||
|
None)
|
||||||
# orderMethod must contain a method returning a dict containing info
|
# orderMethod must contain a method returning a dict containing info
|
||||||
# about the order. Following keys are mandatory:
|
# about the order. Following keys are mandatory:
|
||||||
# * orderID An identifier for the order. Don't use the object UID
|
# * orderID An identifier for the order. Don't use the object UID
|
||||||
|
|
|
@ -29,14 +29,12 @@ class PoMessage:
|
||||||
# The following messages (starting with MSG_) correspond to tool
|
# The following messages (starting with MSG_) correspond to tool
|
||||||
# attributes added for every gen-class (warning: the message IDs correspond
|
# attributes added for every gen-class (warning: the message IDs correspond
|
||||||
# to MSG_<attributePrefix>).
|
# to MSG_<attributePrefix>).
|
||||||
MSG_defaultValue = "Default value for field '%s'"
|
|
||||||
MSG_podTemplate = "POD template for field '%s'"
|
MSG_podTemplate = "POD template for field '%s'"
|
||||||
MSG_formats = "Output format(s) for field '%s'"
|
MSG_formats = "Output format(s) for field '%s'"
|
||||||
MSG_resultColumns = "Columns to display while showing query results"
|
MSG_resultColumns = "Columns to display while showing query results"
|
||||||
MSG_enableAdvancedSearch = "Enable advanced search"
|
MSG_enableAdvancedSearch = "Enable advanced search"
|
||||||
MSG_numberOfSearchColumns = "Number of search columns"
|
MSG_numberOfSearchColumns = "Number of search columns"
|
||||||
MSG_searchFields = "Search fields"
|
MSG_searchFields = "Search fields"
|
||||||
MSG_optionalFields = 'Optional fields'
|
|
||||||
MSG_showWorkflow = 'Show workflow-related information'
|
MSG_showWorkflow = 'Show workflow-related information'
|
||||||
MSG_showAllStatesInPhase = 'Show all states in phase'
|
MSG_showAllStatesInPhase = 'Show all states in phase'
|
||||||
POD_ASKACTION = 'Trigger related action'
|
POD_ASKACTION = 'Trigger related action'
|
||||||
|
|
Binary file not shown.
|
@ -1,6 +0,0 @@
|
||||||
from appy.gen import *
|
|
||||||
|
|
||||||
class Engine:
|
|
||||||
engineType = String()
|
|
||||||
description = String(format=String.XHTML)
|
|
||||||
pod = True
|
|
|
@ -1,5 +0,0 @@
|
||||||
from appy.gen import *
|
|
||||||
|
|
||||||
class Radio:
|
|
||||||
abstract = True
|
|
||||||
name = String()
|
|
|
@ -1,6 +0,0 @@
|
||||||
from appy.gen import *
|
|
||||||
|
|
||||||
class Wheel:
|
|
||||||
title = String(multiplicity=(1,1))
|
|
||||||
description = String(format=String.XHTML)
|
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
from appy.gen import *
|
|
||||||
from AppyCar.Interior.Radio import Radio
|
|
||||||
|
|
||||||
class RallyCarWorkflow:
|
|
||||||
# Roles
|
|
||||||
carDriver = 'CarDriver'
|
|
||||||
driverM = ('Manager', carDriver)
|
|
||||||
# Specific permissions
|
|
||||||
readColor = ReadPermission('Car.color')
|
|
||||||
writeColor = WritePermission('Car.color')
|
|
||||||
# States
|
|
||||||
created = State({r:driverM, w:driverM, d:driverM,
|
|
||||||
readColor: driverM, writeColor: driverM}, initial=True)
|
|
||||||
running = State({r:driverM, w:driverM, d:driverM,
|
|
||||||
readColor: 'Manager', writeColor: 'Manager'})
|
|
||||||
# Transitions
|
|
||||||
run = Transition( (created, running), condition=driverM)
|
|
||||||
stop = Transition( (running, created), condition=driverM)
|
|
||||||
|
|
||||||
class Car:
|
|
||||||
sport = Boolean()
|
|
||||||
color = String(specificReadPermission=True, specificWritePermission=True)
|
|
||||||
description = String(format=String.TEXT)
|
|
||||||
|
|
||||||
class RallyCar(Car):
|
|
||||||
root = True
|
|
||||||
workflow = RallyCarWorkflow
|
|
||||||
test = Integer()
|
|
||||||
|
|
||||||
class StandardRadio(Radio):
|
|
||||||
test1 = Integer()
|
|
||||||
|
|
||||||
c = Config()
|
|
||||||
c.languages = ('en', 'fr')
|
|
|
@ -1,10 +0,0 @@
|
||||||
from appy.gen import *
|
|
||||||
|
|
||||||
class Meeting:
|
|
||||||
place = String(editDefault=True)
|
|
||||||
date = Date()
|
|
||||||
myObservations = String(format=String.XHTML, optional=True)
|
|
||||||
annex = File(optional=True)
|
|
||||||
leader = String(validator=['andyStein', 'joelLambillotte'])
|
|
||||||
root = True
|
|
||||||
pod = ['Meeting']
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,10 +0,0 @@
|
||||||
This folder contains some example Appy applications used for testing purposes.
|
|
||||||
|
|
||||||
Directly in the folder, you have the application "AppyMeeting" which consists in
|
|
||||||
a single Python file (module AppyMeeting.py) and a POD template for producing
|
|
||||||
documents (Meeting.odt). You also have simple applications Zzz.py and
|
|
||||||
ZopeComponent.py that are one-file applications.
|
|
||||||
|
|
||||||
In sub-folder AppyCar, you have a more complex application that is structured
|
|
||||||
as a Python package (a hierarchy of folders).
|
|
||||||
|
|
Binary file not shown.
|
@ -1,130 +0,0 @@
|
||||||
from appy.gen import *
|
|
||||||
|
|
||||||
class BunchOfGeek:
|
|
||||||
description = String(format=String.TEXT)
|
|
||||||
|
|
||||||
class ZopeComponentTool(Tool):
|
|
||||||
someUsefulConfigurationOption = String()
|
|
||||||
def onInstall(self):
|
|
||||||
self.someUsefulConfigurationOption = 'My app is configured now!'
|
|
||||||
install = Action(action=onInstall)
|
|
||||||
|
|
||||||
class ZopeComponentWorkflow:
|
|
||||||
# Specific permissions
|
|
||||||
wf = WritePermission('ZopeComponent.funeralDate')
|
|
||||||
rb = ReadPermission('ZopeComponent.responsibleBunch')
|
|
||||||
# Roles
|
|
||||||
zManager = 'ZManager'
|
|
||||||
zLeader = 'ZLeader'
|
|
||||||
managerM = (zManager, 'Manager')
|
|
||||||
leaderM = (zLeader, 'Manager')
|
|
||||||
everybody = (zManager, zLeader, 'Manager')
|
|
||||||
# States
|
|
||||||
created = State({r:leaderM, w:('Owner', 'Manager'), d:leaderM, wf:'Owner',
|
|
||||||
rb:everybody}, initial=True)
|
|
||||||
validated = State({r:everybody, w:everybody, d:None, wf:everybody,
|
|
||||||
rb:everybody})
|
|
||||||
underDevelopment = State({r:everybody, w:leaderM, d:None, wf:leaderM,
|
|
||||||
rb:everybody})
|
|
||||||
whereIsTheClient = State({r:everybody, w:managerM, d:None, wf:managerM,
|
|
||||||
rb:everybody})
|
|
||||||
# Transitions
|
|
||||||
def funeralOk(self, obj): return obj.funeralDate
|
|
||||||
validate = Transition( (created, validated),
|
|
||||||
condition=managerM + (funeralOk,))
|
|
||||||
def updateDescription(self, obj):
|
|
||||||
obj.description = 'Description edited by my manager was silly.'
|
|
||||||
startDevelopment = Transition( (validated, underDevelopment),
|
|
||||||
condition=leaderM, action=updateDescription)
|
|
||||||
cancelDevelopment = Transition( (underDevelopment, whereIsTheClient),
|
|
||||||
condition=managerM)
|
|
||||||
cancel = Transition( ( (whereIsTheClient, underDevelopment),
|
|
||||||
(underDevelopment, validated),
|
|
||||||
(validated, created)), condition='Manager')
|
|
||||||
|
|
||||||
class ZopeComponent:
|
|
||||||
root = True
|
|
||||||
workflow = ZopeComponentWorkflow
|
|
||||||
def showDate(self):
|
|
||||||
return True
|
|
||||||
def validateDescription(self, value):
|
|
||||||
res = True
|
|
||||||
if value.find('simple') != -1:
|
|
||||||
res = self.translate('zope_3_is_not_simple')
|
|
||||||
return res
|
|
||||||
description = String(editDefault=True)
|
|
||||||
technicalDescription = String(format=String.XHTML,
|
|
||||||
validator=validateDescription)
|
|
||||||
#status = String(validator=['underDevelopement', 'stillSomeWorkToPerform',
|
|
||||||
# 'weAreAlmostFinished', 'alphaReleaseIsBugged', 'whereIsTheClient'],
|
|
||||||
# optional=True, editDefault=True)
|
|
||||||
funeralDate = Date(optional=True, specificWritePermission=True)
|
|
||||||
responsibleBunch = Ref(BunchOfGeek, multiplicity=(1,1), add=False,
|
|
||||||
link=True, back=Ref(attribute='components'),
|
|
||||||
specificReadPermission=True)
|
|
||||||
|
|
||||||
class CobolComponentWorkflow(ZopeComponentWorkflow):
|
|
||||||
p = ZopeComponentWorkflow # Shortcut to workflow parent
|
|
||||||
# An additional state
|
|
||||||
finished = State(p.whereIsTheClient.permissions)
|
|
||||||
# Override validate: condition on funeralDate has no sense here
|
|
||||||
validate = Transition(p.validate.states, condition=p.managerM)
|
|
||||||
# Override cancelDevelopment: go to finished instead
|
|
||||||
cancelDevelopment = Transition( (p.underDevelopment, finished),
|
|
||||||
condition=p.managerM)
|
|
||||||
# Update cancel accordingly
|
|
||||||
cancel = Transition( ((finished, p.underDevelopment),) +p.cancel.states[1:],
|
|
||||||
condition=p.cancel.condition)
|
|
||||||
|
|
||||||
class CobolComponent:
|
|
||||||
root = True
|
|
||||||
workflow = CobolComponentWorkflow
|
|
||||||
description = String()
|
|
||||||
|
|
||||||
class Person:
|
|
||||||
abstract = True
|
|
||||||
pod = True
|
|
||||||
title = String(show=False)
|
|
||||||
n = 'name_3'
|
|
||||||
firstName = String(group=n, width=15)
|
|
||||||
middleInitial = String(group=n, width=3)
|
|
||||||
name = String(multiplicity=(1,1), group=n, width=30)
|
|
||||||
contractDetails = String(format=String.XHTML)
|
|
||||||
cia = {'page': 'contactInformation', 'group': 'address_2'}
|
|
||||||
street = String(**cia)
|
|
||||||
number = Integer(**cia)
|
|
||||||
country = String(**cia)
|
|
||||||
zipCode = Integer(**cia)
|
|
||||||
cio = {'page': 'contactInformation', 'group': 'numbers_2', 'width': 20}
|
|
||||||
phoneNumber = String(**cio)
|
|
||||||
faxNumber = String(**cio)
|
|
||||||
mobilePhone = String(**cio)
|
|
||||||
workPhoneNumber = String(**cio)
|
|
||||||
workFaxNumber = String(**cio)
|
|
||||||
workMobilePhone = String(**cio)
|
|
||||||
def onEdit(self, created):
|
|
||||||
self.title = self.firstName + ' ' + self.name
|
|
||||||
|
|
||||||
class Worker(Person):
|
|
||||||
root = True
|
|
||||||
productivity = Integer()
|
|
||||||
|
|
||||||
class Parasite(Person):
|
|
||||||
root = True
|
|
||||||
pod = ['SordidGossips', 'MoreGossips']
|
|
||||||
hairColor = String(group='hairyCharacteristics')
|
|
||||||
sordidGossips = String(format = String.XHTML, page='Gossips')
|
|
||||||
parasiteIndex = String(validator=['low', 'medium', 'high',
|
|
||||||
'unquantifiable'],
|
|
||||||
page='contactInformation', group='numbers')
|
|
||||||
details = String(page='contactInformation', group='numbers',
|
|
||||||
master=parasiteIndex, masterValue='unquantifiable')
|
|
||||||
avoidAnyPhysicalContact = Boolean(page='contactInformation')
|
|
||||||
def validate(self, new, errors):
|
|
||||||
if (new.hairColor == 'flashy') and (new.firstName == 'Gerard'):
|
|
||||||
errors.hairColor = True
|
|
||||||
errors.firstName = "Flashy Gerards are disgusting."
|
|
||||||
|
|
||||||
c = Config()
|
|
||||||
c.languages = ('en', 'fr')
|
|
||||||
c.defaultCreators += ['ZLeader']
|
|
|
@ -1,67 +0,0 @@
|
||||||
from appy.gen import *
|
|
||||||
class Zzz:
|
|
||||||
root = True
|
|
||||||
def show_f2(self): return True
|
|
||||||
def validate_i2(self, value):
|
|
||||||
if (value != None) and (value < 10):
|
|
||||||
return 'Value must be higher or equal to 10.'
|
|
||||||
return True
|
|
||||||
title=String(multiplicity=(0,1), show=False)
|
|
||||||
i1 = Integer(show=False)
|
|
||||||
i2 = Integer(validator = validate_i2)
|
|
||||||
f1 = Float(show=show_f2, page='other')
|
|
||||||
f2 = Float(multiplicity=(1,1))
|
|
||||||
|
|
||||||
class SeveralStrings:
|
|
||||||
root=True
|
|
||||||
anEmail = String(validator=String.EMAIL)
|
|
||||||
anUrl = String(validator=String.URL)
|
|
||||||
anAlphanumericValue = String(validator=String.ALPHANUMERIC)
|
|
||||||
aSingleSelectedValue = String(validator=['valueA', 'valueB', 'valueC'])
|
|
||||||
aSingleMandatorySelectedValue = String(
|
|
||||||
validator=['valueX', 'valueY', 'valueZ'], multiplicity=(1,1))
|
|
||||||
aMultipleSelectedValue = String(
|
|
||||||
validator=['valueS', 'valueT', 'valueU', 'valueV'],
|
|
||||||
multiplicity=(1,None), searchable=True)
|
|
||||||
aBooleanValue = Boolean(default=True)
|
|
||||||
dateWithHour = Date()
|
|
||||||
dateWithoutHour = Date(format=Date.WITHOUT_HOUR)
|
|
||||||
anAttachedFile = File()
|
|
||||||
anAttachedImage = File(isImage=True)
|
|
||||||
|
|
||||||
class Product:
|
|
||||||
root = True
|
|
||||||
description = String(format=String.TEXT)
|
|
||||||
stock = Integer()
|
|
||||||
def needOrder(self): return self.stock < 3
|
|
||||||
def orderProduct(self): self.stock = 3
|
|
||||||
order = Action(action=orderProduct, show=needOrder)
|
|
||||||
|
|
||||||
class Order:
|
|
||||||
description = String(format=String.TEXT)
|
|
||||||
number = Float(show=False)
|
|
||||||
# Reference field
|
|
||||||
def getReference(self): return 'OR-%f' % self.number
|
|
||||||
reference = Computed(method=getReference)
|
|
||||||
def filterProducts(self, allProducts):
|
|
||||||
return [f for f in allProducts if f.description.find('Descr') != -1]
|
|
||||||
products = Ref(Product, add=False, link=True, multiplicity=(1,None),
|
|
||||||
back=Ref(attribute='orders'), showHeaders=True,
|
|
||||||
shownInfo=('description','title', 'order'),
|
|
||||||
select=filterProducts)
|
|
||||||
def onEdit(self, created):
|
|
||||||
if created:
|
|
||||||
import random
|
|
||||||
self.number = random.random()
|
|
||||||
|
|
||||||
class Client:
|
|
||||||
root = True
|
|
||||||
folder = True
|
|
||||||
title = String(show=False)
|
|
||||||
firstName = String()
|
|
||||||
name = String()
|
|
||||||
orders = Ref(Order, add=True, link=False, multiplicity=(0,None),
|
|
||||||
back=Ref(attribute='client'), showHeaders=True,
|
|
||||||
shownInfo=('reference', 'description', 'products'), wide=True)
|
|
||||||
def onEdit(self, created):
|
|
||||||
self.title = self.firstName + ' ' + self.name
|
|
|
@ -26,9 +26,10 @@
|
||||||
<div class="portletSep" tal:define="nb repeat/rootClass/number"
|
<div class="portletSep" tal:define="nb repeat/rootClass/number"
|
||||||
tal:condition="python: (nb == 1 and contextObj) or (nb != 1)"></div>
|
tal:condition="python: (nb == 1 and contextObj) or (nb != 1)"></div>
|
||||||
|
|
||||||
<div class="portletContent">
|
<div class="portletContent" tal:define="searchInfo python: tool.getSearches(rootClass)">
|
||||||
<tal:comment replace="nothing">Section title, with action icons</tal:comment>
|
<tal:comment replace="nothing">Section title (link triggers the default search), with action icons</tal:comment>
|
||||||
<a tal:attributes="href python: '%s?className=%s' % (queryUrl, rootClass);
|
<a tal:define="queryParam python: searchInfo['default'] and ('&search=%s' % searchInfo['default']['name']) or ''"
|
||||||
|
tal:attributes="href python: '%s?className=%s%s' % (queryUrl, rootClass, queryParam);
|
||||||
class python:test(not currentSearch and (currentClass==rootClass), 'portletCurrent', '')"
|
class python:test(not currentSearch and (currentClass==rootClass), 'portletCurrent', '')"
|
||||||
tal:content="structure python: _(rootClass + '_plural')">
|
tal:content="structure python: _(rootClass + '_plural')">
|
||||||
</a>
|
</a>
|
||||||
|
@ -56,7 +57,7 @@
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<tal:comment replace="nothing">Searches for this content type.</tal:comment>
|
<tal:comment replace="nothing">Searches for this content type.</tal:comment>
|
||||||
<tal:searchOrGroup repeat="searchOrGroup python: tool.getSearches(rootClass)">
|
<tal:searchOrGroup repeat="searchOrGroup searchInfo/searches">
|
||||||
<tal:group condition="searchOrGroup/isGroup">
|
<tal:group condition="searchOrGroup/isGroup">
|
||||||
<tal:expanded define="group searchOrGroup;
|
<tal:expanded define="group searchOrGroup;
|
||||||
expanded python: request.get(group['labelId'], 'collapsed') == 'expanded'">
|
expanded python: request.get(group['labelId'], 'collapsed') == 'expanded'">
|
||||||
|
@ -83,13 +84,13 @@
|
||||||
</tal:group>
|
</tal:group>
|
||||||
<dt tal:define="search searchOrGroup" tal:condition="not: searchOrGroup/isGroup"
|
<dt tal:define="search searchOrGroup" tal:condition="not: searchOrGroup/isGroup"
|
||||||
class="portletAppyItem portletSearch">
|
class="portletAppyItem portletSearch">
|
||||||
|
|
||||||
<a tal:attributes="href python: '%s?className=%s&search=%s' % (queryUrl, rootClass, search['name']);
|
<a tal:attributes="href python: '%s?className=%s&search=%s' % (queryUrl, rootClass, search['name']);
|
||||||
title search/descr;
|
title search/descr;
|
||||||
class python: test(search['name'] == currentSearch, 'portletCurrent', '');"
|
class python: test(search['name'] == currentSearch, 'portletCurrent', '');"
|
||||||
tal:content="structure search/label"></a>
|
tal:content="structure search/label"></a>
|
||||||
</dt>
|
</dt>
|
||||||
</tal:searchOrGroup>
|
</tal:searchOrGroup>
|
||||||
|
</div>
|
||||||
</tal:section>
|
</tal:section>
|
||||||
</metal:portlet>
|
</metal:portlet>
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,14 @@
|
||||||
<label tal:content="python: _(widget['labelId'])"></label><br>
|
<label tal:content="python: _(widget['labelId'])"></label><br>
|
||||||
<tal:from define="fromName python: '%s*float' % widgetName">
|
<tal:from define="fromName python: '%s*float' % widgetName">
|
||||||
<label tal:attributes="for fromName" tal:content="python: _('search_from')"></label>
|
<label tal:attributes="for fromName" tal:content="python: _('search_from')"></label>
|
||||||
<input type="text" tal:attributes="name fromName; maxlength maxChars" size="4"/>
|
<input type="text" size="4"
|
||||||
|
tal:attributes="name fromName; maxlength maxChars;
|
||||||
|
value python: widget['defaultForSearch'][0]"/>
|
||||||
</tal:from>
|
</tal:from>
|
||||||
<tal:to define="toName python: '%s_to' % name">
|
<tal:to define="toName python: '%s_to' % name">
|
||||||
<label tal:attributes="for toName" tal:content="python: _('search_to')"></label>
|
<label tal:attributes="for toName" tal:content="python: _('search_to')"></label>
|
||||||
<input type="text" tal:attributes="name toName; maxlength maxChars" size="4"/>
|
<input type="text" size="4"
|
||||||
|
tal:attributes="name toName; maxlength maxChars;
|
||||||
|
value python: widget['defaultForSearch'][1]"/>
|
||||||
</tal:to><br/>
|
</tal:to><br/>
|
||||||
</metal:search>
|
</metal:search>
|
||||||
|
|
|
@ -23,10 +23,14 @@
|
||||||
<label tal:content="python: _(widget['labelId'])"></label><br>
|
<label tal:content="python: _(widget['labelId'])"></label><br>
|
||||||
<tal:from define="fromName python: '%s*int' % widgetName">
|
<tal:from define="fromName python: '%s*int' % widgetName">
|
||||||
<label tal:attributes="for fromName" tal:content="python: _('search_from')"></label>
|
<label tal:attributes="for fromName" tal:content="python: _('search_from')"></label>
|
||||||
<input type="text" tal:attributes="name fromName; maxlength maxChars" size="4"/>
|
<input type="text" size="4"
|
||||||
|
tal:attributes="name fromName; maxlength maxChars;
|
||||||
|
value python: widget['defaultForSearch'][0]"/>
|
||||||
</tal:from>
|
</tal:from>
|
||||||
<tal:to define="toName python: '%s_to' % name">
|
<tal:to define="toName python: '%s_to' % name">
|
||||||
<label tal:attributes="for toName" tal:content="python: _('search_to')"></label>
|
<label tal:attributes="for toName" tal:content="python: _('search_to')"></label>
|
||||||
<input type="text" tal:attributes="name toName; maxlength maxChars" size="4"/>
|
<input type="text" size="4"
|
||||||
|
tal:attributes="name toName; maxlength maxChars;
|
||||||
|
value python: widget['defaultForSearch'][1]"/>
|
||||||
</tal:to><br/>
|
</tal:to><br/>
|
||||||
</metal:search>
|
</metal:search>
|
||||||
|
|
|
@ -89,7 +89,8 @@
|
||||||
<input type="text" tal:define="maxChars python: test(widget['maxChars'], widget['maxChars'], '')"
|
<input type="text" tal:define="maxChars python: test(widget['maxChars'], widget['maxChars'], '')"
|
||||||
tal:attributes="name python: '%s*string-%s' % (widgetName, widget['transform']);
|
tal:attributes="name python: '%s*string-%s' % (widgetName, widget['transform']);
|
||||||
maxlength maxChars;
|
maxlength maxChars;
|
||||||
style python: 'text-transform:%s' % widget['transform']"/>
|
style python: 'text-transform:%s' % widget['transform'];
|
||||||
|
value widget/defaultForSearch"/>
|
||||||
</tal:simpleSearch>
|
</tal:simpleSearch>
|
||||||
<tal:comment replace="nothing">Show a multi-selection box for fields whose
|
<tal:comment replace="nothing">Show a multi-selection box for fields whose
|
||||||
validator defines a list of values, with a "AND/OR" checkbox.</tal:comment>
|
validator defines a list of values, with a "AND/OR" checkbox.</tal:comment>
|
||||||
|
@ -105,9 +106,11 @@
|
||||||
<label tal:attributes="for andName" tal:content="python: _('search_and')"></label><br/>
|
<label tal:attributes="for andName" tal:content="python: _('search_and')"></label><br/>
|
||||||
</tal:operator>
|
</tal:operator>
|
||||||
<tal:comment replace="nothing">The list of values</tal:comment>
|
<tal:comment replace="nothing">The list of values</tal:comment>
|
||||||
<select tal:attributes="name widgetName; size widget/height" multiple="multiple">
|
<select tal:define="preSelected widget/defaultForSearch"
|
||||||
|
tal:attributes="name widgetName; size widget/height" multiple="multiple">
|
||||||
<option tal:repeat="v python:tool.getPossibleValues(name, withTranslations=True, withBlankValue=False, className=className)"
|
<option tal:repeat="v python:tool.getPossibleValues(name, withTranslations=True, withBlankValue=False, className=className)"
|
||||||
tal:attributes="value python:v[0]; title python: v[1]"
|
tal:attributes="value python:v[0]; title python: v[1];
|
||||||
|
selected python: v[0] in preSelected"
|
||||||
tal:content="python: tool.truncateValue(v[1], widget)">
|
tal:content="python: tool.truncateValue(v[1], widget)">
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
|
@ -83,16 +83,9 @@ class ToolWrapper(AbstractWrapper):
|
||||||
'''Some names of Tool attributes are not easy to guess. For example,
|
'''Some names of Tool attributes are not easy to guess. For example,
|
||||||
the attribute that stores the names of the columns to display in
|
the attribute that stores the names of the columns to display in
|
||||||
query results for class A that is in package x.y is
|
query results for class A that is in package x.y is
|
||||||
"tool.resultColumnsForx_y_A". Other example: the attribute that
|
"tool.resultColumnsForx_y_A". This method generates the attribute
|
||||||
stores the editable default value of field "f1" of class x.y.A is
|
|
||||||
"tool.defaultValueForx_y_A_f1". This method generates the attribute
|
|
||||||
name based on p_attributeType, a p_klass from the application, and a
|
name based on p_attributeType, a p_klass from the application, and a
|
||||||
p_attrName (given only if needed, for example if p_attributeType is
|
p_attrName (given only if needed). p_attributeType may be:
|
||||||
"defaultValue"). p_attributeType may be:
|
|
||||||
|
|
||||||
"defaultValue"
|
|
||||||
Stores the editable default value for a given p_attrName of a
|
|
||||||
given p_klass.
|
|
||||||
|
|
||||||
"podTemplate"
|
"podTemplate"
|
||||||
Stores the pod template for p_attrName.
|
Stores the pod template for p_attrName.
|
||||||
|
@ -117,10 +110,6 @@ class ToolWrapper(AbstractWrapper):
|
||||||
Determines, among all indexed fields for p_klass, which one will
|
Determines, among all indexed fields for p_klass, which one will
|
||||||
really be used in the search screen.
|
really be used in the search screen.
|
||||||
|
|
||||||
"optionalFields"
|
|
||||||
Stores the list of optional attributes that are in use in the
|
|
||||||
tool for the given p_klass.
|
|
||||||
|
|
||||||
"showWorkflow"
|
"showWorkflow"
|
||||||
Stores the boolean field indicating if we must show workflow-
|
Stores the boolean field indicating if we must show workflow-
|
||||||
related information for p_klass or not.
|
related information for p_klass or not.
|
||||||
|
|
Loading…
Reference in a new issue