[gen] Added field.persist to avoid storing values for fields that do not require it (like master fields only used to determine selectable values among slave fields).

This commit is contained in:
Gaetan Delannay 2014-03-04 15:03:37 +01:00
parent b8ceb66a49
commit ea08d7981f
19 changed files with 106 additions and 75 deletions

View file

@ -61,8 +61,7 @@ class Field:
value=zobj.getFormattedFieldValue(name, rawValue, showChanges); value=zobj.getFormattedFieldValue(name, rawValue, showChanges);
requestValue=zobj.getRequestFieldValue(name); requestValue=zobj.getRequestFieldValue(name);
inRequest=req.has_key(name); inRequest=req.has_key(name);
errors=errors|(); error=req.get('%s_error' % name);
inError=name in errors;
isMultiple=(field.multiplicity[1] == None) or \ isMultiple=(field.multiplicity[1] == None) or \
(field.multiplicity[1] > 1); (field.multiplicity[1] > 1);
masterCss=field.slaves and ('master_%s' % name) or ''; masterCss=field.slaves and ('master_%s' % name) or '';
@ -86,8 +85,8 @@ class Field:
src=":url('help')"/></acronym>''') src=":url('help')"/></acronym>''')
# Displays validation-error-related info about a field. # Displays validation-error-related info about a field.
pxValidation = Px('''<x><acronym if="inError" title=":errors[name]"><img pxValidation = Px('''<x><acronym if="error" title=":error"><img
src=":url('warning')"/></acronym><img if="not inError" src=":url('warning')"/></acronym><img if="not error"
src=":url('warning_no.gif')"/></x>''') src=":url('warning_no.gif')"/></x>''')
# Displays the fact that a field is required. # Displays the fact that a field is required.
@ -107,7 +106,7 @@ class Field:
layouts, move, indexed, searchable, specificReadPermission, layouts, move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, maxChars, colspan, specificWritePermission, width, height, maxChars, colspan,
master, masterValue, focus, historized, sync, mapping, label, master, masterValue, focus, historized, sync, mapping, label,
sdefault, scolspan, swidth, sheight): sdefault, scolspan, swidth, sheight, persist):
# The validator restricts which values may be defined. It can be an # The validator restricts which values may be defined. It can be an
# interval (1,None), a list of string values ['choice1', 'choice2'], # interval (1,None), a list of string values ['choice1', 'choice2'],
# a regular expression, a custom function, a Selection instance, etc. # a regular expression, a custom function, a Selection instance, etc.
@ -215,6 +214,10 @@ class Field:
# Width and height for the search widget # Width and height for the search widget
self.swidth = swidth or width self.swidth = swidth or width
self.sheight = sheight or height self.sheight = sheight or height
# "persist" indicates if field content must be stored in the database.
# For some fields it is not wanted (ie, fields used only as masters to
# update slave's selectable values).
self.persist = persist
def init(self, name, klass, appName): def init(self, name, klass, appName):
'''When the application server starts, this secondary constructor is '''When the application server starts, this secondary constructor is
@ -584,26 +587,14 @@ class Field:
res += '+' res += '+'
return res return res
def getOnChange(self, name, zobj, layoutType): def getOnChange(self, zobj, layoutType):
'''When this field is a master, this method computes the call to the '''When this field is a master, this method computes the call to the
Javascript function that will be called when its value changes (in Javascript function that will be called when its value changes (in
order to update slaves).''' order to update slaves).'''
if not self.slaves: return '' if not self.slaves: return ''
q = zobj.getTool().quote q = zobj.getTool().quote
# Create the dict of request values for slave fields. return 'updateSlaves(this,null,%s,%s)' % \
rvs = {} (q(zobj.absolute_url()), q(layoutType))
req = zobj.REQUEST
for slave in self.slaves:
name = slave.name
if not req.has_key(name): continue
if not req[name]: continue
rvs[name] = req[name]
if rvs:
rvs = ',%s' % sutils.getStringDict(rvs)
else:
rvs = ''
return 'updateSlaves(this,null,%s,%s%s)' % \
(q(zobj.absolute_url()), q(layoutType), rvs)
def isEmptyValue(self, value, obj=None): def isEmptyValue(self, value, obj=None):
'''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.'''
@ -680,7 +671,7 @@ class Field:
def store(self, obj, value): def store(self, obj, value):
'''Stores the p_value (produced by m_getStorableValue) that complies to '''Stores the p_value (produced by m_getStorableValue) that complies to
p_self type definition on p_obj.''' p_self type definition on p_obj.'''
setattr(obj, self.name, value) if self.persist: setattr(obj, self.name, value)
def callMethod(self, obj, method, cache=True): def callMethod(self, obj, method, cache=True):
'''This method is used to call a p_method on p_obj. p_method is part of '''This method is used to call a p_method on p_obj. p_method is part of

View file

@ -74,7 +74,7 @@ class Action(Field):
move, indexed, False, specificReadPermission, move, indexed, False, specificReadPermission,
specificWritePermission, width, height, None, colspan, specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, False, mapping, master, masterValue, focus, historized, False, mapping,
label, None, None, None, None) label, None, None, None, None, False)
self.validable = False self.validable = False
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'} def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}

