[gen] Ref field: first cmplete version of Ref with link='popup'.

This commit is contained in:
Gaetan Delannay 2014-07-28 12:29:16 +02:00
parent 0bcd0055a3
commit a45dfa8dd0
5 changed files with 93 additions and 45 deletions

View file

@ -168,6 +168,23 @@ class Ref(Field):
navBaseCall, startNumber)"/>
</form>''')
# Displays the button allowing to select from a popup objects to be linked
# via the Ref field.
pxLink = Px('''
<a target="appyIFrame"
var="tiedClassName=ztool.getPortalType(field.klass);
className=ztool.getPortalType(obj.klass)"
href=":'%s/query?className=%s&amp;search=%s:%s:%s&amp;popup=1' % \
(ztool.absolute_url(), tiedClassName, obj.uid, field.name, \
popupMode)">
<input type="button" class="buttonSmall button"
var="labelId= (popupMode=='repl') and 'search_button' or 'add_ref';
icon= (popupMode=='repl') and 'search' or 'add';
label=_(labelId)" value=":label"
style=":'%s;%s' % (url(icon,bg=True), ztool.getButtonWidth(label))"
onclick="openPopup('iframePopup')"/>
</a>''')
# This PX displays, in a cell header from a ref table, icons for sorting the
# ref field according to the field that corresponds to this column.
pxSortIcons = Px('''
@ -202,10 +219,13 @@ class Ref(Field):
# PX that displays referred objects as a list.
pxViewList = Px('''
<div if="not innerRef or mayAdd" style="margin-bottom: 4px">
<div if="not innerRef or mayAdd or mayLink" style="margin-bottom: 4px">
<span if="subLabel" class="discreet">:_(subLabel)</span>
(<span class="discreet">:totalNumber</span>)
<x>:field.pxAdd</x>
<!-- This button opens a popup for linking additional objects -->
<x if="mayLink and not inPickList"
var2="popupMode='add'">:field.pxLink</x>
<!-- The search button if field is queryable -->
<input if="objects and field.queryable" type="button"
class="buttonSmall button"
@ -308,9 +328,9 @@ class Ref(Field):
not field.isBack and zobj.mayEdit(field.writePermission);
mayUnlink=False;
mayAdd=False;
navBaseCall='askRefField(%s,%s,%s,%s,**v**)' % \
(q(ajaxHookId), q(zobj.absolute_url()), \
q(field.name), q(innerRef));
mayLink=False;
navBaseCall='askRefField(%s,%s,%s,**v**)' % \
(q(ajaxHookId), q(zobj.absolute_url()), q(innerRef));
changeOrder=False;
changeNumber=False;
checkboxes=field.getAttribute(zobj, 'checkboxes') and \
@ -404,11 +424,12 @@ class Ref(Field):
mayEdit=not field.isBack and zobj.mayEdit(field.writePermission);
mayUnlink=mayEdit and field.getAttribute(zobj, 'unlink');
mayAdd=mayEdit and field.mayAdd(zobj, checkMayEdit=False);
mayLink=mayEdit and field.mayAdd(zobj, mode='link', \
checkMayEdit=False);
addConfirmMsg=field.addConfirm and \
_('%s_addConfirm' % field.labelId) or '';
navBaseCall='askRefField(%s,%s,%s,%s,**v**)' % \
(q(ajaxHookId), q(zobj.absolute_url()), \
q(field.name), q(innerRef));
navBaseCall='askRefField(%s,%s,%s,**v**)' % \
(q(ajaxHookId), q(zobj.absolute_url()), q(innerRef));
changeOrder=mayEdit and field.getAttribute(zobj, 'changeOrder');
numbered=field.isNumbered(zobj);
changeNumber=not inPickList and numbered and changeOrder and \
@ -446,17 +467,7 @@ class Ref(Field):
</select>
<div if="not objects">-</div>
<!-- The button for opening the popup -->
<a target="appyIFrame"
var="tiedClassName=ztool.getPortalType(field.klass);
className=ztool.getPortalType(obj.klass)"
href=":'%s/query?className=%s&amp;search=%s:%s&amp;popup=1' % \
(ztool.absolute_url(), tiedClassName, obj.uid, field.name)">
<input type="button" class="buttonSmall button"
var="label=_('search_button')" value=":label"
style=":'%s; %s' % (url('search', bg=True), \
ztool.getButtonWidth(label))"
onclick="openPopup('iframePopup')"/></a>
</x>''')
<x var="popupMode='repl'">:field.pxLink</x></x>''')
pxEdit = Px('''
<x if="(field.link) and (field.link != 'list')">
@ -1079,17 +1090,21 @@ class Ref(Field):
# Link new objects
if objects: self.linkObject(appyObj, objects)
def mayAdd(self, obj, checkMayEdit=True):
'''May the user create a new referred object from p_obj via this Ref?
If p_checkMayEdit is False, it means that the condition of being
def mayAdd(self, obj, mode='create', checkMayEdit=True):
'''May the user create (if p_mode == "create") or link
(if mode == "link") (a) new referred object(s) from p_obj via this
Ref? If p_checkMayEdit is False, it means that the condition of being
allowed to edit this Ref field has already been checked somewhere
else (it is always required, we just want to avoid checking it
twice).'''
# We can't (yet) do that on back references.
if self.isBack: return gutils.No('is_back')
# Check if this Ref is addable
add = self.getAttribute(obj, 'add')
if not add: return gutils.No('no_add')
# Check if this Ref is addable/linkable.
if mode == 'create':
add = self.getAttribute(obj, 'add')
if not add: return gutils.No('no_add')
elif mode == 'link':
if (self.link != 'popup') or not self.isMultiValued(): return
# Have we reached the maximum number of referred elements?
if self.multiplicity[1] != None:
refCount = len(getattr(obj, self.name, ()))
@ -1099,8 +1114,9 @@ class Ref(Field):
if not obj.mayEdit(self.writePermission):
return gutils.No('no_write_perm')
# May the user create instances of the referred class?
if not obj.getTool().userMayCreate(self.klass):
return gutils.No('no_create_perm')
if mode == 'create':
if not obj.getTool().userMayCreate(self.klass):
return gutils.No('no_create_perm')
return True
def checkAdd(self, obj):
@ -1294,6 +1310,14 @@ class Ref(Field):
res.append(tool.getObject(uid))
return res
def onSelectFromPopup(self, obj):
'''This method is called on Ref fields with link="popup", when a user
has selected objects from the popup, to be added to existing tied
objects, from the view widget.'''
obj = obj.appy()
for tied in self.getPopupObjects(obj, obj.request, None):
self.linkObject(obj, tied, noSecurity=False)
def onUiRequest(self, obj, rq):
'''This method is called when an action tied to this Ref field is
triggered from the user interface (link, unlink, link_many,

View file

@ -189,11 +189,23 @@ class UiSearch:
self.translated = label and _(label) or ''
self.translatedDescr = labelDescr and _(labelDescr) or ''
def setInitiator(self, initiator, field):
def setInitiator(self, initiator, field, mode):
'''If the search is defined in an attribute Ref.select, we receive here
the p_initiator object and its Ref p_field.'''
the p_initiator object, its Ref p_field and the p_mode, that can be:
- "repl" if the objects selected in the popup will replace already
tied objects;
- "add" if those objects will be added to the already tied ones.
.'''
self.initiator = initiator
self.initiatorField = field
self.initiatorMode = mode
# "initiatorHook" is the ID of the initiator field's XHTML tag.
self.initiatorHook = '%s_%s' % (initiator.uid, field.name)
def getRootHookId(self):
'''If an initiator field is there, return the initiator hook.
Else, simply return the name of the search.'''
return getattr(self, 'initiatorHook', self.name)
def showCheckboxes(self):
'''If checkboxes are enabled for this search (and if an initiator field

View file

@ -736,7 +736,7 @@ class ToolMixin(BaseMixin):
elif ':' in name:
# The search is defined in a Ref field with link=popup. Get the
# search, the initiator object and the Ref field.
uid, ref = name.split(':')
uid, ref, mode = name.split(':')
initiator = self.getObject(uid, appy=True)
initiatorField = initiator.getField(ref)
res = getattr(initiator.klass, ref).select
@ -756,7 +756,7 @@ class ToolMixin(BaseMixin):
# Return a UiSearch if required.
if ui:
res = UiSearch(res, className, self)
if initiator: res.setInitiator(initiator, initiatorField)
if initiator: res.setInitiator(initiator, initiatorField, mode)
return res
def advancedSearchEnabledFor(self, klass):

View file

@ -249,11 +249,13 @@ function askObjectHistory(hookId, objectUrl, maxPerPage, startNumber) {
askAjaxChunk(hookId, 'GET', objectUrl, 'pxHistory', params);
}
function askRefField(hookId, objectUrl, fieldName, innerRef, startNumber,
action, actionParams){
function askRefField(hookId, objectUrl, innerRef, startNumber, action,
actionParams){
var hookElems = hookId.split('_');
var fieldName = hookElems[1];
// Sends an Ajax request for getting the content of a reference field.
var startKey = hookId + '_startNumber';
var scope = hookId.split('_').pop();
var scope = hookElems.pop();
var params = {'innerRef': innerRef, 'scope': scope};
params[startKey] = startNumber;
if (action) params['action'] = action;
@ -1028,7 +1030,7 @@ function onSelectDate(cal) {
}
}
function onSelectObjects(nodeId, objectUrl, sortKey, sortOrder,
function onSelectObjects(nodeId, objectUrl, mode, sortKey, sortOrder,
filterKey, filterValue){
/* Objects have been selected in a popup, to be linked via a Ref with
link='popup'. Get them. */
@ -1042,12 +1044,21 @@ function onSelectObjects(nodeId, objectUrl, sortKey, sortOrder,
}
// Close the popup.
closePopup('iframePopup');
/* Refresh the Ref edit widget to include the linked objects. All those
parameters are needed to replay the query in the popup. */
askField(':'+nodeId, objectUrl, 'edit', null, null, null, null, null,
{'selected': uids, 'semantics': semantics, 'sortKey': sortKey,
'sortOrder': sortOrder, 'filterKey': filterKey,
'filterValue': filterValue});
/* When refreshing the Ref field we will need to pass all those parameters,
for replaying the popup query. */
var params = {'selected': uids, 'semantics': semantics, 'sortKey': sortKey,
'sortOrder': sortOrder, 'filterKey': filterKey,
'filterValue': filterValue};
if (mode == 'repl') {
/* Link the selected objects (and unlink the potentially already linked
ones) and refresh the Ref edit widget. */
askField(':'+nodeId,objectUrl,'edit',null,null,null,null,null,params);
}
else {
// Link the selected objects and refresh the Ref view widget.
params['action'] = 'onSelectFromPopup';
askField(':'+nodeId,objectUrl,'view',null,null,null,null,null,params);
}
}
function onSelectObject(tdId, nodeId, objectUrl) {

View file

@ -417,9 +417,10 @@ class ToolWrapper(AbstractWrapper):
<input type="button" class="button"
var="label=_('object_link_many')"
value=":label"
onclick=":'onSelectObjects(%s,%s,%s,%s,%s,%s)' % (q(rootHookId), \
q(uiSearch.initiator.url),q(sortKey),q(sortOrder), \
q(filterKey), q(filterValue))"
onclick=":'onSelectObjects(%s,%s,%s,%s,%s,%s,%s)' % \
(q(rootHookId), q(uiSearch.initiator.url), \
q(uiSearch.initiatorMode), q(sortKey), q(sortOrder), \
q(filterKey), q(filterValue))"
style=":'%s; %s' % (url('linkMany', bg=True), \
ztool.getButtonWidth(label))"/>
</div>
@ -450,8 +451,8 @@ class ToolWrapper(AbstractWrapper):
_=ztool.translate;
className=req['className'];
searchName=req.get('search', '');
rootHookId=rootHookId|searchName.replace(':', '_');
uiSearch=uiSearch|ztool.getSearch(className,searchName,ui=True);
rootHookId=uiSearch.getRootHookId();
refInfo=ztool.getRefInfo();
refObject=refInfo[0];
refField=refInfo[1];
@ -537,7 +538,7 @@ class ToolWrapper(AbstractWrapper):
<div var="className=req['className'];
searchName=req.get('search', '');
uiSearch=ztool.getSearch(className, searchName, ui=True);
rootHookId=searchName.replace(':', '_');
rootHookId=uiSearch.getRootHookId();
cssJs=None"
id=":rootHookId">
<script type="text/javascript">:uiSearch.search.getCbJsInit(rootHookId)