[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)"/> navBaseCall, startNumber)"/>
</form>''') </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 # 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. # ref field according to the field that corresponds to this column.
pxSortIcons = Px(''' pxSortIcons = Px('''
@ -202,10 +219,13 @@ class Ref(Field):
# PX that displays referred objects as a list. # PX that displays referred objects as a list.
pxViewList = Px(''' 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 if="subLabel" class="discreet">:_(subLabel)</span>
(<span class="discreet">:totalNumber</span>) (<span class="discreet">:totalNumber</span>)
<x>:field.pxAdd</x> <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 --> <!-- The search button if field is queryable -->
<input if="objects and field.queryable" type="button" <input if="objects and field.queryable" type="button"
class="buttonSmall button" class="buttonSmall button"
@ -308,9 +328,9 @@ class Ref(Field):
not field.isBack and zobj.mayEdit(field.writePermission); not field.isBack and zobj.mayEdit(field.writePermission);
mayUnlink=False; mayUnlink=False;
mayAdd=False; mayAdd=False;
navBaseCall='askRefField(%s,%s,%s,%s,**v**)' % \ mayLink=False;
(q(ajaxHookId), q(zobj.absolute_url()), \ navBaseCall='askRefField(%s,%s,%s,**v**)' % \
q(field.name), q(innerRef)); (q(ajaxHookId), q(zobj.absolute_url()), q(innerRef));
changeOrder=False; changeOrder=False;
changeNumber=False; changeNumber=False;
checkboxes=field.getAttribute(zobj, 'checkboxes') and \ checkboxes=field.getAttribute(zobj, 'checkboxes') and \
@ -404,11 +424,12 @@ class Ref(Field):
mayEdit=not field.isBack and zobj.mayEdit(field.writePermission); mayEdit=not field.isBack and zobj.mayEdit(field.writePermission);
mayUnlink=mayEdit and field.getAttribute(zobj, 'unlink'); mayUnlink=mayEdit and field.getAttribute(zobj, 'unlink');
mayAdd=mayEdit and field.mayAdd(zobj, checkMayEdit=False); mayAdd=mayEdit and field.mayAdd(zobj, checkMayEdit=False);
mayLink=mayEdit and field.mayAdd(zobj, mode='link', \
checkMayEdit=False);
addConfirmMsg=field.addConfirm and \ addConfirmMsg=field.addConfirm and \
_('%s_addConfirm' % field.labelId) or ''; _('%s_addConfirm' % field.labelId) or '';
navBaseCall='askRefField(%s,%s,%s,%s,**v**)' % \ navBaseCall='askRefField(%s,%s,%s,**v**)' % \
(q(ajaxHookId), q(zobj.absolute_url()), \ (q(ajaxHookId), q(zobj.absolute_url()), q(innerRef));
q(field.name), q(innerRef));
changeOrder=mayEdit and field.getAttribute(zobj, 'changeOrder'); changeOrder=mayEdit and field.getAttribute(zobj, 'changeOrder');
numbered=field.isNumbered(zobj); numbered=field.isNumbered(zobj);
changeNumber=not inPickList and numbered and changeOrder and \ changeNumber=not inPickList and numbered and changeOrder and \
@ -446,17 +467,7 @@ class Ref(Field):
</select> </select>
<div if="not objects">-</div> <div if="not objects">-</div>
<!-- The button for opening the popup --> <!-- The button for opening the popup -->
<a target="appyIFrame" <x var="popupMode='repl'">:field.pxLink</x></x>''')
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>''')
pxEdit = Px(''' pxEdit = Px('''
<x if="(field.link) and (field.link != 'list')"> <x if="(field.link) and (field.link != 'list')">
@ -1079,17 +1090,21 @@ class Ref(Field):
# Link new objects # Link new objects
if objects: self.linkObject(appyObj, objects) if objects: self.linkObject(appyObj, objects)
def mayAdd(self, obj, checkMayEdit=True): def mayAdd(self, obj, mode='create', checkMayEdit=True):
'''May the user create a new referred object from p_obj via this Ref? '''May the user create (if p_mode == "create") or link
If p_checkMayEdit is False, it means that the condition of being (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 allowed to edit this Ref field has already been checked somewhere
else (it is always required, we just want to avoid checking it else (it is always required, we just want to avoid checking it
twice).''' twice).'''
# We can't (yet) do that on back references. # We can't (yet) do that on back references.
if self.isBack: return gutils.No('is_back') if self.isBack: return gutils.No('is_back')
# Check if this Ref is addable # Check if this Ref is addable/linkable.
if mode == 'create':
add = self.getAttribute(obj, 'add') add = self.getAttribute(obj, 'add')
if not add: return gutils.No('no_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? # Have we reached the maximum number of referred elements?
if self.multiplicity[1] != None: if self.multiplicity[1] != None:
refCount = len(getattr(obj, self.name, ())) refCount = len(getattr(obj, self.name, ()))
@ -1099,6 +1114,7 @@ class Ref(Field):
if not obj.mayEdit(self.writePermission): if not obj.mayEdit(self.writePermission):
return gutils.No('no_write_perm') return gutils.No('no_write_perm')
# May the user create instances of the referred class? # May the user create instances of the referred class?
if mode == 'create':
if not obj.getTool().userMayCreate(self.klass): if not obj.getTool().userMayCreate(self.klass):
return gutils.No('no_create_perm') return gutils.No('no_create_perm')
return True return True
@ -1294,6 +1310,14 @@ class Ref(Field):
res.append(tool.getObject(uid)) res.append(tool.getObject(uid))
return res 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): def onUiRequest(self, obj, rq):
'''This method is called when an action tied to this Ref field is '''This method is called when an action tied to this Ref field is
triggered from the user interface (link, unlink, link_many, triggered from the user interface (link, unlink, link_many,

View file

@ -189,11 +189,23 @@ class UiSearch:
self.translated = label and _(label) or '' self.translated = label and _(label) or ''
self.translatedDescr = labelDescr and _(labelDescr) 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 '''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.initiator = initiator
self.initiatorField = field 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): def showCheckboxes(self):
'''If checkboxes are enabled for this search (and if an initiator field '''If checkboxes are enabled for this search (and if an initiator field

View file

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

View file

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

View file

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