View file

@ -23,19 +23,17 @@ from appy.gen.layout import Table
class Boolean(Field): class Boolean(Field):
'''Field for storing boolean values.''' '''Field for storing boolean values.'''
pxView = pxCell = Px(''' pxView = pxCell = Px('''<x>:value</x>
<x><x>:value</x> <input type="hidden" if="masterCss"
<input type="hidden" if="masterCss" class=":masterCss" value=":rawValue" name=":name" id=":name"/>''')
class=":masterCss" value=":rawValue" name=":name" id=":name"/>
</x>''')
pxEdit = Px(''' pxEdit = Px('''
<x var="isChecked=field.isChecked(zobj, rawValue)"> <x var="isChecked=field.isChecked(zobj, rawValue)">
<input type="checkbox" name=":name + '_visible'" id=":name" <input type="checkbox" name=":name + '_visible'" id=":name"
class=":masterCss" checked=":isChecked" class=":masterCss" checked=":isChecked"
onclick=":'toggleCheckbox(%s, %s); %s' % (q(name), \ onclick=":'toggleCheckbox(%s, %s); %s' % (q(name), \
q('%s_hidden' % name), field.getOnChange(name, zobj, \ q('%s_hidden' % name), \
layoutType))"/> field.getOnChange(zobj, layoutType))"/>
<input type="hidden" name=":name" id=":'%s_hidden' % name" <input type="hidden" name=":name" id=":'%s_hidden' % name"
value=":isChecked and 'True' or 'False'"/> value=":isChecked and 'True' or 'False'"/>
</x>''') </x>''')
@ -64,13 +62,14 @@ class Boolean(Field):
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None, focus=False, historized=False, mapping=None, label=None,
sdefault=False, scolspan=1, swidth=None, sheight=None): sdefault=False, scolspan=1, swidth=None, sheight=None,
persist=True):
Field.__init__(self, validator, multiplicity, default, show, page, Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable, group, layouts, move, indexed, searchable,
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan, historized, True, mapping, label, sdefault, scolspan,
swidth, sheight) swidth, sheight, persist)
self.pythonType = bool self.pythonType = bool
# Layout including a description # Layout including a description

View file

@ -222,7 +222,7 @@ class Calendar(Field):
layouts, move, False, False, specificReadPermission, layouts, move, False, False, specificReadPermission,
specificWritePermission, width, height, None, colspan, specificWritePermission, width, height, None, colspan,
master, masterValue, focus, False, True, mapping, label, master, masterValue, focus, False, True, mapping, label,
None, None, None, None) None, None, None, None, True)
# eventTypes can be a "static" list or tuple of strings that identify # eventTypes can be a "static" list or tuple of strings that identify
# the types of events that are supported by this calendar. It can also # the types of events that are supported by this calendar. It can also
# be a method that computes such a "dynamic" list or tuple. When # be a method that computes such a "dynamic" list or tuple. When

View file

@ -70,7 +70,7 @@ class Computed(Field):
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, sync, mapping, label, sdefault, scolspan, historized, sync, mapping, label, sdefault, scolspan,
swidth, sheight) swidth, sheight, False)
self.validable = False self.validable = False
def getValue(self, obj): def getValue(self, obj):

