[gen] More work on string multilingual fields.
This commit is contained in:
parent
6770d23a50
commit
4131ba899e
|
@ -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=req.has_key(name);
|
inRequest=field.valueIsInRequest(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);
|
||||||
|
@ -544,6 +544,14 @@ class Field:
|
||||||
return res
|
return res
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def valueIsInRequest(self, 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
|
||||||
|
request is sufficient. But in some cases it may be more complex, ie
|
||||||
|
for string multilingual fields.'''
|
||||||
|
return request.has_key(name)
|
||||||
|
|
||||||
def getRequestValue(self, request, requestName=None):
|
def getRequestValue(self, request, 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
|
||||||
|
@ -601,6 +609,18 @@ class Field:
|
||||||
'''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):
|
||||||
|
'''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.
|
||||||
|
For example, a multilingual string value is not empty as soon as a
|
||||||
|
value is given for some language but will not be considered as
|
||||||
|
complete while a value is missing for some language. Another example:
|
||||||
|
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)
|
||||||
|
|
||||||
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
|
||||||
the right moment by m_validate defined below for triggering
|
the right moment by m_validate defined below for triggering
|
||||||
|
@ -623,8 +643,8 @@ class Field:
|
||||||
m_getRequestValue defined above, is valid according to this type
|
m_getRequestValue defined above, is valid according to this type
|
||||||
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.'''
|
||||||
# Check that a value is given if required.
|
# If the value is required, check that a (complete) value is present.
|
||||||
if self.isEmptyValue(value, obj):
|
if not self.isCompleteValue(value, obj):
|
||||||
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.
|
||||||
|
|
|
@ -35,6 +35,7 @@ class Boolean(Field):
|
||||||
cLayouts = {'view': 'lf|', 'edit': 'flrv|'}
|
cLayouts = {'view': 'lf|', 'edit': 'flrv|'}
|
||||||
# Layout for radio buttons (render = "radios")
|
# Layout for radio buttons (render = "radios")
|
||||||
rLayouts = {'edit': 'f', 'view': 'f', 'search': 'l-f'}
|
rLayouts = {'edit': 'f', 'view': 'f', 'search': 'l-f'}
|
||||||
|
rlLayouts = {'edit': 'l-f', 'view': 'lf', 'search': 'l-f'}
|
||||||
|
|
||||||
pxView = pxCell = Px('''<x>:value</x>
|
pxView = pxCell = Px('''<x>:value</x>
|
||||||
<input type="hidden" if="masterCss"
|
<input type="hidden" if="masterCss"
|
||||||
|
|
262
fields/string.py
262
fields/string.py
|
@ -77,6 +77,13 @@ class String(Field):
|
||||||
URL = c('(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*(\.[a-z]{2,5})?' \
|
URL = c('(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*(\.[a-z]{2,5})?' \
|
||||||
'(([0-9]{1,5})?\/.*)?')
|
'(([0-9]{1,5})?\/.*)?')
|
||||||
|
|
||||||
|
# Possible values for "format"
|
||||||
|
LINE = 0
|
||||||
|
TEXT = 1
|
||||||
|
XHTML = 2
|
||||||
|
PASSWORD = 3
|
||||||
|
CAPTCHA = 4
|
||||||
|
|
||||||
pxView = Px('''
|
pxView = Px('''
|
||||||
<x var="fmt=field.format; isUrl=field.isUrl;
|
<x var="fmt=field.format; isUrl=field.isUrl;
|
||||||
mayAjaxEdit=not showChanges and field.inlineEdit and \
|
mayAjaxEdit=not showChanges and field.inlineEdit and \
|
||||||
|
@ -101,16 +108,45 @@ class String(Field):
|
||||||
<div if="not mayAjaxEdit" class="xhtml">::value or '-'</div>
|
<div if="not mayAjaxEdit" class="xhtml">::value or '-'</div>
|
||||||
<div if="mayAjaxEdit" class="xhtml" contenteditable="true"
|
<div if="mayAjaxEdit" class="xhtml" contenteditable="true"
|
||||||
id=":'%s_%s_ck' % (zobj.id, name)">::value or '-'</div>
|
id=":'%s_%s_ck' % (zobj.id, name)">::value or '-'</div>
|
||||||
<script if="mayAjaxEdit">::field.getJsInlineInit(zobj)</script>
|
<script if="mayAjaxEdit">::field.getJsInlineInit(zobj,None)</script>
|
||||||
</x>
|
</x>
|
||||||
<span if="not value and (fmt != 2)" class="smaller">-</span>
|
<span if="not value and (fmt != 2)" class="smaller">-</span>
|
||||||
<input type="hidden" if="masterCss" class=":masterCss" value=":rawValue"
|
<input type="hidden" if="masterCss" class=":masterCss" value=":rawValue"
|
||||||
name=":name" id=":name"/>
|
name=":name" id=":name"/>
|
||||||
</x>''')
|
</x>''')
|
||||||
|
|
||||||
|
# pxEdit part for formats String.LINE (but that are not selections),
|
||||||
|
# String.PASSWORD and String.CAPTCHA.
|
||||||
|
pxEditText = Px('''
|
||||||
|
<input var="inputId=not lg and name or '%s_%s' % (name, lg);
|
||||||
|
placeholder=field.getAttribute(obj, 'placeholder') or ''"
|
||||||
|
id=":inputId" name=":inputId" size=":field.width"
|
||||||
|
maxlength=":field.maxChars" placeholder=":placeholder"
|
||||||
|
value=":inRequest and requestValue or value"
|
||||||
|
style=":'text-transform:%s' % field.transform"
|
||||||
|
type=":(fmt == 3) and 'password' or 'text'"/>
|
||||||
|
<!-- Display a captcha if required -->
|
||||||
|
<span if="fmt == 4">:_('captcha_text', \
|
||||||
|
mapping=field.getCaptchaChallenge(req.SESSION))
|
||||||
|
</span>''')
|
||||||
|
|
||||||
|
# pxEdit part for formats String.TEXT and String.XHTML.
|
||||||
|
pxEditTextArea = Px('''
|
||||||
|
<textarea var="inputId=not lg and name or '%s_%s' % (name, lg)"
|
||||||
|
id=":inputId" name=":inputId" cols=":field.width"
|
||||||
|
class=":(fmt == 2) and ('rich_%s' % name) or ''"
|
||||||
|
style=":'text-transform:%s' % field.transform"
|
||||||
|
rows=":field.height">:inRequest and requestValue or value
|
||||||
|
</textarea>
|
||||||
|
<script if="fmt == 2"
|
||||||
|
type="text/javascript">::field.getJsInit(zobj, lg)</script>''')
|
||||||
|
|
||||||
|
# Mapping formats -> PXs defining the edit widgets.
|
||||||
|
editPx = {LINE:pxEditText, TEXT:pxEditTextArea, XHTML:pxEditTextArea,
|
||||||
|
PASSWORD:pxEditText, CAPTCHA:pxEditText}
|
||||||
pxEdit = Px('''
|
pxEdit = Px('''
|
||||||
<x var="fmt=field.format;
|
<x var="fmt=field.format;
|
||||||
isOneLine=fmt in (0,3,4)">
|
multilingual=field.isMultilingual()">
|
||||||
<select if="field.isSelect"
|
<select if="field.isSelect"
|
||||||
var2="possibleValues=field.getPossibleValues(zobj, \
|
var2="possibleValues=field.getPossibleValues(zobj, \
|
||||||
withTranslations=True, withBlankValue=True)"
|
withTranslations=True, withBlankValue=True)"
|
||||||
|
@ -122,28 +158,22 @@ class String(Field):
|
||||||
selected=":field.isSelected(zobj, name, val[0], rawValue)"
|
selected=":field.isSelected(zobj, name, val[0], rawValue)"
|
||||||
title=":val[1]">:ztool.truncateValue(val[1],field.width)</option>
|
title=":val[1]">:ztool.truncateValue(val[1],field.width)</option>
|
||||||
</select>
|
</select>
|
||||||
<x if="isOneLine and not field.isSelect"
|
<!-- Any other unilingual field. -->
|
||||||
var2="placeholder=field.getAttribute(obj, 'placeholder') or ''">
|
<x if="not field.isSelect and not multilingual"
|
||||||
<input id=":name" name=":name" size=":field.width"
|
var2="lg=None">:field.editPx[fmt]</x>
|
||||||
maxlength=":field.maxChars" placeholder=":placeholder"
|
<!-- Any other multilingual field. -->
|
||||||
value=":inRequest and requestValue or value"
|
<table if="not field.isSelect and multilingual" width="100%">
|
||||||
style=":'text-transform:%s' % field.transform"
|
<tr valign="top">
|
||||||
type=":(fmt == 3) and 'password' or 'text'"/>
|
<x for="lg in field.languages">
|
||||||
<!-- Display a captcha if required -->
|
<td style=":(fmt==2) and 'padding-top:1px' or 'padding-top:3px'"
|
||||||
<span if="fmt == 4">:_('captcha_text', \
|
width="25px">
|
||||||
mapping=field.getCaptchaChallenge(req.SESSION))
|
<span class="language help"
|
||||||
</span>
|
title=":ztool.getLanguageName(lg)">:lg.upper()</span></td>
|
||||||
</x>
|
<td var="requestValue=requestValue[lg];
|
||||||
<x if="fmt in (1,2)">
|
value=value[lg]|''">:field.editPx[fmt]</td>
|
||||||
<textarea id=":name" name=":name" cols=":field.width"
|
</x>
|
||||||
class=":(fmt == 2) and ('rich_%s' % name) or ''"
|
</tr>
|
||||||
style=":'text-transform:%s' % field.transform"
|
</table></x>''')
|
||||||
rows=":field.height">:inRequest and requestValue or value
|
|
||||||
</textarea>
|
|
||||||
<script if="fmt == 2"
|
|
||||||
type="text/javascript">::field.getJsInit(zobj)</script>
|
|
||||||
</x>
|
|
||||||
</x>''')
|
|
||||||
|
|
||||||
pxCell = Px('''
|
pxCell = Px('''
|
||||||
<x var="multipleValues=value and isMultiple">
|
<x var="multipleValues=value and isMultiple">
|
||||||
|
@ -285,12 +315,6 @@ class String(Field):
|
||||||
if not alpha.match(c): return False
|
if not alpha.match(c): return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Possible values for "format"
|
|
||||||
LINE = 0
|
|
||||||
TEXT = 1
|
|
||||||
XHTML = 2
|
|
||||||
PASSWORD = 3
|
|
||||||
CAPTCHA = 4
|
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
def __init__(self, validator=None, multiplicity=(0,1), default=None,
|
||||||
format=LINE, show=True, page='main', group=None, layouts=None,
|
format=LINE, show=True, page='main', group=None, layouts=None,
|
||||||
move=0, indexed=False, searchable=False,
|
move=0, indexed=False, searchable=False,
|
||||||
|
@ -300,7 +324,7 @@ class String(Field):
|
||||||
label=None, sdefault='', scolspan=1, swidth=None, sheight=None,
|
label=None, sdefault='', scolspan=1, swidth=None, sheight=None,
|
||||||
persist=True, transform='none', placeholder=None,
|
persist=True, transform='none', placeholder=None,
|
||||||
styles=('p','h1','h2','h3','h4'), allowImageUpload=True,
|
styles=('p','h1','h2','h3','h4'), allowImageUpload=True,
|
||||||
spellcheck=False, contentLanguage=None, inlineEdit=False):
|
spellcheck=False, languages=('en',), inlineEdit=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
|
||||||
|
@ -314,8 +338,11 @@ class String(Field):
|
||||||
self.allowImageUpload = allowImageUpload
|
self.allowImageUpload = allowImageUpload
|
||||||
# When format is XHTML, do we run the CK spellchecker ?
|
# When format is XHTML, do we run the CK spellchecker ?
|
||||||
self.spellcheck = spellcheck
|
self.spellcheck = spellcheck
|
||||||
# What is the language of field content?
|
# If "languages" holds more than one language, the field will be
|
||||||
self.contentLanguage = contentLanguage
|
# multi-lingual and several widgets will allow to edit/visualize the
|
||||||
|
# field content in all the supported languages. The field is also used
|
||||||
|
# by the CK spell checker.
|
||||||
|
self.languages = languages
|
||||||
# When format in XHTML, can the field be inline-edited (ckeditor)?
|
# When format in XHTML, can the field be inline-edited (ckeditor)?
|
||||||
self.inlineEdit = inlineEdit
|
self.inlineEdit = inlineEdit
|
||||||
# The following field has a direct impact on the text entered by the
|
# The following field has a direct impact on the text entered by the
|
||||||
|
@ -361,6 +388,12 @@ class String(Field):
|
||||||
not self.isSelect
|
not self.isSelect
|
||||||
self.swidth = self.swidth or self.width
|
self.swidth = self.swidth or self.width
|
||||||
self.sheight = self.sheight or self.height
|
self.sheight = self.sheight or self.height
|
||||||
|
self.checkParameters()
|
||||||
|
|
||||||
|
def checkParameters(self):
|
||||||
|
'''Ensures this String is correctly defined.'''
|
||||||
|
if self.isSelect and self.isMultilingual():
|
||||||
|
raise Exception("A selection field can't be multilingual.")
|
||||||
|
|
||||||
def isSelection(self):
|
def isSelection(self):
|
||||||
'''Does the validator of this type definition define a list of values
|
'''Does the validator of this type definition define a list of values
|
||||||
|
@ -376,6 +409,8 @@ class String(Field):
|
||||||
res = False
|
res = False
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def isMultilingual(self): return len(self.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
|
||||||
acccording to format, multiplicity or history.'''
|
acccording to format, multiplicity or history.'''
|
||||||
|
@ -394,7 +429,7 @@ class String(Field):
|
||||||
return {'view': 'l-f', 'edit': 'lrv-f'}
|
return {'view': 'l-f', 'edit': 'lrv-f'}
|
||||||
|
|
||||||
def getValue(self, obj):
|
def getValue(self, obj):
|
||||||
# Cheat if this field represents p_obj's state
|
# Cheat if this field represents p_obj's state.
|
||||||
if self.name == 'state': return obj.State()
|
if self.name == 'state': return obj.State()
|
||||||
value = Field.getValue(self, obj)
|
value = Field.getValue(self, obj)
|
||||||
if not value:
|
if not value:
|
||||||
|
@ -406,27 +441,46 @@ class String(Field):
|
||||||
value = list(value)
|
value = list(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def store(self, obj, value):
|
def valueIsInRequest(self, request, name):
|
||||||
'''When the value is XHTML, we perform some cleanup.'''
|
if not self.isMultilingual():
|
||||||
if not self.persist: return
|
return Field.valueIsInRequest(self, request, name)
|
||||||
if (self.format == String.XHTML) and value:
|
return request.has_key('%s_%s' % (name, self.languages[0]))
|
||||||
# When image upload is allowed, ckeditor inserts some "style" attrs
|
|
||||||
# (ie for image size when images are resized). So in this case we
|
|
||||||
# can't remove style-related information.
|
|
||||||
try:
|
|
||||||
value = XhtmlCleaner(keepStyles=False).clean(value)
|
|
||||||
except XhtmlCleaner.Error, e:
|
|
||||||
# Errors while parsing p_value can't prevent the user from
|
|
||||||
# storing it.
|
|
||||||
obj.log('Unparsable XHTML content in field "%s".' % self.name,
|
|
||||||
type='warning')
|
|
||||||
Field.store(self, obj, value)
|
|
||||||
|
|
||||||
def storeFromAjax(self, obj):
|
def getRequestValue(self, request, requestName=None):
|
||||||
'''Stores the new field value from an Ajax request, or do nothing if
|
'''The request value may be multilingual.'''
|
||||||
the action was canceled.'''
|
name = requestName or self.name
|
||||||
rq = obj.REQUEST
|
# A unilingual field.
|
||||||
if rq.get('cancel') != 'True': self.store(obj, rq['fieldContent'])
|
if not self.isMultilingual(): return request.get(name, None)
|
||||||
|
# A multilingual field.
|
||||||
|
res = {}
|
||||||
|
for language in self.languages:
|
||||||
|
res[language] = request.get('%s_%s' % (name, language), None)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def isEmptyValue(self, value, obj=None):
|
||||||
|
'''Returns True if the p_value must be considered as an empty value.'''
|
||||||
|
if not self.isMultilingual():
|
||||||
|
return Field.isEmptyValue(self, value, obj)
|
||||||
|
# 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
|
||||||
|
return True
|
||||||
|
|
||||||
|
def isCompleteValue(self, value, obj=None):
|
||||||
|
'''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)
|
||||||
|
# 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
|
||||||
|
return True
|
||||||
|
|
||||||
def getDiffValue(self, obj, value):
|
def getDiffValue(self, obj, value):
|
||||||
'''Returns a version of p_value that includes the cumulative diffs
|
'''Returns a version of p_value that includes the cumulative diffs
|
||||||
|
@ -452,8 +506,8 @@ class String(Field):
|
||||||
comparator = HtmlDiff(res, value or '', iMsg, dMsg)
|
comparator = HtmlDiff(res, value or '', iMsg, dMsg)
|
||||||
return comparator.get()
|
return comparator.get()
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value, showChanges=False):
|
def getUnilingualFormattedValue(self, obj, value, showChanges=False):
|
||||||
if self.isEmptyValue(value): return ''
|
if Field.isEmptyValue(self, value): return ''
|
||||||
res = value
|
res = value
|
||||||
if self.isSelect:
|
if self.isSelect:
|
||||||
if isinstance(self.validator, Selection):
|
if isinstance(self.validator, Selection):
|
||||||
|
@ -480,6 +534,17 @@ class String(Field):
|
||||||
(res.startswith('\n') or res.startswith('\r\n')): res = ' ' + res
|
(res.startswith('\n') or res.startswith('\r\n')): res = ' ' + res
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def getFormattedValue(self, obj, value, showChanges=False):
|
||||||
|
if not self.isMultilingual():
|
||||||
|
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:
|
||||||
|
res[lg]=self.getUnilingualFormattedValue(obj,value[lg],showChanges)
|
||||||
|
return res
|
||||||
|
|
||||||
emptyStringTuple = ('',)
|
emptyStringTuple = ('',)
|
||||||
emptyValuesCatalogIgnored = (None, '')
|
emptyValuesCatalogIgnored = (None, '')
|
||||||
def getIndexValue(self, obj, forSearch=False):
|
def getIndexValue(self, obj, forSearch=False):
|
||||||
|
@ -623,12 +688,23 @@ class String(Field):
|
||||||
elif self.transform == 'capitalize': return value.capitalize()
|
elif self.transform == 'capitalize': return value.capitalize()
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def getStorableValue(self, value):
|
def getUnilingualStorableValue(self, value):
|
||||||
isString = isinstance(value, basestring)
|
isString = isinstance(value, basestring)
|
||||||
|
isEmpty = Field.isEmptyValue(self, value)
|
||||||
# Apply transform if required
|
# Apply transform if required
|
||||||
if isString and not self.isEmptyValue(value) and \
|
if isString and not isEmpty and (self.transform != 'none'):
|
||||||
(self.transform != 'none'):
|
|
||||||
value = self.applyTransform(value)
|
value = self.applyTransform(value)
|
||||||
|
# Clean XHTML if format is XHTML
|
||||||
|
if (self.format == String.XHTML) and not isEmpty:
|
||||||
|
# When image upload is allowed, ckeditor inserts some "style" attrs
|
||||||
|
# (ie for image size when images are resized). So in this case we
|
||||||
|
# can't remove style-related information.
|
||||||
|
try:
|
||||||
|
value = XhtmlCleaner(keepStyles=False).clean(value)
|
||||||
|
except XhtmlCleaner.Error, e:
|
||||||
|
# Errors while parsing p_value can't prevent the user from
|
||||||
|
# storing it.
|
||||||
|
pass
|
||||||
# Truncate the result if longer than self.maxChars
|
# Truncate the result if longer than self.maxChars
|
||||||
if isString and self.maxChars and (len(value) > self.maxChars):
|
if isString and self.maxChars and (len(value) > self.maxChars):
|
||||||
value = value[:self.maxChars]
|
value = value[:self.maxChars]
|
||||||
|
@ -638,6 +714,33 @@ class String(Field):
|
||||||
value = [value]
|
value = [value]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def getStorableValue(self, value):
|
||||||
|
if not self.isMultilingual():
|
||||||
|
return self.getUnilingualStorableValue(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])
|
||||||
|
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))):
|
||||||
|
raise Exception('Multilingual field "%s" accepts a dict whose '\
|
||||||
|
'keys are in field.languages and whose ' \
|
||||||
|
'values are strings.' % self.name)
|
||||||
|
Field.store(self, obj, value)
|
||||||
|
|
||||||
|
def storeFromAjax(self, obj):
|
||||||
|
'''Stores the new field value from an Ajax request, or do nothing if
|
||||||
|
the action was canceled.'''
|
||||||
|
rq = obj.REQUEST
|
||||||
|
if rq.get('cancel') != 'True':
|
||||||
|
self.store(obj, self.getStorableValue(rq['fieldContent']))
|
||||||
|
|
||||||
def getIndexType(self):
|
def getIndexType(self):
|
||||||
'''Index type varies depending on String parameters.'''
|
'''Index type varies depending on String parameters.'''
|
||||||
# If String.isSelect, be it multivalued or not, we define a ListIndex:
|
# If String.isSelect, be it multivalued or not, we define a ListIndex:
|
||||||
|
@ -679,19 +782,19 @@ 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):
|
def getCkLanguage(self, language):
|
||||||
'''Gets the language for CK editor SCAYT. We will use
|
'''Gets the language for CK editor SCAYT. p_language is one of
|
||||||
self.contentLanguage. If it is not supported by CK, we use
|
self.languages if the field is multilingual, None else. If p_language
|
||||||
english.'''
|
is not supported by CK, we use english.'''
|
||||||
lang = self.contentLanguage
|
if not language: language = self.languages[0]
|
||||||
if lang and (lang in self.ckLanguages): return self.ckLanguages[lang]
|
if language in self.ckLanguages: return self.ckLanguages[language]
|
||||||
return 'en_US'
|
return 'en_US'
|
||||||
|
|
||||||
def getCkParams(self, obj):
|
def getCkParams(self, obj, language):
|
||||||
'''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()}
|
'scayt_sLang': self.getCkLanguage(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:
|
||||||
|
@ -704,31 +807,28 @@ class String(Field):
|
||||||
ck.append('%s: %s' % (k, sv))
|
ck.append('%s: %s' % (k, sv))
|
||||||
return ', '.join(ck)
|
return ', '.join(ck)
|
||||||
|
|
||||||
def getJsInit(self, obj):
|
def getJsInit(self, obj, language):
|
||||||
'''Gets the Javascript init code for displaying a rich editor for this
|
'''Gets the Javascript init code for displaying a rich editor for this
|
||||||
field (rich field only).'''
|
field (rich field only). If the field is multilingual, we must init
|
||||||
|
the rich text editor for a given p_language (among self.languages).
|
||||||
|
Else, p_languages is None.'''
|
||||||
|
name = not language and self.name or ('%s_%s' % (self.name, language))
|
||||||
return 'CKEDITOR.replace("%s", {%s})' % \
|
return 'CKEDITOR.replace("%s", {%s})' % \
|
||||||
(self.name, self.getCkParams(obj))
|
(name, self.getCkParams(obj, language))
|
||||||
|
|
||||||
def getJsInlineInit(self, obj):
|
def getJsInlineInit(self, obj, language):
|
||||||
'''Gets the Javascript init code for enabling inline edition of this
|
'''Gets the Javascript init code for enabling inline edition of this
|
||||||
field (rich text only).'''
|
field (rich text only). If the field is multilingual, the current
|
||||||
|
p_language is given. Else, p_language is None.'''
|
||||||
uid = obj.id
|
uid = obj.id
|
||||||
|
name = not language and self.name or ('%s_%s' % (self.name, language))
|
||||||
return "CKEDITOR.disableAutoInline = true;\n" \
|
return "CKEDITOR.disableAutoInline = true;\n" \
|
||||||
"CKEDITOR.inline('%s_%s_ck', {%s, on: {blur: " \
|
"CKEDITOR.inline('%s_%s_ck', {%s, on: {blur: " \
|
||||||
"function( event ) { var content = event.editor.getData(); " \
|
"function( event ) { var content = event.editor.getData(); " \
|
||||||
"doInlineSave('%s', '%s', '%s', content)}}})" % \
|
"doInlineSave('%s', '%s', '%s', content)}}})" % \
|
||||||
(uid, self.name, self.getCkParams(obj), uid, self.name,
|
(uid, name, self.getCkParams(obj, language), uid, name,
|
||||||
obj.absolute_url())
|
obj.absolute_url())
|
||||||
|
|
||||||
return "CKEDITOR.disableAutoInline = true;\n" \
|
|
||||||
"CKEDITOR.inline('%s_%s_ck', {on: {blur: " \
|
|
||||||
"function( event ) { var data = event.editor.getData(); " \
|
|
||||||
"askAjaxChunk('%s_%s','POST','%s','%s:pxSave', " \
|
|
||||||
"{'fieldContent': encodeURIComponent(data)}, " \
|
|
||||||
"null, evalInnerScripts);}}});"% \
|
|
||||||
(uid, self.name, uid, self.name, obj.absolute_url(), self.name)
|
|
||||||
|
|
||||||
def isSelected(self, obj, fieldName, vocabValue, dbValue):
|
def isSelected(self, obj, fieldName, vocabValue, dbValue):
|
||||||
'''When displaying a selection box (only for fields with a validator
|
'''When displaying a selection box (only for fields with a validator
|
||||||
being a list), must the _vocabValue appear as selected? p_fieldName
|
being a list), must the _vocabValue appear as selected? p_fieldName
|
||||||
|
|
|
@ -29,13 +29,13 @@ input[type=submit] { border: 1px solid #d0d0d0; background-color: #f8f8f8;
|
||||||
cursor: pointer }
|
cursor: pointer }
|
||||||
input[type=password] { border: 1px solid #d0d0d0; background-color: #f8f8f8;
|
input[type=password] { border: 1px solid #d0d0d0; background-color: #f8f8f8;
|
||||||
font-family: "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif }
|
font-family: "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif }
|
||||||
input[type=text] { border: 1px solid #d0d0d0; background-color: #f8f8f8;
|
input[type=text] { border: 1px solid #d0d0d0;
|
||||||
font-family: "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif;
|
font-family: "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif;
|
||||||
margin-bottom: 1px }
|
margin-bottom: 1px }
|
||||||
select { border: 1px solid #d0d0d0; background-color: #f8f8f8 }
|
select { border: 1px solid #d0d0d0; background-color: #f8f8f8 }
|
||||||
|
|
||||||
textarea { width: 99%; font: 100% "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif;
|
textarea { width: 99%; font: 100% "Lucida Grande","Lucida Sans Unicode",Helvetica,Arial,Verdana,sans-serif;
|
||||||
border: 1px solid #d0d0d0; background-color: #f8f8f8 }
|
border: 1px solid #d0d0d0; background-color: white }
|
||||||
label { color: #888888; font-size: 11px; margin: 3px 0;
|
label { color: #888888; font-size: 11px; margin: 3px 0;
|
||||||
text-transform: uppercase }
|
text-transform: uppercase }
|
||||||
legend { padding-bottom: 2px; padding-right: 3px; color: black }
|
legend { padding-bottom: 2px; padding-right: 3px; color: black }
|
||||||
|
@ -167,3 +167,5 @@ td.search { padding-top: 8px }
|
||||||
.tabs { position:relative; bottom:-2px }
|
.tabs { position:relative; bottom:-2px }
|
||||||
.tab { padding: 0 10px 0 10px; text-align: center; font-size: 90%;
|
.tab { padding: 0 10px 0 10px; text-align: center; font-size: 90%;
|
||||||
font-weight: bold}
|
font-weight: bold}
|
||||||
|
.language {color: grey; font-size: 7pt; border: grey 1px solid; padding: 2px;
|
||||||
|
margin: 0 2px 0 4px}
|
||||||
|
|
|
@ -211,7 +211,7 @@ class AbstractWrapper(object):
|
||||||
<!-- Language selector -->
|
<!-- Language selector -->
|
||||||
<select if="ztool.showLanguageSelector()" class="pageLink"
|
<select if="ztool.showLanguageSelector()" class="pageLink"
|
||||||
var2="languages=ztool.getLanguages();
|
var2="languages=ztool.getLanguages();
|
||||||
defaultLanguage=languages[0]"
|
defaultLanguage=languages[0]"
|
||||||
onchange=":'switchLanguage(this,%s)' % q(ztool.getSiteUrl())">
|
onchange=":'switchLanguage(this,%s)' % q(ztool.getSiteUrl())">
|
||||||
<option for="lg in languages" value=":lg"
|
<option for="lg in languages" value=":lg"
|
||||||
selected=":lang == lg">:ztool.getLanguageName(lg)</option>
|
selected=":lang == lg">:ztool.getLanguageName(lg)</option>
|
||||||
|
|
Loading…
Reference in a new issue