[gen] Ref field: more work on global actions.
This commit is contained in:
parent
09b0ed5121
commit
b9bfee9615
|
@ -685,6 +685,13 @@ class Field:
|
|||
obj.log(sutils.Traceback.get(), type='error')
|
||||
raise e
|
||||
|
||||
def getAttribute(self, obj, name):
|
||||
'''Gets the value of attribue p_name on p_self, which can be a simple
|
||||
value or the result of a method call on p_obj.'''
|
||||
res = getattr(self, name)
|
||||
if not callable(res): return res
|
||||
return self.callMethod(obj, res)
|
||||
|
||||
def process(self, obj):
|
||||
'''This method is a general hook allowing a field to perform some
|
||||
processing after an URL on an object has been called, of the form
|
||||
|
|
|
@ -277,7 +277,8 @@ class Ref(Field):
|
|||
(q(ajaxHookId), q(zobj.absolute_url()), \
|
||||
q(field.name), q(innerRef));
|
||||
changeOrder=False;
|
||||
checkboxes=field.checkboxesEnabled(zobj) and (totalNumber > 1);
|
||||
checkboxes=field.getAttribute(zobj, 'checkboxes') and \
|
||||
(totalNumber > 1);
|
||||
showSubTitles=showSubTitles|\
|
||||
req.get('showSubTitles', 'true') == 'true';
|
||||
subLabel='selectable_objects'">:field.pxViewList</x>''')
|
||||
|
@ -341,8 +342,8 @@ class Ref(Field):
|
|||
navBaseCall='askRefField(%s,%s,%s,%s,**v**)' % \
|
||||
(q(ajaxHookId), q(zobj.absolute_url()), \
|
||||
q(field.name), q(innerRef));
|
||||
changeOrder=field.changeOrderEnabled(zobj);
|
||||
checkboxesEnabled=field.checkboxesEnabled(zobj);
|
||||
changeOrder=field.getAttribute(zobj, 'changeOrder');
|
||||
checkboxesEnabled=field.getAttribute(zobj, 'checkboxes');
|
||||
checkboxes=checkboxesEnabled and (totalNumber > 1);
|
||||
showSubTitles=req.get('showSubTitles', 'true') == 'true'">
|
||||
<!-- The definition of "atMostOneRef" above may sound strange: we
|
||||
|
@ -415,9 +416,9 @@ class Ref(Field):
|
|||
masterValue=None, focus=False, historized=False, mapping=None,
|
||||
label=None, queryable=False, queryFields=None, queryNbCols=1,
|
||||
navigable=False, changeOrder=True, checkboxes=True,
|
||||
sdefault='', scolspan=1, swidth=None, sheight=None,
|
||||
sselect=None, persist=True, render='list', menuIdMethod=None,
|
||||
menuInfoMethod=None, menuUrlMethod=None):
|
||||
checkboxesDefault=None, sdefault='', scolspan=1, swidth=None,
|
||||
sheight=None, sselect=None, persist=True, render='list',
|
||||
menuIdMethod=None, menuInfoMethod=None, menuUrlMethod=None):
|
||||
self.klass = klass
|
||||
self.attribute = attribute
|
||||
# May the user add new objects through this ref ?
|
||||
|
@ -509,6 +510,11 @@ class Ref(Field):
|
|||
# a checkbox: global actions will be possible, that will act on the
|
||||
# subset of selected objects: delete, unlink, etc.
|
||||
self.checkboxes = checkboxes
|
||||
# Default value for checkboxes, if enabled.
|
||||
if checkboxesDefault == None:
|
||||
self.checkboxesDefault = bool(self.link)
|
||||
else:
|
||||
self.checkboxesDefault = checkboxesDefault
|
||||
# There are different ways to render a bunch of linked objects:
|
||||
# - "list" (the default) renders them as a list (=a XHTML table);
|
||||
# - "menus" renders them as a series of popup menus, grouped by type.
|
||||
|
@ -886,20 +892,6 @@ class Ref(Field):
|
|||
raise Unauthorized("User can't write Ref field '%s' (%s)." % \
|
||||
(self.name, may.msg))
|
||||
|
||||
def changeOrderEnabled(self, obj):
|
||||
'''Is changeOrder enabled?'''
|
||||
if isinstance(self.changeOrder, bool):
|
||||
return self.changeOrder
|
||||
else:
|
||||
return self.callMethod(obj, self.changeOrder)
|
||||
|
||||
def checkboxesEnabled(self, obj):
|
||||
'''Are checkboxes enabled?'''
|
||||
if isinstance(self.checkboxes, bool):
|
||||
return self.checkboxes
|
||||
else:
|
||||
return self.callMethod(obj, self.checkboxes)
|
||||
|
||||
def getCbJsInit(self, obj):
|
||||
'''When checkboxes are enabled, this method defines a JS associative
|
||||
array (named "_appy_objs_cbs") that will store checkboxes' statuses.
|
||||
|
@ -909,19 +901,23 @@ class Ref(Field):
|
|||
Moreover, if self.link is "list", an additional array (named
|
||||
"_appy_poss_cbs") is defined for possible values.
|
||||
|
||||
Initial semantics of this (those) array(s) is as follows: if a key
|
||||
is present in it for a given linked object, it means that the
|
||||
checkbox is unchecked. All linked objects are thus selected by
|
||||
default. This semantics may be inverted: presence of a key may mean
|
||||
that the checkbox is checked. The current array semantics is stored
|
||||
in a variable named "_appy_objs_sem" (or "_appy_poss_sem") and may
|
||||
hold "unchecked" (initial semantics) or "checked" (inversed
|
||||
semantics). Inversing semantic allows to keep the array small even
|
||||
Semantics of this (those) array(s) can be as follows: if a key is
|
||||
present in it for a given linked object, it means that the
|
||||
checkbox is unchecked. In this case, all linked objects are selected
|
||||
by default. But the semantics can be inverted: presence of a key may
|
||||
mean that the checkbox is checked. The current array semantics is
|
||||
stored in a variable named "_appy_objs_sem" (or "_appy_poss_sem")
|
||||
and may hold "unchecked" (initial semantics) or "checked" (inverted
|
||||
semantics). Inverting semantics allows to keep the array small even
|
||||
when checking/unchecking all checkboxes.
|
||||
|
||||
The mentioned JS arrays and variables are stored as attributes of the
|
||||
DOM node representing this field.'''
|
||||
code = "\nnode['_appy_%s_cbs']={};\nnode['_appy_%s_sem']='unchecked';"
|
||||
# The initial semantics depends on the checkboxes default value.
|
||||
default = self.getAttribute(obj, 'checkboxesDefault') and \
|
||||
'unchecked' or 'checked'
|
||||
code = "\nnode['_appy_%%s_cbs']={};\nnode['_appy_%%s_sem']='%s';" % \
|
||||
default
|
||||
poss = (self.link == 'list') and (code % ('poss', 'poss')) or ''
|
||||
return "var node=document.getElementById('%s_%s');%s%s" % \
|
||||
(obj.id, self.name, code % ('objs', 'objs'), poss)
|
||||
|
@ -987,7 +983,11 @@ class Ref(Field):
|
|||
tied = tool.getObject(rq['targetUid'])
|
||||
exec 'self.%sObject(obj, tied, noSecurity=False)' % action
|
||||
else:
|
||||
# "link_many", "unlink_many", "delete_many"
|
||||
# "link_many", "unlink_many", "delete_many". As a preamble, perform
|
||||
# a security check once, instead of doing it on every object-level
|
||||
# operation.
|
||||
obj.allows(self.writePermission, raiseError=True)
|
||||
# Get the (un-)checked objects from the request.
|
||||
uids = rq['targetUid'].strip(',') or ();
|
||||
if uids: uids = uids.split(',')
|
||||
unchecked = rq['semantics'] == 'unchecked'
|
||||
|
@ -997,9 +997,10 @@ class Ref(Field):
|
|||
isObj = True
|
||||
else:
|
||||
# Get current values (uids)
|
||||
values = list(getattr(obj.aq_base, self.name, ()))
|
||||
values = getattr(obj.aq_base, self.name, ())
|
||||
isObj = False
|
||||
mustDelete = action == 'delete_many'
|
||||
# Collect the objects onto which the action must be performed.
|
||||
targets = []
|
||||
for value in values:
|
||||
uid = not isObj and value or value.uid
|
||||
if unchecked:
|
||||
|
@ -1008,16 +1009,27 @@ class Ref(Field):
|
|||
else:
|
||||
# Keep only objects being in uids.
|
||||
if uid not in uids: continue
|
||||
# (Un-)link or delete this object.
|
||||
tied = not isObj and tool.getObject(value) or value.o
|
||||
# Collect this object
|
||||
target = not isObj and tool.getObject(value) or value.o
|
||||
targets.append(target)
|
||||
# Perform the action on every target. Count the number of failed
|
||||
# operations.
|
||||
mustDelete = action == 'delete_many'
|
||||
failed = 0
|
||||
for target in targets:
|
||||
if mustDelete:
|
||||
if tied.mayDelete(): tied.delete()
|
||||
# Delete
|
||||
if target.mayDelete(): target.delete()
|
||||
else: failed += 1
|
||||
else:
|
||||
# Link or unlink
|
||||
exec 'self.%sObject(obj, tied, noSecurity=False)' % \
|
||||
action.split('_')[0]
|
||||
exec 'self.%sObject(obj, target)' % action.split('_')[0]
|
||||
urlBack = obj.getUrl(rq['HTTP_REFERER'])
|
||||
obj.say(obj.translate('action_done'))
|
||||
if not failed:
|
||||
msg = obj.translate('action_done')
|
||||
else:
|
||||
msg = obj.translate('action_partial', mapping={'nb':failed})
|
||||
obj.say(msg)
|
||||
tool.goto(urlBack)
|
||||
|
||||
def autoref(klass, field):
|
||||
|
|
|
@ -227,6 +227,10 @@ msgstr ""
|
|||
msgid "action_done"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Action could not be performed on ${nb} element(s)."
|
||||
msgid "action_partial"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Go to top"
|
||||
msgid "goto_first"
|
||||
msgstr ""
|
||||
|
|
|
@ -227,6 +227,10 @@ msgstr ""
|
|||
msgid "action_done"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Action could not be performed on ${nb} element(s)."
|
||||
msgid "action_partial"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Go to top"
|
||||
msgid "goto_first"
|
||||
msgstr ""
|
||||
|
|
|
@ -227,6 +227,10 @@ msgstr ""
|
|||
msgid "action_done"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Action could not be performed on ${nb} element(s)."
|
||||
msgid "action_partial"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Go to top"
|
||||
msgid "goto_first"
|
||||
msgstr "Zurück zum Anfang"
|
||||
|
|
|
@ -228,6 +228,10 @@ msgstr ""
|
|||
msgid "action_done"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Action could not be performed on ${nb} element(s)."
|
||||
msgid "action_partial"
|
||||
msgstr "Action could not be performed on ${nb} element(s)."
|
||||
|
||||
#. Default: "Go to top"
|
||||
msgid "goto_first"
|
||||
msgstr "Go to top"
|
||||
|
|
|
@ -227,6 +227,10 @@ msgstr "¿Está seguro?"
|
|||
msgid "action_done"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Action could not be performed on ${nb} element(s)."
|
||||
msgid "action_partial"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Go to top"
|
||||
msgid "goto_first"
|
||||
msgstr "Ir al inicio"
|
||||
|
|
|
@ -228,6 +228,10 @@ msgstr "Êtes-vous sûr?"
|
|||
msgid "action_done"
|
||||
msgstr "L'action a été réalisée."
|
||||
|
||||
#. Default: "Action could not be performed on ${nb} element(s)."
|
||||
msgid "action_partial"
|
||||
msgstr "L'action n'a pu être réalisée pour ${nb} élément(s)."
|
||||
|
||||
#. Default: "Go to top"
|
||||
msgid "goto_first"
|
||||
msgstr "Aller au début"
|
||||
|
|
|
@ -227,6 +227,10 @@ msgstr "È sicuro?"
|
|||
msgid "action_done"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Action could not be performed on ${nb} element(s)."
|
||||
msgid "action_partial"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Go to top"
|
||||
msgid "goto_first"
|
||||
msgstr "Andare all'inizio"
|
||||
|
|
|
@ -227,6 +227,10 @@ msgstr ""
|
|||
msgid "action_done"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Action could not be performed on ${nb} element(s)."
|
||||
msgid "action_partial"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Go to top"
|
||||
msgid "goto_first"
|
||||
msgstr "Ga naar het begin"
|
||||
|
|
Loading…
Reference in a new issue