View file

@ -173,7 +173,8 @@ class Date(Field):
specificWritePermission=False, width=None, height=None, specificWritePermission=False, width=None, height=None,
maxChars=None, colspan=1, master=None, masterValue=None, maxChars=None, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None, focus=False, historized=False, mapping=None, label=None,
sdefault=None, scolspan=1, swidth=None, sheight=None): sdefault=None, scolspan=1, swidth=None, sheight=None,
persist=True):
self.format = format self.format = format
self.calendar = calendar self.calendar = calendar
self.startYear = startYear self.startYear = startYear
@ -186,7 +187,7 @@ class Date(Field):
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan, historized, True, mapping, label, sdefault, scolspan,
swidth, sheight) swidth, sheight, persist)
def getCss(self, layoutType, res): def getCss(self, layoutType, res):
# CSS files are only required if the calendar must be shown. # CSS files are only required if the calendar must be shown.

View file

@ -279,7 +279,7 @@ class File(Field):
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan, historized, True, mapping, label, sdefault, scolspan,
swidth, sheight) swidth, sheight, True)
@staticmethod @staticmethod
def getFileObject(filePath, fileName=None, zope=False): def getFileObject(filePath, fileName=None, zope=False):

View file

@ -58,7 +58,7 @@ class Float(Field):
maxChars=13, colspan=1, master=None, masterValue=None, maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None, focus=False, historized=False, mapping=None, label=None,
sdefault=('',''), scolspan=1, swidth=None, sheight=None, sdefault=('',''), scolspan=1, swidth=None, sheight=None,
precision=None, sep=(',', '.'), tsep=' '): persist=True, precision=None, sep=(',', '.'), tsep=' '):
# The precision is the number of decimal digits. This number is used # The precision is the number of decimal digits. This number is used
# for rendering the float, but the internal float representation is not # for rendering the float, but the internal float representation is not
# rounded. # rounded.
@ -80,7 +80,7 @@ class Float(Field):
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus, height, maxChars, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan, historized, True, mapping, label, sdefault, scolspan,
swidth, sheight) swidth, sheight, persist)
self.pythonType = float self.pythonType = float
def getFormattedValue(self, obj, value, showChanges=False): def getFormattedValue(self, obj, value, showChanges=False):

View file

@ -34,6 +34,6 @@ class Info(Field):
move, indexed, False, specificReadPermission, move, indexed, False, specificReadPermission,
specificWritePermission, width, height, None, colspan, specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, False, mapping, master, masterValue, focus, historized, False, mapping,
label, None, None, None, None) label, None, None, None, None, False)
self.validable = False self.validable = False
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -54,13 +54,14 @@ class Integer(Field):
specificWritePermission=False, width=5, height=None, specificWritePermission=False, width=5, height=None,
maxChars=13, colspan=1, master=None, masterValue=None, maxChars=13, colspan=1, master=None, masterValue=None,
focus=False, historized=False, mapping=None, label=None, focus=False, historized=False, mapping=None, label=None,
sdefault=('',''), scolspan=1, swidth=None, sheight=None): sdefault=('',''), scolspan=1, swidth=None, sheight=None,
persist=True):
Field.__init__(self, validator, multiplicity, default, show, page, Field.__init__(self, validator, multiplicity, default, show, page,
group, layouts, move, indexed, searchable, group, layouts, move, indexed, searchable,
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus, height, maxChars, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan, historized, True, mapping, label, sdefault, scolspan,
swidth, sheight) swidth, sheight, persist)
self.pythonType = long self.pythonType = long
def validateValue(self, obj, value): def validateValue(self, obj, value):

View file

@ -83,7 +83,8 @@ class List(Field):
group, layouts, move, indexed, False, group, layouts, move, indexed, False,
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, True, mapping, label, None, None, None, None) historized, True, mapping, label, None, None, None, None,
True)
self.validable = True self.validable = True
# Tuples of (names, Field instances) determining the format of every # Tuples of (names, Field instances) determining the format of every
# element in the list. # element in the list.

View file

