[gen] Changes in parameters of some basic field methods to enable field.languages to be defined dymanically via a method.

This commit is contained in:
Gaetan Delannay 2014-09-05 17:13:23 +02:00
parent 18afb4416c
commit f8a7103c7a
15 changed files with 153 additions and 112 deletions

View file

@ -63,7 +63,7 @@ class Field:
value=not isSearch and \ value=not isSearch and \
field.getFormattedValue(zobj, rawValue, showChanges); field.getFormattedValue(zobj, rawValue, showChanges);
requestValue=not isSearch and zobj.getRequestFieldValue(name); requestValue=not isSearch and zobj.getRequestFieldValue(name);
inRequest=field.valueIsInRequest(req, name); inRequest=field.valueIsInRequest(zobj, req, name);
error=req.get('%s_error' % name); error=req.get('%s_error' % name);
isMultiple=(field.multiplicity[1] == None) or \ isMultiple=(field.multiplicity[1] == None) or \
(field.multiplicity[1] > 1); (field.multiplicity[1] > 1);
@ -360,7 +360,7 @@ class Field:
else: else:
master, masterValue = masterData master, masterValue = masterData
if masterValue and callable(masterValue): return True if masterValue and callable(masterValue): return True
reqValue = master.getRequestValue(obj.REQUEST) reqValue = master.getRequestValue(obj)
# reqValue can be a list or not # reqValue can be a list or not
if type(reqValue) not in sutils.sequenceTypes: if type(reqValue) not in sutils.sequenceTypes:
return reqValue in masterValue return reqValue in masterValue
@ -517,7 +517,7 @@ class Field:
def getValue(self, obj): def getValue(self, obj):
'''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(obj, value):
# If there is no value, get the default value if any: return # If there is no value, get the default value if any: return
# self.default, of self.default() if it is a method. # self.default, of self.default() if it is a method.
if callable(self.default): if callable(self.default):
@ -539,7 +539,7 @@ class Field:
purposes. Needs to be overridden by some child classes. If purposes. Needs to be overridden by some child classes. If
p_showChanges is True, the result must also include the changes that p_showChanges is True, the result must also include the changes that
occurred on p_value across the ages.''' occurred on p_value across the ages.'''
if self.isEmptyValue(value): return '' if self.isEmptyValue(obj, value): return ''
return value return value
def getIndexType(self): def getIndexType(self):
@ -573,7 +573,7 @@ class Field:
res = str(res) res = str(res)
return res return res
def valueIsInRequest(self, request, name): def valueIsInRequest(self, obj, request, name):
'''Is there a value corresponding to this field in the request? p_name '''Is there a value corresponding to this field in the request? p_name
can be different from self.name (ie, if it is a field within another can be different from self.name (ie, if it is a field within another
(List) field). In most cases, checking that this p_name is in the (List) field). In most cases, checking that this p_name is in the
@ -581,7 +581,7 @@ class Field:
for string multilingual fields.''' for string multilingual fields.'''
return request.has_key(name) return request.has_key(name)
def getRequestValue(self, request, requestName=None): def getRequestValue(self, obj, requestName=None):
'''Gets a value for this field as carried in the request object. In the '''Gets a value for this field as carried in the request object. In the
simplest cases, the request value is a single value whose name in the simplest cases, the request value is a single value whose name in the
request is the name of the field. request is the name of the field.
@ -595,15 +595,15 @@ class Field:
the container field). In this case, p_requestName must be used for the container field). In this case, p_requestName must be used for
searching into the request, instead of the field name (self.name).''' searching into the request, instead of the field name (self.name).'''
name = requestName or self.name name = requestName or self.name
return request.get(name, None) return obj.REQUEST.get(name, None)
def getStorableValue(self, value): def getStorableValue(self, obj, value):
'''p_value is a valid value initially computed through calling '''p_value is a valid value initially computed through calling
m_getRequestValue. So, it is a valid string (or list of strings) m_getRequestValue. So, it is a valid string (or list of strings)
representation of the field value coming from the request. representation of the field value coming from the request.
This method computes the real (potentially converted or manipulated This method computes the real (potentially converted or manipulated
in some other way) value as can be stored in the database.''' in some other way) value as can be stored in the database.'''
if self.isEmptyValue(value): return if self.isEmptyValue(obj, value): return
return value return value
def getMasterData(self): def getMasterData(self):
@ -634,11 +634,11 @@ class Field:
return 'updateSlaves(this,null,%s,%s,null,null%s)' % \ return 'updateSlaves(this,null,%s,%s,null,null%s)' % \
(q(zobj.absolute_url()), q(layoutType), cName) (q(zobj.absolute_url()), q(layoutType), cName)
def isEmptyValue(self, value, obj=None): def isEmptyValue(self, obj, value):
'''Returns True if the p_value must be considered as an empty value.''' '''Returns True if the p_value must be considered as an empty value.'''
return value in self.nullValues return value in self.nullValues
def isCompleteValue(self, value, obj=None): def isCompleteValue(self, obj, value):
'''Returns True if the p_value must be considered as "complete". While, '''Returns True if the p_value must be considered as "complete". While,
in most cases, a "complete" value simply means a "non empty" value in most cases, a "complete" value simply means a "non empty" value
(see m_isEmptyValue above), in some special cases it is more subtle. (see m_isEmptyValue above), in some special cases it is more subtle.
@ -648,7 +648,7 @@ class Field:
a Date with the "hour" part required will not be considered as empty a Date with the "hour" part required will not be considered as empty
if the "day, month, year" part is present but will not be considered if the "day, month, year" part is present but will not be considered
as complete without the "hour, minute" part.''' as complete without the "hour, minute" part.'''
return not self.isEmptyValue(value, obj) return not self.isEmptyValue(obj, value)
def validateValue(self, obj, value): def validateValue(self, obj, value):
'''This method may be overridden by child classes and will be called at '''This method may be overridden by child classes and will be called at
@ -673,7 +673,7 @@ class Field:
definition. If it is the case, None is returned. Else, a translated definition. If it is the case, None is returned. Else, a translated
error message is returned.''' error message is returned.'''
# If the value is required, check that a (complete) value is present. # If the value is required, check that a (complete) value is present.
if not self.isCompleteValue(value, obj): if not self.isCompleteValue(obj, value):
if self.required and self.isClientVisible(obj): if self.required and self.isClientVisible(obj):
# If the field is required, but not visible according to # If the field is required, but not visible according to
# master/slave relationships, we consider it not to be required. # master/slave relationships, we consider it not to be required.
@ -686,7 +686,7 @@ class Field:
message = self.validateValue(obj, value) message = self.validateValue(obj, value)
if message: return message if message: return message
# Evaluate the custom validator if one has been specified # Evaluate the custom validator if one has been specified
value = self.getStorableValue(value) value = self.getStorableValue(obj, value)
if self.validator and (type(self.validator) in self.validatorTypes): if self.validator and (type(self.validator) in self.validatorTypes):
obj = obj.appy() obj = obj.appy()
if type(self.validator) != self.validatorTypes[-1]: if type(self.validator) != self.validatorTypes[-1]:

View file

@ -117,8 +117,8 @@ class Boolean(Field):
def getFormattedValue(self, obj, value, showChanges=False): def getFormattedValue(self, obj, value, showChanges=False):
return obj.translate(self.getValueLabel(value)) return obj.translate(self.getValueLabel(value))
def getStorableValue(self, value): def getStorableValue(self, obj, value):
if not self.isEmptyValue(value): if not self.isEmptyValue(obj, value):
exec 'res = %s' % value exec 'res = %s' % value
return res return res

View file

@ -207,7 +207,7 @@ class Date(Field):
return obj.translate('bad_date') return obj.translate('bad_date')
def getFormattedValue(self, obj, value, showChanges=False): def getFormattedValue(self, obj, value, showChanges=False):
if self.isEmptyValue(value): return '' if self.isEmptyValue(obj, value): return ''
tool = obj.getTool().appy() tool = obj.getTool().appy()
# A problem may occur with some extreme year values. Replace the "year" # A problem may occur with some extreme year values. Replace the "year"
# part "by hand". # part "by hand".
@ -219,7 +219,8 @@ class Date(Field):
res += ' %s' % value.strftime(tool.hourFormat) res += ' %s' % value.strftime(tool.hourFormat)
return res return res
def getRequestValue(self, request, requestName=None): def getRequestValue(self, obj, requestName=None):
request = obj.REQUEST
name = requestName or self.name name = requestName or self.name
# Manage the "date" part # Manage the "date" part
value = '' value = ''
@ -238,8 +239,8 @@ class Date(Field):
value = value[:-1] value = value[:-1]
return value return value
def getStorableValue(self, value): def getStorableValue(self, obj, value):
if not self.isEmptyValue(value): if not self.isEmptyValue(obj, value):
import DateTime import DateTime
return DateTime.DateTime(value) return DateTime.DateTime(value)

View file

@ -327,16 +327,15 @@ class File(Field):
historized, mapping, label, sdefault, scolspan, swidth, historized, mapping, label, sdefault, scolspan, swidth,
sheight, True) sheight, True)
def getRequestValue(self, request, requestName=None): def getRequestValue(self, obj, requestName=None):
name = requestName or self.name name = requestName or self.name
return request.get('%s_file' % name) return obj.REQUEST.get('%s_file' % name)
def getDefaultLayouts(self): return {'view':'l-f','edit':'lrv-f'} def getDefaultLayouts(self): return {'view':'l-f','edit':'lrv-f'}
def isEmptyValue(self, value, obj=None): def isEmptyValue(self, obj, value):
'''Must p_value be considered as empty?''' '''Must p_value be considered as empty?'''
if not obj: return Field.isEmptyValue(self, value) if value: return
if value: return False
# If "nochange", the value must not be considered as empty # If "nochange", the value must not be considered as empty
return obj.REQUEST.get('%s_delete' % self.name) != 'nochange' return obj.REQUEST.get('%s_delete' % self.name) != 'nochange'

View file

@ -94,8 +94,8 @@ class Float(Field):
except ValueError: except ValueError:
return obj.translate('bad_%s' % self.pythonType.__name__) return obj.translate('bad_%s' % self.pythonType.__name__)
def getStorableValue(self, value): def getStorableValue(self, obj, value):
if not self.isEmptyValue(value): if not self.isEmptyValue(obj, value):
for sep in self.sep: value = value.replace(sep, '.') for sep in self.sep: value = value.replace(sep, '.')
value = value.replace(self.tsep, '') value = value.replace(self.tsep, '')
return self.pythonType(value) return self.pythonType(value)

View file

@ -68,10 +68,10 @@ class Integer(Field):
except ValueError: except ValueError:
return obj.translate('bad_%s' % self.pythonType.__name__) return obj.translate('bad_%s' % self.pythonType.__name__)
def getStorableValue(self, value): def getStorableValue(self, obj, value):
if not self.isEmptyValue(value): return self.pythonType(value) if not self.isEmptyValue(obj, value): return self.pythonType(value)
def getFormattedValue(self, obj, value, showChanges=False): def getFormattedValue(self, obj, value, showChanges=False):
if self.isEmptyValue(value): return '' if self.isEmptyValue(obj, value): return ''
return str(value) return str(value)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -119,8 +119,9 @@ class List(Field):
res.append(elem) res.append(elem)
return res return res
def getRequestValue(self, request, requestName=None): def getRequestValue(self, obj, requestName=None):
'''Concatenates the list from distinct form elements in the request.''' '''Concatenates the list from distinct form elements in the request.'''
request = obj.REQUEST
name = requestName or self.name # A List may be into another List (?) name = requestName or self.name # A List may be into another List (?)
prefix = name + '*' + self.fields[0][0] + '*' prefix = name + '*' + self.fields[0][0] + '*'
res = {} res = {}
@ -133,7 +134,7 @@ class List(Field):
if rowIndex == -1: continue # Ignore the template row. if rowIndex == -1: continue # Ignore the template row.
for subName, subField in self.fields: for subName, subField in self.fields:
keyName = '%s*%s*%s' % (name, subName, rowIndex) keyName = '%s*%s*%s' % (name, subName, rowIndex)
v = subField.getRequestValue(request, requestName=keyName) v = subField.getRequestValue(obj, requestName=keyName)
setattr(row, subName, v) setattr(row, subName, v)
res[rowIndex] = row res[rowIndex] = row
# Produce a sorted list. # Produce a sorted list.
@ -148,7 +149,7 @@ class List(Field):
request.set(name, res) request.set(name, res)
return res return res
def getStorableValue(self, value): def getStorableValue(self, obj, value):
'''Gets p_value in a form that can be stored in the database.''' '''Gets p_value in a form that can be stored in the database.'''
res = [] res = []
for v in value: for v in value:
@ -156,11 +157,11 @@ class List(Field):
for name, field in self.fields: for name, field in self.fields:
subValue = getattr(v, name) subValue = getattr(v, name)
try: try:
setattr(sv, name, field.getStorableValue(subValue)) setattr(sv, name, field.getStorableValue(obj, subValue))
except ValueError: except ValueError:
# The value for this field for this specific row is # The value for this field for this specific row is
# incorrect. It can happen in the process of validating the # incorrect. It can happen in the process of validating the
# whole List field (a call to getStorableValue occurs at # whole List field (a call to m_getStorableValue occurs at
# this time). We don't care about it, because later on we # this time). We don't care about it, because later on we
# will have sub-field specific validation that will also # will have sub-field specific validation that will also
# detect the error and will prevent storing the wrong value # detect the error and will prevent storing the wrong value

View file

@ -295,7 +295,7 @@ class Pod(Field):
tool = obj.tool tool = obj.tool
diskFolder = tool.getDiskFolder() diskFolder = tool.getDiskFolder()
# Get the path to the pod template. # Get the path to the pod template.
templatePath = os.path.join(diskFolder, template) templatePath = sutils.resolvePath(os.path.join(diskFolder, template))
if not os.path.isfile(templatePath): if not os.path.isfile(templatePath):
raise Exception(self.TEMPLATE_NOT_FOUND % templatePath) raise Exception(self.TEMPLATE_NOT_FOUND % templatePath)
# Get or compute the specific POD context # Get or compute the specific POD context

View file

@ -130,8 +130,8 @@ class String(Field):
pxMultilingual = Px(''' pxMultilingual = Px('''
<!-- Horizontally-layouted multilingual field --> <!-- Horizontally-layouted multilingual field -->
<table if="mLayout == 'horizontal'" width="100%" <table if="mLayout == 'horizontal'" width="100%"
var="count=len(field.languages)"> var="count=len(languages)">
<tr valign="top"><x for="lg in field.languages"><x>:field.pxLanguage</x> <tr valign="top"><x for="lg in languages"><x>:field.pxLanguage</x>
<td width=":'%d%%' % int(100.0/count)" <td width=":'%d%%' % int(100.0/count)"
var="requestValue=requestValue[lg]|None; var="requestValue=requestValue[lg]|None;
value=value[lg]|emptyDefault">:field.subPx[layoutType][fmt]</td> value=value[lg]|emptyDefault">:field.subPx[layoutType][fmt]</td>
@ -139,7 +139,7 @@ class String(Field):
<!-- Vertically-layouted multilingual field --> <!-- Vertically-layouted multilingual field -->
<table if="mLayout == 'vertical'"> <table if="mLayout == 'vertical'">
<tr valign="top" height="20px" for="lg in field.languages"> <tr valign="top" height="20px" for="lg in languages">
<x>:field.pxLanguage</x> <x>:field.pxLanguage</x>
<td var="requestValue=requestValue[lg]|None; <td var="requestValue=requestValue[lg]|None;
value=value[lg]|emptyDefault">:field.subPx[layoutType][fmt]</td> value=value[lg]|emptyDefault">:field.subPx[layoutType][fmt]</td>
@ -147,7 +147,8 @@ class String(Field):
pxView = Px(''' pxView = Px('''
<x var="fmt=field.format; isUrl=field.isUrl; <x var="fmt=field.format; isUrl=field.isUrl;
multilingual=field.isMultilingual(); languages=field.getAttribute(zobj, 'languages');
multilingual=len(languages) &gt; 1;
mLayout=multilingual and field.getLanguagesLayout('view'); mLayout=multilingual and field.getLanguagesLayout('view');
mayAjaxEdit=not showChanges and field.inlineEdit and \ mayAjaxEdit=not showChanges and field.inlineEdit and \
zobj.mayEdit(field.writePermission)"> zobj.mayEdit(field.writePermission)">
@ -193,7 +194,8 @@ class String(Field):
pxEdit = Px(''' pxEdit = Px('''
<x var="fmt=field.format; <x var="fmt=field.format;
multilingual=field.isMultilingual(); languages=field.getAttribute(zobj, 'languages');
multilingual=len(languages) &gt; 1;
mLayout=multilingual and field.getLanguagesLayout('edit')"> mLayout=multilingual and field.getLanguagesLayout('edit')">
<select if="field.isSelect" <select if="field.isSelect"
var2="possibleValues=field.getPossibleValues(zobj, \ var2="possibleValues=field.getPossibleValues(zobj, \
@ -447,7 +449,7 @@ class String(Field):
def checkParameters(self): def checkParameters(self):
'''Ensures this String is correctly defined.''' '''Ensures this String is correctly defined.'''
error = None error = None
if self.isMultilingual(): if self.isMultilingual(None):
if self.isSelect: if self.isSelect:
error = "A selection field can't be multilingual." error = "A selection field can't be multilingual."
elif self.format in (String.PASSWORD, String.CAPTCHA): elif self.format in (String.PASSWORD, String.CAPTCHA):
@ -468,7 +470,11 @@ class String(Field):
res = False res = False
return res return res
def isMultilingual(self): return len(self.languages) > 1 def isMultilingual(self, obj):
'''Is this field multilingual ?.'''
# In the following case, impossible to know: we say no.
if not obj and callable(self.languages): return
return len(self.getAttribute(obj, 'languages')) > 1
def getDefaultLayouts(self): def getDefaultLayouts(self):
'''Returns the default layouts for this type. Default layouts can vary '''Returns the default layouts for this type. Default layouts can vary
@ -507,45 +513,50 @@ class String(Field):
value = list(value) value = list(value)
return value return value
def valueIsInRequest(self, request, name): def valueIsInRequest(self, obj, request, name):
if not self.isMultilingual(): languages = self.getAttribute(obj, 'languages')
return Field.valueIsInRequest(self, request, name) if len(languages) == 1:
return request.has_key('%s_%s' % (name, self.languages[0])) return Field.valueIsInRequest(self, obj, request, name)
# Is is sufficient to check that at least one of the language-specific
# values is in the request.
return request.has_key('%s_%s' % (name, languages[0]))
def getRequestValue(self, request, requestName=None): def getRequestValue(self, obj, requestName=None):
'''The request value may be multilingual.''' '''The request value may be multilingual.'''
request = obj.REQUEST
name = requestName or self.name name = requestName or self.name
languages = self.getAttribute(obj, 'languages')
# A unilingual field. # A unilingual field.
if not self.isMultilingual(): return request.get(name, None) if len(languages) == 1: return request.get(name, None)
# A multilingual field. # A multilingual field.
res = {} res = {}
for language in self.languages: for language in languages:
res[language] = request.get('%s_%s' % (name, language), None) res[language] = request.get('%s_%s' % (name, language), None)
return res return res
def isEmptyValue(self, value, obj=None): def isEmptyValue(self, obj, value):
'''Returns True if the p_value must be considered as an empty value.''' '''Returns True if the p_value must be considered as an empty value.'''
if not self.isMultilingual(): if not self.isMultilingual(obj):
return Field.isEmptyValue(self, value, obj) return Field.isEmptyValue(self, obj, value)
# For a multilingual value, as soon as a value is not empty for a given # For a multilingual value, as soon as a value is not empty for a given
# language, the whole value is considered as not being empty. # language, the whole value is considered as not being empty.
if not value: return True if not value: return True
for v in value.itervalues(): for v in value.itervalues():
if not Field.isEmptyValue(self, v, obj): return if not Field.isEmptyValue(self, obj, v): return
return True return True
def isCompleteValue(self, value, obj=None): def isCompleteValue(self, obj, value):
'''Returns True if the p_value must be considered as complete. For a '''Returns True if the p_value must be considered as complete. For a
unilingual field, being complete simply means not being empty. For a unilingual field, being complete simply means not being empty. For a
multilingual field, being complete means that a value is present for multilingual field, being complete means that a value is present for
every language''' every language.'''
if not self.isMultilingual(): if not self.isMultilingual(obj):
return Field.isCompleteValue(self, value, obj) return Field.isCompleteValue(self, obj, value)
# As soon as a given language value is empty, the global value is not # As soon as a given language value is empty, the global value is not
# complete. # complete.
if not value: return True if not value: return True
for v in value.itervalues(): for v in value.itervalues():
if Field.isEmptyValue(self, v, obj): return if Field.isEmptyValue(self, obj, v): return
return True return True
def getDiffValue(self, obj, value, language): def getDiffValue(self, obj, value, language):
@ -581,7 +592,7 @@ class String(Field):
m_getFormattedValue for getting a non-multilingual value (ie, in m_getFormattedValue for getting a non-multilingual value (ie, in
most cases). Else, this method returns a formatted value for the most cases). Else, this method returns a formatted value for the
p_language-specific part of a multilingual value.''' p_language-specific part of a multilingual value.'''
if Field.isEmptyValue(self, value): return '' if Field.isEmptyValue(self, obj, value): return ''
res = value res = value
if self.isSelect: if self.isSelect:
if isinstance(self.validator, Selection): if isinstance(self.validator, Selection):
@ -609,13 +620,14 @@ class String(Field):
return res return res
def getFormattedValue(self, obj, value, showChanges=False): def getFormattedValue(self, obj, value, showChanges=False):
if not self.isMultilingual(): languages = self.getAttribute(obj, 'languages')
if len(languages) == 1:
return self.getUnilingualFormattedValue(obj, value, showChanges) return self.getUnilingualFormattedValue(obj, value, showChanges)
# Return the dict of values whose individual, language-specific values # Return the dict of values whose individual, language-specific values
# have been formatted via m_getUnilingualFormattedValue. # have been formatted via m_getUnilingualFormattedValue.
if not value: return value if not value: return value
res = {} res = {}
for lg in self.languages: for lg in languages:
res[lg] = self.getUnilingualFormattedValue(obj, value[lg], res[lg] = self.getUnilingualFormattedValue(obj, value[lg],
showChanges, lg) showChanges, lg)
return res return res
@ -631,7 +643,7 @@ class String(Field):
'''Pure text must be extracted from rich content; multilingual content '''Pure text must be extracted from rich content; multilingual content
must be concatenated.''' must be concatenated.'''
isXhtml = self.format == String.XHTML isXhtml = self.format == String.XHTML
if self.isMultilingual(): if self.isMultilingual(obj):
res = self.getValue(obj) res = self.getValue(obj)
if res: if res:
vals = [] vals = []
@ -775,9 +787,9 @@ class String(Field):
elif self.transform == 'capitalize': return value.capitalize() elif self.transform == 'capitalize': return value.capitalize()
return value return value
def getUnilingualStorableValue(self, value): def getUnilingualStorableValue(self, obj, value):
isString = isinstance(value, basestring) isString = isinstance(value, basestring)
isEmpty = Field.isEmptyValue(self, value) isEmpty = Field.isEmptyValue(self, obj, value)
# Apply transform if required # Apply transform if required
if isString and not isEmpty and (self.transform != 'none'): if isString and not isEmpty and (self.transform != 'none'):
value = self.applyTransform(value) value = self.applyTransform(value)
@ -804,21 +816,23 @@ class String(Field):
value = [value] value = [value]
return value return value
def getStorableValue(self, value): def getStorableValue(self, obj, value):
if not self.isMultilingual(): languages = self.getAttribute(obj, 'languages')
return self.getUnilingualStorableValue(value) if len(languages) == 1:
return self.getUnilingualStorableValue(obj, value)
# A multilingual value is stored as a dict whose keys are ISO 2-letters # A multilingual value is stored as a dict whose keys are ISO 2-letters
# language codes and whose values are strings storing content in the # language codes and whose values are strings storing content in the
# language ~{s_language: s_content}~. # language ~{s_language: s_content}~.
if not value: return if not value: return
for lg in self.languages: for lg in languages:
value[lg] = self.getUnilingualStorableValue(value[lg]) value[lg] = self.getUnilingualStorableValue(obj, value[lg])
return value return value
def store(self, obj, value): def store(self, obj, value):
'''Stores p_value on p_obj for this field.''' '''Stores p_value on p_obj for this field.'''
if self.isMultilingual() and value and \ languages = self.getAttribute(obj, 'languages')
(not isinstance(value, dict) or (len(value) != len(self.languages))): if (len(languages) > 1) and value and \
(not isinstance(value, dict) or (len(value) != len(languages))):
raise Exception('Multilingual field "%s" accepts a dict whose '\ raise Exception('Multilingual field "%s" accepts a dict whose '\
'keys are in field.languages and whose ' \ 'keys are in field.languages and whose ' \
'values are strings.' % self.name) 'values are strings.' % self.name)
@ -832,16 +846,16 @@ class String(Field):
requestValue = rq['fieldContent'] requestValue = rq['fieldContent']
# Remember previous value if the field is historized. # Remember previous value if the field is historized.
previousData = obj.rememberPreviousData([self]) previousData = obj.rememberPreviousData([self])
if self.isMultilingual(): if self.isMultilingual(obj):
# We take a copy of previousData because it is mutable (dict). # We take a copy of previousData because it is mutable (dict).
previousData[self.name] = previousData[self.name].copy() previousData[self.name] = previousData[self.name].copy()
# We get a partial value, for one language only. # We get a partial value, for one language only.
language = rq['languageOnly'] language = rq['languageOnly']
v = self.getUnilingualStorableValue(requestValue) v = self.getUnilingualStorableValue(obj, requestValue)
getattr(obj.aq_base, self.name)[language] = v getattr(obj.aq_base, self.name)[language] = v
part = ' (%s)' % language part = ' (%s)' % language
else: else:
self.store(obj, self.getStorableValue(requestValue)) self.store(obj, self.getStorableValue(obj, requestValue))
part = '' part = ''
# Update the object history when relevant # Update the object history when relevant
if previousData: obj.historizeData(previousData) if previousData: obj.historizeData(previousData)
@ -892,11 +906,12 @@ class String(Field):
'fi': 'fi_FI', 'fr': 'fr_FR', 'de': 'de_DE', 'el': 'el_GR', 'fi': 'fi_FI', 'fr': 'fr_FR', 'de': 'de_DE', 'el': 'el_GR',
'it': 'it_IT', 'nb': 'nb_NO', 'pt': 'pt_PT', 'es': 'es_ES', 'it': 'it_IT', 'nb': 'nb_NO', 'pt': 'pt_PT', 'es': 'es_ES',
'sv': 'sv_SE'} 'sv': 'sv_SE'}
def getCkLanguage(self, language): def getCkLanguage(self, obj, language):
'''Gets the language for CK editor SCAYT. p_language is one of '''Gets the language for CK editor SCAYT. p_language is one of
self.languages if the field is multilingual, None else. If p_language self.languages if the field is multilingual, None else. If p_language
is not supported by CK, we use english.''' is not supported by CK, we use english.'''
if not language: language = self.languages[0] if not language:
language = self.getAttribute(obj, 'languages')[0]
if language in self.ckLanguages: return self.ckLanguages[language] if language in self.ckLanguages: return self.ckLanguages[language]
return 'en_US' return 'en_US'
@ -904,7 +919,7 @@ class String(Field):
'''Gets the base params to set on a rich text field.''' '''Gets the base params to set on a rich text field.'''
ckAttrs = {'toolbar': 'Appy', ckAttrs = {'toolbar': 'Appy',
'format_tags': ';'.join(self.styles), 'format_tags': ';'.join(self.styles),
'scayt_sLang': self.getCkLanguage(language)} 'scayt_sLang': self.getCkLanguage(obj, language)}
if self.width: ckAttrs['width'] = self.width if self.width: ckAttrs['width'] = self.width
if self.spellcheck: ckAttrs['scayt_autoStartup'] = True if self.spellcheck: ckAttrs['scayt_autoStartup'] = True
if self.allowImageUpload: if self.allowImageUpload:

View file

@ -1246,12 +1246,27 @@ class ToolMixin(BaseMixin):
return [f for f in self.getAllAppyTypes(contentType) \ return [f for f in self.getAllAppyTypes(contentType) \
if (f.type == 'Pod') and (f.show == 'result')] if (f.type == 'Pod') and (f.show == 'result')]
def formatDate(self, date, withHour=True): def formatDate(self, date, format=None, withHour=True, language=None):
'''Returns p_date formatted as specified by tool.dateFormat. '''Returns p_date formatted as specified by p_format, or tool.dateFormat
If p_withHour is True, hour is appended, with a format specified if not specified. If p_withHour is True, hour is appended, with a
in tool.hourFormat.''' format specified in tool.hourFormat.'''
tool = self.appy() tool = self.appy()
res = date.strftime(tool.dateFormat) fmt = format or tool.dateFormat
# Resolve appy-specific formatting symbols used for getting translated
# names of days or months:
# - %dt: translated name of day
# - %DT: translated name of day, capitalized
# - %mt: translated name of month
# - %MT: translated name of month, capitalized
if ('%dt' in fmt) or ('%DT' in fmt):
day = self.translate('day_%s' % date._aday, language=language)
fmt = fmt.replace('%dt', day.lower()).replace('%DT', day)
if ('%mt' in fmt) or ('%MT' in fmt):
month = self.translate('month_%s' % date._amon, language=language)
fmt = fmt.replace('%mt', month.lower()).replace('%MT', month)
# Resolve all other, standard, symbols
res = date.strftime(fmt)
# Append hour from tool.hourFormat
if withHour: res += ' (%s)' % date.strftime(tool.hourFormat) if withHour: res += ' (%s)' % date.strftime(tool.hourFormat)
return res return res

View file

@ -337,12 +337,12 @@ class BaseMixin:
rq = self.REQUEST rq = self.REQUEST
for field in self.getAppyTypes('edit', rq.form.get('page')): for field in self.getAppyTypes('edit', rq.form.get('page')):
if not field.validable or not field.isClientVisible(self): continue if not field.validable or not field.isClientVisible(self): continue
value = field.getRequestValue(rq) value = field.getRequestValue(self)
message = field.validate(self, value) message = field.validate(self, value)
if message: if message:
setattr(errors, field.name, message) setattr(errors, field.name, message)
else: else:
setattr(values, field.name, field.getStorableValue(value)) setattr(values, field.name, field.getStorableValue(self, value))
# Validate sub-fields within Lists # Validate sub-fields within Lists
if field.type != 'List': continue if field.type != 'List': continue
i = -1 i = -1
@ -634,7 +634,7 @@ class BaseMixin:
if lg: if lg:
isEmpty = not changes[name] or not changes[name].get(lg) isEmpty = not changes[name] or not changes[name].get(lg)
else: else:
isEmpty = field.isEmptyValue(changes[name], self) isEmpty = field.isEmptyValue(self, changes[name])
if isEmpty: if isEmpty:
del changes[name] del changes[name]
else: else:
@ -664,14 +664,17 @@ class BaseMixin:
# In some cases the old value must be formatted. # In some cases the old value must be formatted.
if field.type == 'Ref': if field.type == 'Ref':
previousData[name] = [r.title for r in previousData[name]] previousData[name] = [r.title for r in previousData[name]]
elif (field.type == 'String') and field.isMultilingual(): elif field.type == 'String':
# Consider every language-specific value as a first-class value languages = field.getAttribute(self, 'languages')
del previousData[name] if len(languages) > 1:
for lg in field.languages: # Consider every language-specific value as a first-class
lgPrev = prev and prev.get(lg) or None # value.
lgCurr = curr and curr.get(lg) or None del previousData[name]
if lgPrev == lgCurr: continue for lg in languages:
previousData['%s-%s' % (name, lg)] = lgPrev lgPrev = prev and prev.get(lg) or None
lgCurr = curr and curr.get(lg) or None
if lgPrev == lgCurr: continue
previousData['%s-%s' % (name, lg)] = lgPrev
if previousData: if previousData:
self.addDataChange(previousData) self.addDataChange(previousData)
@ -714,7 +717,7 @@ class BaseMixin:
'''Gets the value of field p_name as may be present in the request.''' '''Gets the value of field p_name as may be present in the request.'''
# Return the request value for standard fields. # Return the request value for standard fields.
if '*' not in name: if '*' not in name:
return self.getAppyType(name).getRequestValue(self.REQUEST) return self.getAppyType(name).getRequestValue(self)
# For sub-fields within Lists, the corresponding request values have # For sub-fields within Lists, the corresponding request values have
# already been computed in the request key corresponding to the whole # already been computed in the request key corresponding to the whole
# List. # List.
@ -1083,7 +1086,11 @@ class BaseMixin:
return True return True
else: else:
field = self.getAppyType(name) field = self.getAppyType(name)
multilingual = (field.type == 'String') and field.isMultilingual() # Is this field a multilingual field ?
languages = None
if field.type == 'String':
languages = field.getAttribute(self, 'languages')
multilingual = len(languages) > 1
for event in history: for event in history:
if event['action'] != '_datachange_': continue if event['action'] != '_datachange_': continue
# Is there a value present for this field in this data change? # Is there a value present for this field in this data change?
@ -1093,7 +1100,7 @@ class BaseMixin:
return True return True
else: else:
# At least one language-specific value must be present # At least one language-specific value must be present
for lg in field.languages: for lg in languages:
lgName = '%s-%s' % (field.name, lg) lgName = '%s-%s' % (field.name, lg)
if (lgName in event['changes']) and \ if (lgName in event['changes']) and \
event['changes'][lgName][0]: event['changes'][lgName][0]:
@ -1142,7 +1149,7 @@ class BaseMixin:
# previous value, we propose a diff with the next # previous value, we propose a diff with the next
# version, excepted if the previous value is empty. # version, excepted if the previous value is empty.
if lg: isEmpty = not oldValue[0] if lg: isEmpty = not oldValue[0]
else: isEmpty = field.isEmptyValue(oldValue[0]) else: isEmpty = field.isEmptyValue(self, oldValue[0])
if isEmpty: if isEmpty:
val = '-' val = '-'
else: else:

View file

@ -627,15 +627,6 @@ class ToolWrapper(AbstractWrapper):
(user.o.absolute_url(), user.title,access)) (user.o.absolute_url(), user.title,access))
return res + '\n'.join(rows) + '</table>' return res + '\n'.join(rows) + '</table>'
def getInitiator(self, field=False):
'''Retrieves the object that triggered the creation of the object
being currently created (if any), or the name of the field in this
object if p_field is given.'''
nav = self.o.REQUEST.get('nav', '')
if not nav or not nav.startswith('ref.'): return
if not field: return self.getObject(nav.split('.')[1])
return nav.split('.')[2].split(':')[0]
def getObject(self, uid): def getObject(self, uid):
'''Allow to retrieve an object from its unique identifier p_uid.''' '''Allow to retrieve an object from its unique identifier p_uid.'''
return self.o.getObject(uid, appy=True) return self.o.getObject(uid, appy=True)
@ -666,10 +657,10 @@ class ToolWrapper(AbstractWrapper):
'''Sends a mail. See doc for appy.gen.mail.sendMail.''' '''Sends a mail. See doc for appy.gen.mail.sendMail.'''
sendMail(self, to, subject, body, attachments=attachments) sendMail(self, to, subject, body, attachments=attachments)
def formatDate(self, date, withHour=True): def formatDate(self, date, format=None, withHour=True, language=None):
'''Check doc @ToolMixin::formatDate.''' '''Check doc @ToolMixin::formatDate.'''
if not date: return if not date: return
return self.o.formatDate(date, withHour=withHour) return self.o.formatDate(date, format, withHour, language)
def getUserName(self, login=None, normalized=False): def getUserName(self, login=None, normalized=False):
return self.o.getUserName(login=login, normalized=normalized) return self.o.getUserName(login=login, normalized=normalized)

View file

@ -43,10 +43,11 @@ class TranslationWrapper(AbstractWrapper):
if field.type == 'Computed': name = field.name[:-6] if field.type == 'Computed': name = field.name[:-6]
else: name = field.name else: name = field.name
# Get the source message # Get the source message
sourceLanguage = self.o.getProductConfig(True).sourceLanguage obj = self.o
sourceLanguage = obj.getProductConfig(True).sourceLanguage
sourceTranslation = getattr(tool.o, sourceLanguage).appy() sourceTranslation = getattr(tool.o, sourceLanguage).appy()
sourceMsg = getattr(sourceTranslation, name) sourceMsg = getattr(sourceTranslation, name)
if field.isEmptyValue(sourceMsg): return False if field.isEmptyValue(obj, sourceMsg): return
return True return True
poReplacements = ( ('\r\n', '<br/>'), ('\n', '<br/>'), ('"', '\\"') ) poReplacements = ( ('\r\n', '<br/>'), ('\n', '<br/>'), ('"', '\\"') )

View file

@ -788,7 +788,7 @@ class AbstractWrapper(object):
obj = self.o obj = self.o
if hasattr(obj.aq_base, name): if hasattr(obj.aq_base, name):
field = obj.getAppyType(name) field = obj.getAppyType(name)
return field.isEmptyValue(getattr(obj, name)) return field.isEmptyValue(obj, getattr(obj, name))
return True return True
def isTemp(self): def isTemp(self):

View file

@ -72,6 +72,17 @@ def cleanFolder(folder, exts=extsToClean, folders=(), verbose=False):
if verbose: print('Removing folder %s...' % toDelete) if verbose: print('Removing folder %s...' % toDelete)
FolderDeleter.delete(toDelete) FolderDeleter.delete(toDelete)
# ------------------------------------------------------------------------------
def resolvePath(path):
'''p_path is a file path that can contain occurences of "." and "..". This
function resolves them and procuces a minimal path.'''
res = []
for elem in path.split(os.sep):
if elem == '.': pass
elif elem == '..': res.pop()
else: res.append(elem)
return os.sep.join(res)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
def copyFolder(source, dest, cleanDest=False): def copyFolder(source, dest, cleanDest=False):
'''Copies the content of folder p_source to folder p_dest. p_dest is '''Copies the content of folder p_source to folder p_dest. p_dest is