[gen] Allow Refs with render='menus' to appear in layout 'buttons'.
This commit is contained in:
parent
34cafcdbc1
commit
d9a89f7ad5
|
@ -119,7 +119,7 @@ class Field:
|
||||||
|
|
||||||
# Button for showing changes to the field
|
# Button for showing changes to the field
|
||||||
pxChanges = Px('''
|
pxChanges = Px('''
|
||||||
<x if="zobj.hasHistory(name)">
|
<div if="zobj.hasHistory(name)" style="margin-bottom: 5px">
|
||||||
<!-- Button for showing the field version containing changes -->
|
<!-- Button for showing the field version containing changes -->
|
||||||
<input if="not showChanges"
|
<input if="not showChanges"
|
||||||
var2="label=_('changes_show');
|
var2="label=_('changes_show');
|
||||||
|
@ -133,7 +133,8 @@ class Field:
|
||||||
css=ztool.getButtonCss(label)" type="button" class=":css"
|
css=ztool.getButtonCss(label)" type="button" class=":css"
|
||||||
value=":label" style=":url('changesNo', bg=True)"
|
value=":label" style=":url('changesNo', bg=True)"
|
||||||
onclick=":'askField(%s,%s,%s,null,%s)' % \
|
onclick=":'askField(%s,%s,%s,null,%s)' % \
|
||||||
(q(tagId), q(obj.url), q('view'), q('False'))"/></x>''')
|
(q(tagId), q(obj.url), q('view'), q('False'))"/>
|
||||||
|
</div>''')
|
||||||
|
|
||||||
def __init__(self, validator, multiplicity, default, show, page, group,
|
def __init__(self, validator, multiplicity, default, show, page, group,
|
||||||
layouts, move, indexed, mustIndex, searchable,
|
layouts, move, indexed, mustIndex, searchable,
|
||||||
|
@ -374,6 +375,22 @@ class Field:
|
||||||
# explicitly be returned by the show method.
|
# explicitly be returned by the show method.
|
||||||
if layoutType != 'buttons': return bool(res)
|
if layoutType != 'buttons': return bool(res)
|
||||||
|
|
||||||
|
def isRenderable(self, layoutType):
|
||||||
|
'''In some contexts, computing m_isShowable can be a performance
|
||||||
|
problem. For example, when showing fields of some object on layout
|
||||||
|
"buttons", there are plenty of fields that simply can't be shown on
|
||||||
|
this kind of layout: it is no worth computing m_isShowable for those
|
||||||
|
fields. m_isRenderable is meant to define light conditions to
|
||||||
|
determine, before calling m_isShowable, if some field has a chance to
|
||||||
|
be shown or not.
|
||||||
|
|
||||||
|
In other words, m_isRenderable defines a "structural" condition,
|
||||||
|
independent of any object, while m_isShowable defines a contextual
|
||||||
|
condition, depending on some object.'''
|
||||||
|
# Most fields are not renderable on layout "buttons"
|
||||||
|
if layoutType == 'buttons': return
|
||||||
|
return True
|
||||||
|
|
||||||
def isClientVisible(self, obj):
|
def isClientVisible(self, obj):
|
||||||
'''This method returns True if this field is visible according to
|
'''This method returns True if this field is visible according to
|
||||||
master/slave relationships.'''
|
master/slave relationships.'''
|
||||||
|
|
|
@ -125,6 +125,9 @@ class Action(Field):
|
||||||
if layoutType == 'edit': return
|
if layoutType == 'edit': return
|
||||||
return Field.isShowable(self, obj, layoutType)
|
return Field.isShowable(self, obj, layoutType)
|
||||||
|
|
||||||
|
# Action fields can a priori be shown on every layout, "buttons" included
|
||||||
|
def isRenderable(self, layoutType): return True
|
||||||
|
|
||||||
def onUiRequest(self, obj, rq):
|
def onUiRequest(self, obj, rq):
|
||||||
'''This method is called when a user triggers the execution of this
|
'''This method is called when a user triggers the execution of this
|
||||||
action from the user interface.'''
|
action from the user interface.'''
|
||||||
|
|
|
@ -79,8 +79,8 @@ class Ref(Field):
|
||||||
# This PX displays icons for triggering actions on a given referenced object
|
# This PX displays icons for triggering actions on a given referenced object
|
||||||
# (edit, delete, etc).
|
# (edit, delete, etc).
|
||||||
pxObjectActions = Px('''
|
pxObjectActions = Px('''
|
||||||
<div if="field.showActions"
|
<div if="field.showActions" class="objectActions"
|
||||||
style=":'display:%s; margin-bottom:2px' % field.showActions">
|
style=":'display:%s' % field.showActions">
|
||||||
<!-- Arrows for moving objects up or down -->
|
<!-- Arrows for moving objects up or down -->
|
||||||
<x if="(totalNumber >1) and changeOrder and not inPickList \
|
<x if="(totalNumber >1) and changeOrder and not inPickList \
|
||||||
and not inMenu"
|
and not inMenu"
|
||||||
|
@ -134,12 +134,12 @@ class Ref(Field):
|
||||||
var2="targetObj=tied.o; buttonsMode='small'">:tied.pxTransitions</x>
|
var2="targetObj=tied.o; buttonsMode='small'">:tied.pxTransitions</x>
|
||||||
<!-- Fields (actions) defined with layout "buttons" -->
|
<!-- Fields (actions) defined with layout "buttons" -->
|
||||||
<x if="not inPopup"
|
<x if="not inPopup"
|
||||||
var2="fields=tied.o.getAppyTypes('buttons', 'main', type='Action');
|
var2="fields=tied.o.getAppyTypes('buttons', 'main');
|
||||||
layoutType='view';
|
layoutType='cell';
|
||||||
zobj=tied.o">
|
zobj=tied.o">
|
||||||
<!-- Call pxView and not pxRender to avoid having a table -->
|
<!-- Call pxCell and not pxRender to avoid having a table -->
|
||||||
<x for="field in fields"
|
<x for="field in fields"
|
||||||
var2="name=field.name; smallButtons=True">:field.pxView</x>
|
var2="name=field.name; smallButtons=True">:field.pxCell</x>
|
||||||
</x>
|
</x>
|
||||||
</div>''')
|
</div>''')
|
||||||
|
|
||||||
|
@ -159,8 +159,10 @@ class Ref(Field):
|
||||||
value=":(inPopup or (target.target != '_self')) and '1' or '0'"/>
|
value=":(inPopup or (target.target != '_self')) and '1' or '0'"/>
|
||||||
<input
|
<input
|
||||||
type=":(field.addConfirm or field.noForm) and 'button' or 'submit'"
|
type=":(field.addConfirm or field.noForm) and 'button' or 'submit'"
|
||||||
var="label=_('add_ref'); css=ztool.getButtonCss(label)" class=":css"
|
var="addLabel=_('add_ref');
|
||||||
value=":label" style=":url('add', bg=True)"
|
label=inMenu and tiedClassLabel or addLabel;
|
||||||
|
css=ztool.getButtonCss(label)" class=":css"
|
||||||
|
value=":label" style=":url('add', bg=True)" title=":addLabel"
|
||||||
onclick=":field.getOnAdd(q, formName, addConfirmMsg, target, \
|
onclick=":field.getOnAdd(q, formName, addConfirmMsg, target, \
|
||||||
navBaseCall, startNumber)"/>
|
navBaseCall, startNumber)"/>
|
||||||
</form>''')
|
</form>''')
|
||||||
|
@ -169,7 +171,7 @@ class Ref(Field):
|
||||||
# via the Ref field.
|
# via the Ref field.
|
||||||
pxLink = Px('''
|
pxLink = Px('''
|
||||||
<a target="appyIFrame"
|
<a target="appyIFrame"
|
||||||
var="tiedClassName=ztool.getPortalType(field.klass);
|
var="tiedClassName=tiedClassName|ztool.getPortalType(field.klass);
|
||||||
className=ztool.getPortalType(obj.klass)"
|
className=ztool.getPortalType(obj.klass)"
|
||||||
href=":'%s/query?className=%s&search=%s:%s:%s&popup=1' % \
|
href=":'%s/query?className=%s&search=%s:%s:%s&popup=1' % \
|
||||||
(ztool.absolute_url(), tiedClassName, obj.uid, field.name, \
|
(ztool.absolute_url(), tiedClassName, obj.uid, field.name, \
|
||||||
|
@ -327,6 +329,7 @@ class Ref(Field):
|
||||||
batchSize=info.batchSize;
|
batchSize=info.batchSize;
|
||||||
batchNumber=len(objects);
|
batchNumber=len(objects);
|
||||||
tiedClassName=tiedClassName|ztool.getPortalType(field.klass);
|
tiedClassName=tiedClassName|ztool.getPortalType(field.klass);
|
||||||
|
tiedClassLabel=tiedClassLabel|_(tiedClassName);
|
||||||
target=ztool.getLinksTargetInfo(field.klass);
|
target=ztool.getLinksTargetInfo(field.klass);
|
||||||
mayEdit=mayEdit|\
|
mayEdit=mayEdit|\
|
||||||
not field.isBack and zobj.mayEdit(field.writePermission);
|
not field.isBack and zobj.mayEdit(field.writePermission);
|
||||||
|
@ -351,15 +354,12 @@ class Ref(Field):
|
||||||
<x>:len(menu.objects)</x>''')
|
<x>:len(menu.objects)</x>''')
|
||||||
|
|
||||||
pxViewMenus = Px('''
|
pxViewMenus = Px('''
|
||||||
<x var2="dtc='display: table-cell'; inMenu=True">
|
<x var2="inMenu=True">
|
||||||
<!-- No object is present -->
|
|
||||||
<div if="not objects" style=":'padding-left: 3px; %s' % dtc"
|
|
||||||
class="discreet">-</div>
|
|
||||||
|
|
||||||
<!-- One menu for every object type -->
|
<!-- One menu for every object type -->
|
||||||
<div for="menu in field.getLinkedObjectsByMenu(obj, objects)"
|
<div for="menu in field.getLinkedObjectsByMenu(obj, objects)"
|
||||||
style=":not loop.menu.last and ('%s;padding-right:4px') % dtc or dtc">
|
class="inline"
|
||||||
<div class="dropdownMenu"
|
style=":not loop.menu.last and 'padding-right:4px' or ''">
|
||||||
|
<div class="dropdownMenu inline"
|
||||||
var2="dropdownId='%s_%s_%d' % (zobj.id, name, loop.menu.nb);
|
var2="dropdownId='%s_%s_%d' % (zobj.id, name, loop.menu.nb);
|
||||||
singleObject=len(menu.objects) == 1"
|
singleObject=len(menu.objects) == 1"
|
||||||
onmouseover=":'toggleDropdown(%s)' % q(dropdownId)"
|
onmouseover=":'toggleDropdown(%s)' % q(dropdownId)"
|
||||||
|
@ -424,6 +424,7 @@ class Ref(Field):
|
||||||
batchNumber=len(objects);
|
batchNumber=len(objects);
|
||||||
folder=zobj.getCreateFolder();
|
folder=zobj.getCreateFolder();
|
||||||
tiedClassName=ztool.getPortalType(field.klass);
|
tiedClassName=ztool.getPortalType(field.klass);
|
||||||
|
tiedClassLabel=_(tiedClassName);
|
||||||
target=ztool.getLinksTargetInfo(field.klass);
|
target=ztool.getLinksTargetInfo(field.klass);
|
||||||
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');
|
||||||
|
@ -691,10 +692,11 @@ class Ref(Field):
|
||||||
# There are different ways to render a bunch of linked objects:
|
# There are different ways to render a bunch of linked objects:
|
||||||
# - "list" (the default) renders them as a list (=a XHTML table);
|
# - "list" (the default) renders them as a list (=a XHTML table);
|
||||||
# - "menus" renders them as a series of popup menus, grouped by type.
|
# - "menus" renders them as a series of popup menus, grouped by type.
|
||||||
# Note that render mode "menus" will only be applied in "cell" layouts.
|
# Note that render mode "menus" will only be applied in "cell" and
|
||||||
# Indeed, we need to keep the "list" rendering in the "view" layout
|
# "buttons" layouts. Indeed, we need to keep the "list" rendering in
|
||||||
# because the "menus" rendering is minimalist and does not allow to
|
# the "view" layout because the "menus" rendering is minimalist and does
|
||||||
# perform all operations on linked objects (add, move, delete, edit...);
|
# not allow to perform all operations on linked objects (add, move,
|
||||||
|
# delete, edit...);
|
||||||
# - "minimal" renders a list of comma-separated, not-even-clickable,
|
# - "minimal" renders a list of comma-separated, not-even-clickable,
|
||||||
# data about the tied objects (according to shownInfo).
|
# data about the tied objects (according to shownInfo).
|
||||||
self.render = render
|
self.render = render
|
||||||
|
@ -755,6 +757,12 @@ class Ref(Field):
|
||||||
else: return getattr(obj.aq_base, self.name, None)
|
else: return getattr(obj.aq_base, self.name, None)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def isRenderable(self, layoutType):
|
||||||
|
'''Only Ref fields with render = "menus" can be rendered on "button"
|
||||||
|
layouts.'''
|
||||||
|
if layoutType == 'buttons': return self.render == 'menus'
|
||||||
|
return True
|
||||||
|
|
||||||
def getValue(self, obj, appy=True, noListIfSingleObj=False,
|
def getValue(self, obj, appy=True, noListIfSingleObj=False,
|
||||||
startNumber=None, someObjects=False):
|
startNumber=None, someObjects=False):
|
||||||
'''Returns the objects linked to p_obj through this Ref field. It
|
'''Returns the objects linked to p_obj through this Ref field. It
|
||||||
|
@ -924,7 +932,7 @@ class Ref(Field):
|
||||||
menuIndex = menuIds[menuId]
|
menuIndex = menuIds[menuId]
|
||||||
res[menuIndex].objects.append(tied)
|
res[menuIndex].objects.append(tied)
|
||||||
else:
|
else:
|
||||||
# A new menu.
|
# A new menu
|
||||||
menu = Object(id=menuId, objects=[tied])
|
menu = Object(id=menuId, objects=[tied])
|
||||||
res.append(menu)
|
res.append(menu)
|
||||||
menuIds[menuId] = len(res) - 1
|
menuIds[menuId] = len(res) - 1
|
||||||
|
@ -1214,6 +1222,13 @@ class Ref(Field):
|
||||||
q(addConfirmMsg))
|
q(addConfirmMsg))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def getAddLabel(self, obj, addLabel, tiedClassLabel, inMenu):
|
||||||
|
'''Gets the label of the button allowing to add a new tied object. If
|
||||||
|
p_inMenu, the label must contain the name of the class whose instance
|
||||||
|
will be created by clincking on the button.'''
|
||||||
|
if not inMenu: return obj.translate('add_ref')
|
||||||
|
return tiedClassLabel
|
||||||
|
|
||||||
def mayUnlinkElement(self, obj, tied, raiseError=False):
|
def mayUnlinkElement(self, obj, tied, raiseError=False):
|
||||||
'''May we unlink from this Ref field this specific p_tied object?'''
|
'''May we unlink from this Ref field this specific p_tied object?'''
|
||||||
if not self.unlinkElement: return True
|
if not self.unlinkElement: return True
|
||||||
|
|
|
@ -818,6 +818,7 @@ class BaseMixin:
|
||||||
for field in self.getAllAppyTypes():
|
for field in self.getAllAppyTypes():
|
||||||
if pageName and (field.page.name != pageName): continue
|
if pageName and (field.page.name != pageName): continue
|
||||||
if type and (field.type != type): continue
|
if type and (field.type != type): continue
|
||||||
|
if not field.isRenderable(layoutType): continue
|
||||||
if not field.isShowable(self, layoutType): continue
|
if not field.isShowable(self, layoutType): continue
|
||||||
res.append(field)
|
res.append(field)
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -110,7 +110,8 @@ td.search { padding-top: 8px }
|
||||||
.dropdownMenu { cursor: pointer; font-size: 93%; position: relative }
|
.dropdownMenu { cursor: pointer; font-size: 93%; position: relative }
|
||||||
.dropdown a:hover { text-decoration: underline }
|
.dropdown a:hover { text-decoration: underline }
|
||||||
.addForm { display: inline }
|
.addForm { display: inline }
|
||||||
.addFormMenu { display: table-cell; padding-left: 7px }
|
.addFormMenu { display: inline; padding: 0 5px 0 3px }
|
||||||
|
.inline { display: inline }
|
||||||
.list { margin-bottom: 3px }
|
.list { margin-bottom: 3px }
|
||||||
.list td, .list th { border: 3px solid #ededed; color: grey;
|
.list td, .list th { border: 3px solid #ededed; color: grey;
|
||||||
padding: 3px 5px 3px 5px }
|
padding: 3px 5px 3px 5px }
|
||||||
|
@ -185,3 +186,4 @@ td.search { padding-top: 8px }
|
||||||
margin: 0 2px 0 4px; font-family: monospace }
|
margin: 0 2px 0 4px; font-family: monospace }
|
||||||
.highlight { background-color: yellow }
|
.highlight { background-color: yellow }
|
||||||
.globalActions { margin-bottom: 4px }
|
.globalActions { margin-bottom: 4px }
|
||||||
|
.objectActions { margin: 2px 0 }
|
||||||
|
|
|
@ -178,7 +178,7 @@ def callMethod(obj, method, klass=None, cache=True):
|
||||||
method = method.__get__(klass)
|
method = method.__get__(klass)
|
||||||
elif methodType == 'instancemethod':
|
elif methodType == 'instancemethod':
|
||||||
method = method.im_func
|
method = method.im_func
|
||||||
# Call the method if cache is not needed.
|
# Call the method if cache is not needed
|
||||||
if not cache: return method(obj)
|
if not cache: return method(obj)
|
||||||
# If first arg of method is named "tool" instead of the traditional "self",
|
# If first arg of method is named "tool" instead of the traditional "self",
|
||||||
# we cheat and will call the method with the tool as first arg. This will
|
# we cheat and will call the method with the tool as first arg. This will
|
||||||
|
@ -208,7 +208,6 @@ def callMethod(obj, method, klass=None, cache=True):
|
||||||
rq.methodCache[key] = res
|
rq.methodCache[key] = res
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
# Functions for manipulating the authentication cookie -------------------------
|
# Functions for manipulating the authentication cookie -------------------------
|
||||||
def readCookie(request):
|
def readCookie(request):
|
||||||
'''Returns the tuple (login, password) read from the authentication
|
'''Returns the tuple (login, password) read from the authentication
|
||||||
|
|
|
@ -363,7 +363,7 @@ class ToolWrapper(AbstractWrapper):
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<div if="not inPopup and uiSearch.showActions and zobj.mayAct()"
|
<div if="not inPopup and uiSearch.showActions and zobj.mayAct()"
|
||||||
style=":'display:%s; margin-bottom:2px' % uiSearch.showActions">
|
class="objectActions" style=":'display:%s' % uiSearch.showActions">
|
||||||
<!-- Edit -->
|
<!-- Edit -->
|
||||||
<a if="zobj.mayEdit()"
|
<a if="zobj.mayEdit()"
|
||||||
var2="navInfo='search.%s.%s.%d.%d' % \
|
var2="navInfo='search.%s.%s.%d.%d' % \
|
||||||
|
@ -384,11 +384,11 @@ class ToolWrapper(AbstractWrapper):
|
||||||
buttonsMode='small'">:targetObj.appy().pxTransitions</x>
|
buttonsMode='small'">:targetObj.appy().pxTransitions</x>
|
||||||
<!-- Fields (actions) defined with layout "buttons" -->
|
<!-- Fields (actions) defined with layout "buttons" -->
|
||||||
<x if="not inPopup"
|
<x if="not inPopup"
|
||||||
var2="fields=zobj.getAppyTypes('buttons', 'main', type='Action');
|
var2="fields=zobj.getAppyTypes('buttons', 'main');
|
||||||
layoutType='view'">
|
layoutType='cell'">
|
||||||
<!-- Call pxView and not pxRender to avoid having a table -->
|
<!-- Call pxCell and not pxRender to avoid having a table -->
|
||||||
<x for="field in fields"
|
<x for="field in fields"
|
||||||
var2="name=field.name; smallButtons=True">:field.pxView</x>
|
var2="name=field.name; smallButtons=True">:field.pxCell</x>
|
||||||
</x>
|
</x>
|
||||||
</div>
|
</div>
|
||||||
</x>
|
</x>
|
||||||
|
|
Loading…
Reference in a new issue