@ -54,7 +54,7 @@ class Ogone(Field):
move, False, False,specificReadPermission, move, False, False,specificReadPermission,
specificWritePermission, width, height, None, colspan, specificWritePermission, width, height, None, colspan,
master, masterValue, focus, False, True, mapping, label, master, masterValue, focus, False, True, mapping, label,
None, None, None, None) None, None, None, None, False)
# orderMethod must contain a method returning a dict containing info # orderMethod must contain a method returning a dict containing info
# about the order. Following keys are mandatory: # about the order. Following keys are mandatory:
# * orderID An identifier for the order. Don't use the object UID # * orderID An identifier for the order. Don't use the object UID

View file

@ -84,7 +84,9 @@ class Pod(Field):
move, indexed, searchable, specificReadPermission, move, indexed, searchable, specificReadPermission,
specificWritePermission, width, height, None, colspan, specificWritePermission, width, height, None, colspan,
master, masterValue, focus, historized, False, mapping, master, masterValue, focus, historized, False, mapping,
label, None, None, None, None) label, None, None, None, None, True)
# Param "persist" is set to True but actually, persistence for a pod
# field is determined by freezing.
self.validable = False self.validable = False
def isFrozen(self, obj): def isFrozen(self, obj):

View file

@ -286,7 +286,7 @@ class Ref(Field):
uids=[o.UID() for o in \ uids=[o.UID() for o in \
field.getLinkedObjects(zobj).objects]" field.getLinkedObjects(zobj).objects]"
name=":name" id=":name" size=":isMultiple and field.height or ''" name=":name" id=":name" size=":isMultiple and field.height or ''"
onchange=":field.getOnChange(name, zobj, layoutType)" onchange=":field.getOnChange(zobj, layoutType)"
multiple=":isMultiple"> multiple=":isMultiple">
<option value="" if="not isMultiple">:_('choose_a_value')</option> <option value="" if="not isMultiple">:_('choose_a_value')</option>
<option for="ztied in zobjects" var2="uid=ztied.o.UID()" <option for="ztied in zobjects" var2="uid=ztied.o.UID()"
@ -328,8 +328,8 @@ class Ref(Field):
label=None, queryable=False, queryFields=None, queryNbCols=1, label=None, queryable=False, queryFields=None, queryNbCols=1,
navigable=False, searchSelect=None, changeOrder=True, navigable=False, searchSelect=None, changeOrder=True,
sdefault='', scolspan=1, swidth=None, sheight=None, sdefault='', scolspan=1, swidth=None, sheight=None,
render='list', menuIdMethod=None, menuInfoMethod=None, persist=True, render='list', menuIdMethod=None,
menuUrlMethod=None): menuInfoMethod=None, menuUrlMethod=None):
self.klass = klass self.klass = klass
self.attribute = attribute self.attribute = attribute
# May the user add new objects through this ref ? # May the user add new objects through this ref ?
@ -431,7 +431,7 @@ class Ref(Field):
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, None, colspan, master, masterValue, focus, height, None, colspan, master, masterValue, focus,
historized, sync, mapping, label, sdefault, scolspan, historized, sync, mapping, label, sdefault, scolspan,
swidth, sheight) swidth, sheight, persist)
self.validable = self.link self.validable = self.link
def getDefaultLayouts(self): def getDefaultLayouts(self):
@ -662,6 +662,7 @@ class Ref(Field):
* a Zope object; * a Zope object;
* a Appy object; * a Appy object;
* a list of Appy or Zope objects.''' * a list of Appy or Zope objects.'''
if not self.persist: return
# Standardize p_value into a list of Zope objects # Standardize p_value into a list of Zope objects
objects = value objects = value
if not objects: objects = [] if not objects: objects = []
@ -759,6 +760,11 @@ class Ref(Field):
res = self.masterValue(obj, masterValues) res = self.masterValue(obj, masterValues)
return res return res
else: else:
# If this field is a ajax-updatable slave, no need to compute
# selectable objects: it will be overridden by method
# self.masterValue by a subsequent ajax request (=the "if" statement
# above).
if self.masterValue and callable(self.masterValue): return []
if not self.select: if not self.select:
# No select method has been defined: we must retrieve all # No select method has been defined: we must retrieve all
# objects of the referred type that the user is allowed to # objects of the referred type that the user is allowed to

