[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 \
field.getFormattedValue(zobj, rawValue, showChanges);
requestValue=not isSearch and zobj.getRequestFieldValue(name);
inRequest=field.valueIsInRequest(req, name);
inRequest=field.valueIsInRequest(zobj, req, name);
error=req.get('%s_error' % name);
isMultiple=(field.multiplicity[1] == None) or \
(field.multiplicity[1] > 1);
@ -360,7 +360,7 @@ class Field:
else:
master, masterValue = masterData
if masterValue and callable(masterValue): return True
reqValue = master.getRequestValue(obj.REQUEST)
reqValue = master.getRequestValue(obj)
# reqValue can be a list or not
if type(reqValue) not in sutils.sequenceTypes:
return reqValue in masterValue
@ -517,7 +517,7 @@ class Field:
def getValue(self, obj):
'''Gets, on_obj, the value conforming to self's type definition.'''
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
# self.default, of self.default() if it is a method.
if callable(self.default):
@ -539,7 +539,7 @@ class Field:
purposes. Needs to be overridden by some child classes. If
p_showChanges is True, the result must also include the changes that
occurred on p_value across the ages.'''
if self.isEmptyValue(value): return ''
if self.isEmptyValue(obj, value): return ''
return value
def getIndexType(self):
@ -573,7 +573,7 @@ class Field:
res = str(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
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
@ -581,7 +581,7 @@ class Field:
for string multilingual fields.'''
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
simplest cases, the request value is a single value whose name in the
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
searching into the request, instead of the field name (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
m_getRequestValue. So, it is a valid string (or list of strings)
representation of the field value coming from the request.
This method computes the real (potentially converted or manipulated
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
def getMasterData(self):
@ -634,11 +634,11 @@ class Field:
return 'updateSlaves(this,null,%s,%s,null,null%s)' % \
(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.'''
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,
in most cases, a "complete" value simply means a "non empty" value
(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
if the "day, month, year" part is present but will not be considered
as complete without the "hour, minute" part.'''
return not self.isEmptyValue(value, obj)
return not self.isEmptyValue(obj, value)
def validateValue(self, obj, value):
'''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
error message is returned.'''
# 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 the field is required, but not visible according to
# master/slave relationships, we consider it not to be required.
@ -686,7 +686,7 @@ class Field:
message = self.validateValue(obj, value)
if message: return message
# 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):
obj = obj.appy()
if type(self.validator) != self.validatorTypes[-1]:

View file

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

View file

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

View file

@ -327,16 +327,15 @@ class File(Field):
historized, mapping, label, sdefault, scolspan, swidth,
sheight, True)
def getRequestValue(self, request, requestName=None):
def getRequestValue(self, obj, requestName=None):
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 isEmptyValue(self, value, obj=None):
def isEmptyValue(self, obj, value):
'''Must p_value be considered as empty?'''
if not obj: return Field.isEmptyValue(self, value)
if value: return False
if value: return
# If "nochange", the value must not be considered as empty
return obj.REQUEST.get('%s_delete' % self.name) != 'nochange'

View file

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

View file

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

View file

@ -119,8 +119,9 @@ class List(Field):
res.append(elem)
return res
def getRequestValue(self, request, requestName=None):
def getRequestValue(self, obj, requestName=None):
'''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 (?)
prefix = name + '*' + self.fields[0][0] + '*'
res = {}
@ -133,7 +134,7 @@ class List(Field):
if rowIndex == -1: continue # Ignore the template row.
for subName, subField in self.fields:
keyName = '%s*%s*%s' % (name, subName, rowIndex)
v = subField.getRequestValue(request, requestName=keyName)
v = subField.getRequestValue(obj, requestName=keyName)
setattr(row, subName, v)
res[rowIndex] = row
# Produce a sorted list.
@ -148,7 +149,7 @@ class List(Field):
request.set(name, 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.'''
res = []
for v in value:
@ -156,11 +157,11 @@ class List(Field):
for name, field in self.fields:
subValue = getattr(v, name)
try:
setattr(sv, name, field.getStorableValue(subValue))
setattr(sv, name, field.getStorableValue(obj, subValue))
except ValueError:
# The value for this field for this specific row is
# 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
# will have sub-field specific validation that will also
# detect the error and will prevent storing the wrong value

View file

@ -295,7 +295,7 @@ class Pod(Field):
tool = obj.tool
diskFolder = tool.getDiskFolder()
# 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):
raise Exception(self.TEMPLATE_NOT_FOUND % templatePath)
# Get or compute the specific POD context

View file

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

View file

@ -1246,12 +1246,27 @@ class ToolMixin(BaseMixin):
return [f for f in self.getAllAppyTypes(contentType) \
if (f.type == 'Pod') and (f.show == 'result')]
def formatDate(self, date, withHour=True):
'''Returns p_date formatted as specified by tool.dateFormat.
If p_withHour is True, hour is appended, with a format specified
in tool.hourFormat.'''
def formatDate(self, date, format=None, withHour=True, language=None):
'''Returns p_date formatted as specified by p_format, or tool.dateFormat
if not specified. If p_withHour is True, hour is appended, with a
format specified in tool.hourFormat.'''
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)
return res

View file

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

View file

@ -627,15 +627,6 @@ class ToolWrapper(AbstractWrapper):
(user.o.absolute_url(), user.title,access))
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):
'''Allow to retrieve an object from its unique identifier p_uid.'''
return self.o.getObject(uid, appy=True)
@ -666,10 +657,10 @@ class ToolWrapper(AbstractWrapper):
'''Sends a mail. See doc for appy.gen.mail.sendMail.'''
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.'''
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):
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]
else: name = field.name
# Get the source message
sourceLanguage = self.o.getProductConfig(True).sourceLanguage
obj = self.o
sourceLanguage = obj.getProductConfig(True).sourceLanguage
sourceTranslation = getattr(tool.o, sourceLanguage).appy()
sourceMsg = getattr(sourceTranslation, name)
if field.isEmptyValue(sourceMsg): return False
if field.isEmptyValue(obj, sourceMsg): return
return True
poReplacements = ( ('\r\n', '<br/>'), ('\n', '<br/>'), ('"', '\\"') )

View file

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

View file

@ -72,6 +72,17 @@ def cleanFolder(folder, exts=extsToClean, folders=(), verbose=False):
if verbose: print('Removing folder %s...' % 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):
'''Copies the content of folder p_source to folder p_dest. p_dest is