[gen] More work on Refs with popup='true'.

This commit is contained in:
Gaetan Delannay 2014-07-25 15:07:31 +02:00
parent a14bff45a7
commit 0bcd0055a3
7 changed files with 80 additions and 37 deletions

View file

@ -286,11 +286,8 @@ class Field:
def isMultiValued(self): def isMultiValued(self):
'''Does this type definition allow to define multiple values?''' '''Does this type definition allow to define multiple values?'''
res = False
maxOccurs = self.multiplicity[1] maxOccurs = self.multiplicity[1]
if (maxOccurs == None) or (maxOccurs > 1): return (maxOccurs == None) or (maxOccurs > 1)
res = True
return res
def isSortable(self, usage): def isSortable(self, usage):
'''Can fields of this type be used for sorting purposes (when sorting '''Can fields of this type be used for sorting purposes (when sorting

View file

@ -1273,7 +1273,7 @@ class Ref(Field):
# No object can be selected if the popup has not been opened yet. # No object can be selected if the popup has not been opened yet.
if 'semantics' not in rq: if 'semantics' not in rq:
# In this case, display already linked objects if any. # In this case, display already linked objects if any.
if not obj.isEmpty(self.name): return getattr(obj, self.name) if not obj.isEmpty(self.name): return self.getValue(obj.o)
return res return res
uids = rq['selected'].split(',') uids = rq['selected'].split(',')
tool = obj.tool tool = obj.tool

View file

@ -24,16 +24,16 @@ from group import Group
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
class Search: class Search:
'''Used for specifying a search for a given class.''' '''Used for specifying a search for a given class.'''
def __init__(self, name, group=None, sortBy='', sortOrder='asc', limit=None, def __init__(self, name, group=None, sortBy='', sortOrder='asc',
default=False, colspan=1, translated=None, show=True, maxPerPage=30, default=False, colspan=1, translated=None,
translatedDescr=None, checkboxes=False, checkboxesDefault=True, show=True, translatedDescr=None, checkboxes=False,
**fields): checkboxesDefault=True, **fields):
self.name = name self.name = name
# Searches may be visually grouped in the portlet. # Searches may be visually grouped in the portlet.
self.group = Group.get(group) self.group = Group.get(group)
self.sortBy = sortBy self.sortBy = sortBy
self.sortOrder = sortOrder self.sortOrder = sortOrder
self.limit = limit self.maxPerPage = maxPerPage
# If this search is the default one, it will be triggered by clicking # If this search is the default one, it will be triggered by clicking
# on main link. # on main link.
self.default = default self.default = default
@ -188,4 +188,19 @@ class UiSearch:
_ = tool.translate _ = tool.translate
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):
'''If the search is defined in an attribute Ref.select, we receive here
the p_initiator object and its Ref p_field.'''
self.initiator = initiator
self.initiatorField = field
def showCheckboxes(self):
'''If checkboxes are enabled for this search (and if an initiator field
is there), they must be visible only if the initiator field is
multivalued. Indeed, if it is not the case, it has no sense to select
multiple objects. But in this case, we still want checkboxes to be in
the DOM because they store object UIDs.'''
if not self.search.checkboxes: return
return not self.initiator or self.initiatorField.isMultiValued()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -354,14 +354,17 @@ class ToolMixin(BaseMixin):
# Use index "Allowed" if noSecurity is False # Use index "Allowed" if noSecurity is False
if not noSecurity: params['Allowed'] = self.getAllowedValue() if not noSecurity: params['Allowed'] = self.getAllowedValue()
brains = self.getPath("/catalog")(**params) brains = self.getPath("/catalog")(**params)
if brainsOnly: # Compute maxResults
# Return brains only.
if not maxResults or (maxResults == 'NO_LIMIT'): return brains
else: return brains[:maxResults]
if not maxResults: if not maxResults:
if refField: maxResults = refField.maxPerPage if refField: maxResults = refField.maxPerPage
else: maxResults = self.appy().numberOfResultsPerPage else: maxResults = search.maxPerPage or \
elif maxResults == 'NO_LIMIT': maxResults = None self.appy().numberOfResultsPerPage
elif maxResults == 'NO_LIMIT':
maxResults = None
# Return brains only if required.
if brainsOnly:
if not maxResults: return brains
else: return brains[:maxResults]
res = gutils.SomeObjects(brains, maxResults, startNumber, res = gutils.SomeObjects(brains, maxResults, startNumber,
noSecurity=noSecurity) noSecurity=noSecurity)
res.brainsToObjects() res.brainsToObjects()
@ -725,14 +728,18 @@ class ToolMixin(BaseMixin):
def getSearch(self, className, name, ui=False): def getSearch(self, className, name, ui=False):
'''Gets the Search instance (or a UiSearch instance if p_ui is True) '''Gets the Search instance (or a UiSearch instance if p_ui is True)
corresponding to the search named p_name, on class p_className.''' corresponding to the search named p_name, on class p_className.'''
initiator = None
if name == 'customSearch': if name == 'customSearch':
# It is a custom search whose parameters are in the session. # It is a custom search whose parameters are in the session.
fields = self.REQUEST.SESSION['searchCriteria'] fields = self.REQUEST.SESSION['searchCriteria']
res = Search('customSearch', **fields) res = Search('customSearch', **fields)
elif ':' in name: elif ':' in name:
# The search is defined in a Ref field with link=popup # The search is defined in a Ref field with link=popup. Get the
refObject, ref = name.split(':') # search, the initiator object and the Ref field.
res = getattr(self.getObject(refObject,appy=True).klass,ref).select uid, ref = name.split(':')
initiator = self.getObject(uid, appy=True)
initiatorField = initiator.getField(ref)
res = getattr(initiator.klass, ref).select
elif name: elif name:
appyClass = self.getAppyClass(className) appyClass = self.getAppyClass(className)
# Search among static searches # Search among static searches
@ -747,7 +754,9 @@ class ToolMixin(BaseMixin):
# It is the search for every instance of p_className # It is the search for every instance of p_className
res = Search('allSearch') res = Search('allSearch')
# Return a UiSearch if required. # Return a UiSearch if required.
if ui: res = UiSearch(res, className, self) if ui:
res = UiSearch(res, className, self)
if initiator: res.setInitiator(initiator, initiatorField)
return res return res
def advancedSearchEnabledFor(self, klass): def advancedSearchEnabledFor(self, klass):