View file

@ -110,7 +110,7 @@ class String(Field):
withTranslations=True, withBlankValue=True)" withTranslations=True, withBlankValue=True)"
name=":name" id=":name" class=":masterCss" name=":name" id=":name" class=":masterCss"
multiple=":isMultiple and 'multiple' or ''" multiple=":isMultiple and 'multiple' or ''"
onchange=":field.getOnChange(name, zobj, layoutType)" onchange=":field.getOnChange(zobj, layoutType)"
size=":isMultiple and field.height or 1"> size=":isMultiple and field.height or 1">
<option for="val in possibleValues" value=":val[0]" <option for="val in possibleValues" value=":val[0]"
selected=":field.isSelected(zobj, name, val[0], rawValue)" selected=":field.isSelected(zobj, name, val[0], rawValue)"
@ -292,8 +292,9 @@ class String(Field):
width=None, height=None, maxChars=None, colspan=1, master=None, width=None, height=None, maxChars=None, colspan=1, master=None,
masterValue=None, focus=False, historized=False, mapping=None, masterValue=None, focus=False, historized=False, mapping=None,
label=None, sdefault='', scolspan=1, swidth=None, sheight=None, label=None, sdefault='', scolspan=1, swidth=None, sheight=None,
transform='none', styles=('p','h1','h2','h3','h4'), persist=True, transform='none',
allowImageUpload=True, inlineEdit=False): styles=('p','h1','h2','h3','h4'), allowImageUpload=True,
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
@ -318,7 +319,7 @@ class String(Field):
specificReadPermission, specificWritePermission, width, specificReadPermission, specificWritePermission, width,
height, maxChars, colspan, master, masterValue, focus, height, maxChars, colspan, master, masterValue, focus,
historized, True, mapping, label, sdefault, scolspan, historized, True, mapping, label, sdefault, scolspan,
swidth, sheight) swidth, sheight, persist)
self.isSelect = self.isSelection() self.isSelect = self.isSelection()
# If self.isSelect, self.sdefault must be a list of value(s). # If self.isSelect, self.sdefault must be a list of value(s).
if self.isSelect and not sdefault: if self.isSelect and not sdefault:
@ -390,6 +391,7 @@ class String(Field):
def store(self, obj, value): def store(self, obj, value):
'''When the value is XHTML, we perform some cleanup.''' '''When the value is XHTML, we perform some cleanup.'''
if not self.persist: return
if (self.format == String.XHTML) and value: if (self.format == String.XHTML) and value:
# When image upload is allowed, ckeditor inserts some "style" attrs # When image upload is allowed, ckeditor inserts some "style" attrs
# (ie for image size when images are resized). So in this case we # (ie for image size when images are resized). So in this case we

View file

@ -417,7 +417,7 @@ class BaseMixin:
# Trigger field-specific validation # Trigger field-specific validation
self.intraFieldValidation(errors, values) self.intraFieldValidation(errors, values)
if errors.__dict__: if errors.__dict__:
rq.set('errors', errors.__dict__) for k,v in errors.__dict__.iteritems(): rq.set('%s_error' % k, v)
self.say(errorMessage) self.say(errorMessage)
return self.gotoEdit() return self.gotoEdit()
@ -425,7 +425,7 @@ class BaseMixin:
msg = self.interFieldValidation(errors, values) msg = self.interFieldValidation(errors, values)
if not msg: msg = errorMessage if not msg: msg = errorMessage
if errors.__dict__: if errors.__dict__:
rq.set('errors', errors.__dict__) for k,v in errors.__dict__.iteritems(): rq.set('%s_error' % k, v)
self.say(msg) self.say(msg)
return self.gotoEdit() return self.gotoEdit()
@ -814,19 +814,27 @@ class BaseMixin:
res.append(field) res.append(field)
return res return res
def getSlaveFieldsRequestValues(self, pageName): def getSlavesRequestInfo(self, pageName):
'''Returns the list of slave fields having a masterValue being a '''When slave fields must be updated via Ajax requests, we must carry
method.''' some information from the global request object to the ajax requests:
res = {} - the selected values in slave fields;
- validation errors.'''
requestValues = {}
errors = {}
req = self.REQUEST req = self.REQUEST
for field in self.getAllAppyTypes(): for field in self.getAllAppyTypes():
if field.page.name != pageName: continue if field.page.name != pageName: continue
if field.masterValue and callable(field.masterValue): if field.masterValue and callable(field.masterValue):
# We have such a field # We have a slave field that is updated via ajax requests.
name = field.name name = field.name
# Remember the request value for this field if present.
if req.has_key(name) and req[name]: if req.has_key(name) and req[name]:
res[name] = req[name] requestValues[name] = req[name]
return sutils.getStringDict(res) # Remember the validation error for this field if present.
errorKey = '%s_error' % name
if req.has_key(errorKey):
errors[name] = req[errorKey]
return sutils.getStringDict(requestValues), sutils.getStringDict(errors)
def getCssJs(self, fields, layoutType, res): def getCssJs(self, fields, layoutType, res):
'''Gets, in p_res ~{'css':[s_css], 'js':[s_js]}~ the lists of '''Gets, in p_res ~{'css':[s_css], 'js':[s_js]}~ the lists of

View file

@ -83,6 +83,20 @@ function evalInnerScripts(xhrObject, hookElem) {
for (var i=0; i<scripts.length; i++) { eval(scripts[i].innerHTML) } for (var i=0; i<scripts.length; i++) { eval(scripts[i].innerHTML) }
} }
function injectChunk(elem, content){
if (!isIe) elem.innerHTML = content;
else {
if (elem.tagName != 'TABLE') elem.innerHTML = content;
else {
/* IE doesn't want to replace content of a table. Force it to do so via
a temporary DOM element. */
var temp = document.createElement('div');
temp.innerHTML = content;
elem.replaceChild(temp.firstChild, elem.firstChild);
}
}
}
function getAjaxChunk(pos) { function getAjaxChunk(pos) {
// This function is the callback called by the AJAX machinery (see function // This function is the callback called by the AJAX machinery (see function
// askAjaxChunk below) when an Ajax response is available. // askAjaxChunk below) when an Ajax response is available.
@ -93,13 +107,14 @@ function getAjaxChunk(pos) {
if (xhrObjects[pos].xhr.readyState == 1) { if (xhrObjects[pos].xhr.readyState == 1) {
// The request has been initialized: display the waiting radar // The request has been initialized: display the waiting radar
var hookElem = document.getElementById(hook); var hookElem = document.getElementById(hook);
if (hookElem) hookElem.innerHTML = "<div align=\"center\"><img src=\"ui/waiting.gif\"/><\/div>"; if (hookElem)
injectChunk(hookElem, "<div align=\"center\"><img src=\"ui/waiting.gif\"/><\/div>");
} }
if (xhrObjects[pos].xhr.readyState == 4) { if (xhrObjects[pos].xhr.readyState == 4) {
// We have received the HTML chunk // We have received the HTML chunk
var hookElem = document.getElementById(hook); var hookElem = document.getElementById(hook);
if (hookElem && (xhrObjects[pos].xhr.status == 200)) { if (hookElem && (xhrObjects[pos].xhr.status == 200)) {
hookElem.innerHTML = xhrObjects[pos].xhr.responseText; injectChunk(hookElem, xhrObjects[pos].xhr.responseText);
// Call a custom Javascript function if required // Call a custom Javascript function if required
if (xhrObjects[pos].onGet) { if (xhrObjects[pos].onGet) {
xhrObjects[pos].onGet(xhrObjects[pos], hookElem); xhrObjects[pos].onGet(xhrObjects[pos], hookElem);
@ -229,12 +244,13 @@ function askComputedField(hookId, objectUrl, fieldName) {
} }
function askField(hookId, objectUrl, layoutType, showChanges, masterValues, function askField(hookId, objectUrl, layoutType, showChanges, masterValues,
requestValue){ requestValue, error){
// Sends an Ajax request for getting the content of any field. // Sends an Ajax request for getting the content of any field.
var fieldName = hookId.split('_')[1]; var fieldName = hookId.split('_')[1];
var params = {'layoutType': layoutType, 'showChanges': showChanges}; var params = {'layoutType': layoutType, 'showChanges': showChanges};
if (masterValues) params['masterValues'] = masterValues.join('*'); if (masterValues) params['masterValues'] = masterValues.join('*');
if (requestValue) params[fieldName] = requestValue; if (requestValue) params[fieldName] = requestValue;
if (error) params[fieldName + '_error'] = error;
askAjaxChunk(hookId, 'GET', objectUrl, fieldName+':pxRender', params, null, askAjaxChunk(hookId, 'GET', objectUrl, fieldName+':pxRender', params, null,
evalInnerScripts); evalInnerScripts);
} }
@ -354,7 +370,7 @@ function getSlaves(master) {
return res; return res;
} }
function updateSlaves(master, slave, objectUrl, layoutType, requestValues){ function updateSlaves(master,slave,objectUrl,layoutType,requestValues,errors){
/* Given the value(s) in a master field, we must update slave's visibility or /* Given the value(s) in a master field, we must update slave's visibility or
value(s). If p_slave is given, it updates only this slave. Else, it updates value(s). If p_slave is given, it updates only this slave. Else, it updates
all slaves of p_master. */ all slaves of p_master. */
@ -382,15 +398,19 @@ function updateSlaves(master, slave, objectUrl, layoutType, requestValues){
var reqValue = null; var reqValue = null;
if (requestValues && (slaveName in requestValues)) if (requestValues && (slaveName in requestValues))
reqValue = requestValues[slaveName]; reqValue = requestValues[slaveName];
askField(slaveId, objectUrl, layoutType, false, masterValues, reqValue); var err = null;
if (errors && (slaveName in errors))
err = errors[slaveName];
askField(slaveId,objectUrl,layoutType,false,masterValues,reqValue,err);
} }
} }
} }
function initSlaves(objectUrl, layoutType, requestValues) { function initSlaves(objectUrl, layoutType, requestValues, errors) {
/* When the current page is loaded, we must set the correct state for all /* When the current page is loaded, we must set the correct state for all
slave fields. p_requestValues are those from the slave fields that must slave fields. For those that are updated via Ajax requests, their
be ajax-updated. */ p_requestValues and validation p_errors must be carried to those
requests. */
slaves = getElementsHavingName('table', 'slave'); slaves = getElementsHavingName('table', 'slave');
i = slaves.length -1; i = slaves.length -1;
while (i >= 0) { while (i >= 0) {
@ -398,8 +418,7 @@ function initSlaves(objectUrl, layoutType, requestValues) {
master = document.getElementById(masterName); master = document.getElementById(masterName);
// If master is not here, we can't hide its slaves when appropriate. // If master is not here, we can't hide its slaves when appropriate.
if (master) { if (master) {
updateSlaves(master, slaves[i], objectUrl, layoutType, requestValues); updateSlaves(master,slaves[i],objectUrl,layoutType,requestValues,errors);}
}
i -= 1; i -= 1;
} }
} }

View file

@ -141,9 +141,10 @@ class ToolWrapper(AbstractWrapper):
</x>''') </x>''')
pxPageBottom = Px(''' pxPageBottom = Px('''
<script type="text/javascript">:'initSlaves(%s,%s,%s)' % \ <script var="info=zobj.getSlavesRequestInfo(page)"
(q(zobj.absolute_url()), q(layoutType), \ type="text/javascript">:'initSlaves(%s,%s,%s,%s)' % \
zobj.getSlaveFieldsRequestValues(page))</script>''') (q(zobj.absolute_url()), q(layoutType), info[0], info[1])
</script>''')
pxPortlet = Px(''' pxPortlet = Px('''
<x var="toolUrl=tool.url; <x var="toolUrl=tool.url;

View file

@ -533,7 +533,7 @@ class AbstractWrapper(object):
pxEdit = Px(''' pxEdit = Px('''
<x var="x=zobj.allows('write', raiseError=True); <x var="x=zobj.allows('write', raiseError=True);
errors=req.get('errors', None) or {}; errors=req.get('errors', {});
layout=zobj.getPageLayout(layoutType); layout=zobj.getPageLayout(layoutType);
cssJs={}; cssJs={};
phaseObj=zobj.getAppyPhases(currentOnly=True, \ phaseObj=zobj.getAppyPhases(currentOnly=True, \