View file

@ -1041,7 +1041,6 @@ function onSelectObjects(nodeId, objectUrl, sortKey, sortOrder,
return; return;
} }
// Close the popup. // Close the popup.
var parent = window.parent;
closePopup('iframePopup'); closePopup('iframePopup');
/* Refresh the Ref edit widget to include the linked objects. All those /* Refresh the Ref edit widget to include the linked objects. All those
parameters are needed to replay the query in the popup. */ parameters are needed to replay the query in the popup. */
@ -1051,6 +1050,25 @@ function onSelectObjects(nodeId, objectUrl, sortKey, sortOrder,
'filterValue': filterValue}); 'filterValue': filterValue});
} }
function onSelectObject(tdId, nodeId, objectUrl) {
/* In a Ref field with link="popup", a single object has been clicked. If
multiple objects can be selected, simply update the corresponding checkbox
status. Else, close the popup and return the selected object. p_tdId is
the ID of the td that contains the checkbox. */
var td = document.getElementById(tdId);
// If the td is visible, simply click the checkbox.
var checkbox = td.getElementsByTagName('input')[0];
if (td.style.display == 'table-cell') { checkbox.click(); }
else {
/* Close the popup and directly refresh the initiator field with the
selected object. */
var uids=checkbox.value;
closePopup('iframePopup');
askField(':'+nodeId, objectUrl, 'edit', null, null, null, null, null,
{'selected': uids, 'semantics': 'checked'});
}
}
// Sets the focus on the correct element in some page. // Sets the focus on the correct element in some page.
function initFocus(pageId){ function initFocus(pageId){
var id = pageId + '_title'; var id = pageId + '_title';

View file

@ -317,15 +317,18 @@ class ToolWrapper(AbstractWrapper):
var2="navInfo='search.%s.%s.%d.%d' % \ var2="navInfo='search.%s.%s.%d.%d' % \
(className, searchName, startNumber+currentNumber, totalNumber); (className, searchName, startNumber+currentNumber, totalNumber);
cssClass=zobj.getCssFor('title')"> cssClass=zobj.getCssFor('title')">
<x>::zobj.getSupTitle(navInfo)</x> <x var="sup=zobj.getSupTitle(navInfo)" if="sup">::sup</x>
<a if="enableLinks" class=":cssClass" <a if="enableLinks" class=":cssClass"
var2="linkInPopup=inPopup or (target.target != '_self')" var2="linkInPopup=inPopup or (target.target != '_self')"
target=":target.target" onclick=":target.openPopup" target=":target.target" onclick=":target.openPopup"
href=":zobj.getUrl(nav=navInfo, page=zobj.getDefaultViewPage(), \ href=":zobj.getUrl(nav=navInfo, page=zobj.getDefaultViewPage(), \
inPopup=linkInPopup)">:zobj.Title()</a><span inPopup=linkInPopup)">:zobj.Title()</a>
if="not enableLinks" class=":cssClass">:zobj.Title()</span><span <span if="not enableLinks"
style=":showSubTitles and 'display:inline' or 'display:none'" class=":not checkboxes and cssClass or ('%s clickable' % cssClass)"
name="subTitle">::zobj.getSubTitle()</span> onclick=":checkboxes and ('onSelectObject(%s,%s,%s)' % (q(cbId), \
q(rootHookId), q(uiSearch.initiator.url))) or ''">:obj.title</span>
<span style=":showSubTitles and 'display:inline' or 'display:none'"
name="subTitle" var="sub=zobj.getSubTitle()" if="sub">::sub</span>
<!-- Actions --> <!-- Actions -->
<table class="noStyle" if="not inPopup and zobj.mayAct()"> <table class="noStyle" if="not inPopup and zobj.mayAct()">
@ -368,7 +371,9 @@ class ToolWrapper(AbstractWrapper):
pxQueryResultList = Px(''' pxQueryResultList = Px('''
<x var="showHeaders=showHeaders|True; <x var="showHeaders=showHeaders|True;
checkboxes=uiSearch.search.checkboxes; checkboxes=uiSearch.search.checkboxes;
checkboxesId=rootHookId + '_objs'"> checkboxesId=rootHookId + '_objs';
cbShown=uiSearch.showCheckboxes();
cbDisplay=cbShown and 'display:table-cell' or 'display:none'">
<table class="list" width="100%"> <table class="list" width="100%">
<!-- Headers, with filters and sort arrows --> <!-- Headers, with filters and sort arrows -->
<tr if="showHeaders"> <tr if="showHeaders">
@ -380,7 +385,7 @@ class ToolWrapper(AbstractWrapper):
<x>::ztool.truncateText(_(field.labelId))</x> <x>::ztool.truncateText(_(field.labelId))</x>
<x>:tool.pxSortAndFilter</x><x>:tool.pxShowDetails</x> <x>:tool.pxSortAndFilter</x><x>:tool.pxShowDetails</x>
</th> </th>
<th if="checkboxes" class="cbCell"> <th if="checkboxes" class="cbCell" style=":cbDisplay">
<img src=":url('checkall')" class="clickable" <img src=":url('checkall')" class="clickable"
title=":_('check_uncheck')" title=":_('check_uncheck')"
onclick=":'toggleAllCbs(%s)' % q(checkboxesId)"/> onclick=":'toggleAllCbs(%s)' % q(checkboxesId)"/>
@ -391,30 +396,29 @@ class ToolWrapper(AbstractWrapper):
<tr if="not zobjects"> <tr if="not zobjects">
<td colspan=":len(columns)+1">:_('query_no_result')</td> <td colspan=":len(columns)+1">:_('query_no_result')</td>
</tr> </tr>
<tr for="zobj in zobjects" id="query_row" valign="top" <tr for="zobj in zobjects" valign="top"
var2="@currentNumber=currentNumber + 1; var2="@currentNumber=currentNumber + 1;
obj=zobj.appy(); mayView=zobj.mayView()" obj=zobj.appy(); mayView=zobj.mayView();
cbId='%s_%s' % (checkboxesId, currentNumber)"
class=":loop.zobj.odd and 'even' or 'odd'"> class=":loop.zobj.odd and 'even' or 'odd'">
<td for="column in columns" <td for="column in columns"
var2="field=column.field" id=":'field_%s' % field.name" var2="field=column.field" id=":'field_%s' % field.name"
width=":column.width" width=":column.width"
align=":column.align">:tool.pxQueryField</td> align=":column.align">:tool.pxQueryField</td>
<!-- A checkbox if required --> <!-- A checkbox if required -->
<td if="checkboxes" class="cbCell"> <td if="checkboxes" class="cbCell" id=":cbId" style=":cbDisplay">
<input type="checkbox" name=":checkboxesId" checked="checked" <input type="checkbox" name=":checkboxesId" checked="checked"
value=":zobj.id" onclick="toggleCb(this)"/> value=":zobj.id" onclick="toggleCb(this)"/>
</td> </td>
</tr> </tr>
</table> </table>
<!-- The button for selecting objects and closing the popup. --> <!-- The button for selecting objects and closing the popup. -->
<div if="inPopup" align=":dright"> <div if="inPopup and cbShown" align=":dright">
<input type="button" class="button" <input type="button" class="button"
var="label=_('object_link_many'); var="label=_('object_link_many')"
initiator=ztool.getObject(searchName.split(':')[0], \
appy=True)"
value=":label" value=":label"
onclick=":'onSelectObjects(%s,%s,%s,%s,%s,%s)' % (q(rootHookId), \ onclick=":'onSelectObjects(%s,%s,%s,%s,%s,%s)' % (q(rootHookId), \
q(initiator.url), q(sortKey), q(sortOrder), \ q(uiSearch.initiator.url),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))"/>

View file

@ -949,7 +949,7 @@ class AbstractWrapper(object):
contentType = tool.getPortalType(klass) contentType = tool.getPortalType(klass)
search = Search('customSearch', **fields) search = Search('customSearch', **fields)
res = tool.executeQuery(contentType, search=search, brainsOnly=True, res = tool.executeQuery(contentType, search=search, brainsOnly=True,
noSecurity=noSecurity) noSecurity=noSecurity, maxResults='NO_LIMIT')
if res: return res._len # It is a LazyMap instance if res: return res._len # It is a LazyMap instance
else: return 0 else: return 0