[gen] Class.popup: finalized the development of 'popup' classes.
This commit is contained in:
parent
ef68bb420b
commit
e11e754305
|
@ -307,7 +307,7 @@ class File(Field):
|
|||
size=":field.width"/>
|
||||
<script var="isDisabled=not value and 'false' or 'true'"
|
||||
type="text/javascript">:'document.getElementById(%s).disabled=%s'%\
|
||||
(q(fName), q(isDisabled))</script></x>''')
|
||||
(fName, isDisabled)</script></x>''')
|
||||
|
||||
pxSearch = ''
|
||||
|
||||
|
|
|
@ -33,12 +33,13 @@ class Phase:
|
|||
class=":(aPage == page) and 'currentPage' or ''">
|
||||
<!-- First line: page name and icons -->
|
||||
<span if="not (singlePhase and singlePage)">
|
||||
<a href=":zobj.getUrl(page=aPage)">::aPageInfo.page.getLabel(zobj)</a>
|
||||
<a href=":zobj.getUrl(page=aPage, \
|
||||
inPopup=inPopup)">::aPageInfo.page.getLabel(zobj)</a>
|
||||
<x var="locked=zobj.isLocked(user, aPage);
|
||||
editable=mayEdit and aPageInfo.showOnEdit and \
|
||||
aPageInfo.showEdit">
|
||||
<a if="editable and not locked"
|
||||
href=":zobj.getUrl(mode='edit', page=aPage)">
|
||||
href=":zobj.getUrl(mode='edit', page=aPage, inPopup=inPopup)">
|
||||
<img src=":url('edit')" title=":_('object_edit')"/></a>
|
||||
<a if="editable and locked">
|
||||
<img style="cursor: help"
|
||||
|
|
159
fields/ref.py
159
fields/ref.py
|
@ -35,8 +35,7 @@ class Ref(Field):
|
|||
# reach the consult view for this object. If we are on a back reference, the
|
||||
# link allows to reach the correct page where the forward reference is
|
||||
# defined. If we are on a forward reference, the "nav" parameter is added to
|
||||
# the URL for allowing to navigate from one object to the next/previous on
|
||||
# ui/view.
|
||||
# the URL for allowing to navigate from one object to the next/previous one.
|
||||
pxObjectTitle = Px('''
|
||||
<x var="includeShownInfo=includeShownInfo|False;
|
||||
navInfo='ref.%s.%s:%s.%d.%d' % (zobj.id, field.name, \
|
||||
|
@ -45,12 +44,14 @@ class Ref(Field):
|
|||
cssClass=tied.o.getCssFor('title')">
|
||||
<x>::tied.o.getSupTitle(navInfo)</x>
|
||||
<a var="pageName=field.isBack and field.back.pageName or 'main';
|
||||
fullUrl=tied.o.getUrl(page=pageName, nav=navInfo)"
|
||||
href=":fullUrl" class=":cssClass">:(not includeShownInfo) and \
|
||||
linkInPopup=inPopup or (target.target != '_self');
|
||||
fullUrl=tied.o.getUrl(page=pageName, nav=navInfo, \
|
||||
inPopup=linkInPopup)"
|
||||
href=":fullUrl" class=":cssClass" target=":target.target"
|
||||
onclick=":target.openPopup">:(not includeShownInfo) and \
|
||||
tied.title or field.getReferenceLabel(tied)
|
||||
</a><span name="subTitle" style=":showSubTitles and 'display:inline' or \
|
||||
'display:none'">::tied.o.getSubTitle()</span>
|
||||
</x>''')
|
||||
'display:none'">::tied.o.getSubTitle()</span></x>''')
|
||||
|
||||
# This PX displays buttons for triggering global actions on several linked
|
||||
# objects (delete many, unlink many,...)
|
||||
|
@ -84,7 +85,8 @@ class Ref(Field):
|
|||
<table class="noStyle">
|
||||
<tr>
|
||||
<!-- Arrows for moving objects up or down -->
|
||||
<td if="(totalNumber >1) and changeOrder and not inPickList"
|
||||
<td if="(totalNumber >1) and changeOrder and not inPickList \
|
||||
and not inMenu"
|
||||
var2="ajaxBaseCall=navBaseCall.replace('**v**','%s,%s,{%s:%s,%s:%s}'%\
|
||||
(q(startNumber), q('doChangeOrder'), q('refObjectUid'),
|
||||
q(tiedUid), q('move'), q('**v**')))">
|
||||
|
@ -108,8 +110,11 @@ class Ref(Field):
|
|||
<!-- Edit -->
|
||||
<td if="not field.noForm and tied.o.mayEdit()">
|
||||
<a var="navInfo='ref.%s.%s:%s.%d.%d' % (zobj.id, field.name, \
|
||||
field.pageName, loop.tied.nb + 1 + startNumber, totalNumber)"
|
||||
href=":tied.o.getUrl(mode='edit', page='main', nav=navInfo)">
|
||||
field.pageName, loop.tied.nb + 1 + startNumber, totalNumber);
|
||||
linkInPopup=inPopup or (target.target != '_self')"
|
||||
href=":tied.o.getUrl(mode='edit', page='main', nav=navInfo, \
|
||||
inPopup=linkInPopup)"
|
||||
target=":target.target" onclick=":target.openPopup">
|
||||
<img src=":url('edit')" title=":_('object_edit')"/></a>
|
||||
</td>
|
||||
<!-- Delete -->
|
||||
|
@ -140,24 +145,27 @@ class Ref(Field):
|
|||
# Displays the button allowing to add a new object through a Ref field, if
|
||||
# it has been declared as addable and if multiplicities allow it.
|
||||
pxAdd = Px('''
|
||||
<input if="mayAdd and not inPickList" type="button"
|
||||
<form if="mayAdd and not inPickList"
|
||||
class=":inMenu and 'addFormMenu' or 'addForm'"
|
||||
var2="formName='%s_%s_add' % (zobj.id, field.name)"
|
||||
name=":formName" id=":formName" target=":target.target"
|
||||
action=":'%s/do' % folder.absolute_url()">
|
||||
<input type="hidden" name="action" value="Create"/>
|
||||
<input type="hidden" name="className" value=":tiedClassName"/>
|
||||
<input type="hidden" name="nav"
|
||||
value=":'ref.%s.%s:%s.%d.%d' % (zobj.id, field.name, \
|
||||
field.pageName, 0, totalNumber)"/>
|
||||
<input type="hidden" name="popup"
|
||||
value=":(inPopup or (target.target != '_self')) and '1' or '0'"/>
|
||||
<input
|
||||
type=":(field.addConfirm or field.noForm) and 'button' or 'submit'"
|
||||
class="buttonSmall button"
|
||||
var2="navInfo='ref.%s.%s:%s.%d.%d' % (zobj.id, field.name, \
|
||||
field.pageName, 0, totalNumber);
|
||||
formCall='goto(%s)' % \
|
||||
q('%s/do?action=Create&className=%s&nav=%s' % \
|
||||
(folder.absolute_url(), tiedClassName, navInfo));
|
||||
formCall=not field.addConfirm and formCall or \
|
||||
'askConfirm(%s,%s,%s)' % (q('script'), q(formCall), \
|
||||
q(addConfirmMsg));
|
||||
noFormCall=navBaseCall.replace('**v**', \
|
||||
'%d,%s' % (startNumber, q('doCreateWithoutForm')));
|
||||
noFormCall=not field.addConfirm and noFormCall or \
|
||||
'askConfirm(%s, %s, %s)' % (q('script'), q(noFormCall), \
|
||||
q(addConfirmMsg));
|
||||
label=_('add_ref')"
|
||||
style=":'%s; %s' % (url('add', bg=True), ztool.getButtonWidth(label))"
|
||||
value=":label" onclick=":field.noForm and noFormCall or formCall"/>''')
|
||||
var="label=_('add_ref')" value=":label"
|
||||
style=":'%s; %s' % (url('add', bg=True), \
|
||||
ztool.getButtonWidth(label))"
|
||||
onclick=":field.getOnAdd(q, formName, addConfirmMsg, target, \
|
||||
navBaseCall, startNumber)"/>
|
||||
</form>''')
|
||||
|
||||
# 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.
|
||||
|
@ -309,39 +317,60 @@ class Ref(Field):
|
|||
req.get('showSubTitles', 'true') == 'true';
|
||||
subLabel='selectable_objects'">:field.pxViewList</x>''')
|
||||
|
||||
# PX that displays referred objects as menus.
|
||||
pxViewMenus = Px('''
|
||||
<div if="objects"
|
||||
for="menu in field.getLinkedObjectsByMenu(obj, objects)"
|
||||
var2="dtc='display: table-cell'"
|
||||
style=":not loop.menu.last and ('%s;padding-right:4px') % dtc or dtc">
|
||||
|
||||
<!-- A single object in the menu: show a clickable icon to get it -->
|
||||
<a if="len(menu.objects) == 1" var2="tied=menu.objects[0]"
|
||||
class="dropdownMenu" href=":field.getMenuUrl(zobj, tied)"
|
||||
title=":tied.title">
|
||||
<img if="menu.icon" src=":menu.icon"/><x
|
||||
if="not menu.icon">:menu.text</x> 1</a>
|
||||
|
||||
<!-- Several objects: put them in a dropdown menu -->
|
||||
<div if="len(menu.objects) > 1" class="dropdownMenu"
|
||||
var2="dropdownId='%s_%s_%d' % (zobj.id, name, loop.menu.nb)"
|
||||
onmouseover=":'toggleDropdown(%s)' % q(dropdownId)"
|
||||
onmouseout=":'toggleDropdown(%s,%s)' % (q(dropdownId), q('none'))">
|
||||
# PX that displays referred objects as dropdown menus.
|
||||
pxMenu = Px('''
|
||||
<img if="menu.icon" src=":menu.icon" title=":menu.text"/><x
|
||||
if="not menu.icon">:menu.text</x>
|
||||
<!-- Display the number of objects in the menu (if more than one) -->
|
||||
<x if="len(menu.objects) > 1">:len(menu.objects)</x>
|
||||
<!-- The dropdown menu containing annexes -->
|
||||
<!-- Nb of objects in the menu -->
|
||||
<x>:len(menu.objects)</x>''')
|
||||
|
||||
pxViewMenus = Px('''
|
||||
<x var2="dtc='display: table-cell'; 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 -->
|
||||
<div for="menu in field.getLinkedObjectsByMenu(obj, objects)"
|
||||
style=":not loop.menu.last and ('%s;padding-right:4px') % dtc or dtc">
|
||||
<div class="dropdownMenu"
|
||||
var2="dropdownId='%s_%s_%d' % (zobj.id, name, loop.menu.nb);
|
||||
singleObject=len(menu.objects) == 1"
|
||||
onmouseover=":'toggleDropdown(%s)' % q(dropdownId)"
|
||||
onmouseout=":'toggleDropdown(%s,%s)' % (q(dropdownId), q('none'))">
|
||||
|
||||
<!-- The menu name and/or icon, that is clickable if there is a single
|
||||
object in the menu. -->
|
||||
<x if="singleObject" var2="tied=menu.objects[0]">
|
||||
<a if="field.menuUrlMethod" class="dropdownMenu"
|
||||
href=":field.getMenuUrl(zobj, tied)"
|
||||
title=":tied.title">:field.pxMenu</a>
|
||||
<a if="not field.menuUrlMethod" class="dropdownMenu"
|
||||
var2="linkInPopup=inPopup or (target.target != '_self')"
|
||||
target=":target.target" onclick=":target.openPopup"
|
||||
href=":tied.o.getUrl(nav='',inPopup=linkInPopup)"
|
||||
title=":tied.title">:field.pxMenu</a>
|
||||
</x>
|
||||
<x if="not singleObject">:field.pxMenu</x>
|
||||
|
||||
<!-- The dropdown menu containing tied objects -->
|
||||
<div id=":dropdownId" class="dropdown" style="width:150px">
|
||||
<div for="tied in menu.objects"
|
||||
class=":not loop.tied.first and 'refMenuItem' or ''"
|
||||
var2="tiedUrl=field.getMenuUrl(zobj, tied)">
|
||||
<a href=":tiedUrl">:tied.title</a>
|
||||
var2="startNumber=0;
|
||||
totalNumber=len(menu.objects);
|
||||
tiedUid=tied.uid"
|
||||
class=":not loop.tied.first and 'refMenuItem' or ''">
|
||||
<!-- A specific link may have to be computed from
|
||||
field.menuUrlMethod -->
|
||||
<a if="field.menuUrlMethod"
|
||||
href=":field.getMenuUrl(zobj, tied)">:tied.title</a>
|
||||
<!-- Show standard pxObjectTitle else -->
|
||||
<x if="not field.menuUrlMethod">:field.pxObjectTitle</x>
|
||||
<div if="tied.o.mayAct()">:field.pxObjectActions</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>''')
|
||||
</div><x>:field.pxAdd</x></x> ''')
|
||||
|
||||
# Simplified widget showing minimal info about tied objects.
|
||||
pxViewMinimal = Px('''
|
||||
|
@ -359,6 +388,7 @@ class Ref(Field):
|
|||
linkList=field.link == 'list';
|
||||
renderAll=req.get('scope') != 'objs';
|
||||
inPickList=False;
|
||||
inMenu=False;
|
||||
startNumber=field.getStartNumber(render, req, ajaxHookId);
|
||||
info=field.getValue(zobj,startNumber=startNumber,someObjects=True);
|
||||
objects=info.objects;
|
||||
|
@ -368,6 +398,7 @@ class Ref(Field):
|
|||
batchNumber=len(objects);
|
||||
folder=zobj.getCreateFolder();
|
||||
tiedClassName=ztool.getPortalType(field.klass);
|
||||
target=ztool.getLinksTargetInfo(field.klass);
|
||||
mayEdit=not field.isBack and zobj.mayEdit(field.writePermission);
|
||||
mayUnlink=mayEdit and field.getAttribute(zobj, 'unlink');
|
||||
mayAdd=mayEdit and field.mayAdd(zobj, checkMayEdit=False);
|
||||
|
@ -729,6 +760,7 @@ class Ref(Field):
|
|||
def getLinkedObjectsByMenu(self, obj, objects):
|
||||
'''This method groups p_objects into sub-lists of objects, grouped by
|
||||
menu (happens when self.render == 'menus').'''
|
||||
if not objects: return ()
|
||||
res = []
|
||||
# We store in "menuIds" the already encountered menus:
|
||||
# ~{s_menuId : i_indexInRes}~
|
||||
|
@ -948,6 +980,29 @@ class Ref(Field):
|
|||
obj.raiseUnauthorized("User can't write Ref field '%s' (%s)." % \
|
||||
(self.name, may.msg))
|
||||
|
||||
def getOnAdd(self, q, formName, addConfirmMsg, target, navBaseCall,
|
||||
startNumber):
|
||||
'''Computes the JS code to execute when button "add" is clicked.'''
|
||||
if self.noForm:
|
||||
# Ajax-refresh the Ref with a special param to link a newly created
|
||||
# object.
|
||||
res = navBaseCall.replace('**v**',
|
||||
"%d,'doCreateWithoutForm'" % startNumber)
|
||||
if self.addConfirm:
|
||||
res = "askConfirm('script', %s, %s)" % \
|
||||
(q(res, False), q(addConfirmMsg))
|
||||
else:
|
||||
# In the basic case, no JS code is executed: target.openPopup is
|
||||
# empty and the button-related form is submitted in the main page.
|
||||
res = target.openPopup
|
||||
if self.addConfirm and not target.openPopup:
|
||||
res = "askConfirm('form','%s',%s)" % (formName,q(addConfirmMsg))
|
||||
elif self.addConfirm and target.openPopup:
|
||||
res = "askConfirm('form+script',%s,%s)" % \
|
||||
(q(formName + '+' + target.openPopup, False), \
|
||||
q(addConfirmMsg))
|
||||
return res
|
||||
|
||||
def getCbJsInit(self, obj):
|
||||
'''When checkboxes are enabled, this method defines a JS associative
|
||||
array (named "_appy_objs_cbs") that will store checkboxes' statuses.
|
||||
|
|
|
@ -416,10 +416,11 @@ class ToolMixin(BaseMixin):
|
|||
sub-lists of p_sub elements.'''
|
||||
return sutils.splitList(l, sub)
|
||||
|
||||
def quote(self, s):
|
||||
def quote(self, s, escapeWithEntity=True):
|
||||
'''Returns the quoted version of p_s.'''
|
||||
if not isinstance(s, basestring): s = str(s)
|
||||
s = s.replace('\r\n', '').replace('\n', '').replace("'", "'")
|
||||
repl = escapeWithEntity and ''' or "\\'"
|
||||
s = s.replace('\r\n', '').replace('\n', '').replace("'", repl)
|
||||
return "'%s'" % s
|
||||
|
||||
def getLayoutType(self):
|
||||
|
@ -789,7 +790,7 @@ class ToolMixin(BaseMixin):
|
|||
startNumber += batchSize
|
||||
return startNumber
|
||||
|
||||
def getNavigationInfo(self):
|
||||
def getNavigationInfo(self, inPopup=False):
|
||||
'''Extracts navigation information from request/nav and returns an
|
||||
object with the info that a page can use for displaying object
|
||||
navigation.'''
|
||||
|
@ -841,7 +842,7 @@ class ToolMixin(BaseMixin):
|
|||
uids = getattr(masterObj, fieldName)
|
||||
# Display the reference widget at the page where the current object
|
||||
# lies.
|
||||
startNumberKey = '%s%s_startNumber' % (masterObj.UID(), fieldName)
|
||||
startNumberKey = '%s%s_startNumber' % (masterObj.id, fieldName)
|
||||
startNumber = self.computeStartNumberFrom(res.currentNumber-1,
|
||||
res.totalNumber, batchSize)
|
||||
res.sourceUrl = masterObj.getUrl(**{startNumberKey:startNumber,
|
||||
|
@ -895,7 +896,7 @@ class ToolMixin(BaseMixin):
|
|||
sibling = brain.getObject()
|
||||
setattr(res, urlKey, sibling.getUrl(\
|
||||
nav=newNav % (index + 1),
|
||||
page=rq.get('page', 'main')))
|
||||
page=rq.get('page', 'main'), inPopup=inPopup))
|
||||
return res
|
||||
|
||||
def getGroupedSearchFields(self, searchInfo):
|
||||
|
@ -1368,4 +1369,30 @@ class ToolMixin(BaseMixin):
|
|||
# Set a minimum width for small labels.
|
||||
if len(label) < 15: return 'width:130px'
|
||||
return 'padding-left: 26px; padding-right: 8px'
|
||||
|
||||
def getLinksTargetInfo(self, klass):
|
||||
'''Appy allows to open links to view or edit instances of p_klass
|
||||
either via the same browser window, or via a popup. This method
|
||||
returns info about that, as an object having 2 attributes:
|
||||
- target is "_self" if the link leads to the same browser window,
|
||||
"appyIFrame" if the link must be opened in a popup;
|
||||
- openPopup is unused if target is "_self" and contains the
|
||||
Javascript code to open the popup.'''
|
||||
res = Object(target='_self', openPopup='')
|
||||
if hasattr(klass, 'popup'):
|
||||
res.target = 'appyIFrame'
|
||||
d = klass.popup
|
||||
if isinstance(d, basestring):
|
||||
# Width only
|
||||
params = int(d[:-2])
|
||||
else:
|
||||
# Width and height
|
||||
params = "%s, %s" % (d[0][:-2], d[1][:-2])
|
||||
res.openPopup = "openPopup('iframePopup',null,%s)" % params
|
||||
return res
|
||||
|
||||
def backFromPopup(self):
|
||||
'''Returns the PX allowing to close the iframe popup and refresh the
|
||||
base page.'''
|
||||
return self.appy().pxBack({'ztool': self})
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -196,7 +196,8 @@ class BaseMixin:
|
|||
className = rq.get('className')
|
||||
# Create the params to add to the URL we will redirect the user to
|
||||
# create the object.
|
||||
urlParams = {'mode':'edit', 'page':'main', 'nav':''}
|
||||
urlParams = {'mode':'edit', 'page':'main', 'nav':'',
|
||||
'inPopup':rq.get('popup') == '1'}
|
||||
initiator, initiatorPage, initiatorField = self.getInitiatorInfo()
|
||||
if initiator:
|
||||
# The object to create will be linked to an initiator object through
|
||||
|
@ -379,12 +380,15 @@ class BaseMixin:
|
|||
tool = self.getTool()
|
||||
errorMessage = self.translate('validation_error')
|
||||
isNew = self.isTemporary()
|
||||
inPopup = rq.get('popup') == '1'
|
||||
# If this object is created from an initiator, get info about him.
|
||||
initiator, initiatorPage, initiatorField = self.getInitiatorInfo()
|
||||
# If the user clicked on 'Cancel', go back to the previous page.
|
||||
buttonClicked = rq.get('button')
|
||||
if buttonClicked == 'cancel':
|
||||
if initiator:
|
||||
if inPopup:
|
||||
back = tool.backFromPopup()
|
||||
elif initiator:
|
||||
# Go back to the initiator page.
|
||||
urlBack = initiator.getUrl(page=initiatorPage, nav='')
|
||||
else:
|
||||
|
@ -395,6 +399,7 @@ class BaseMixin:
|
|||
urlBack = self.getUrl()
|
||||
self.say(self.translate('object_canceled'))
|
||||
self.removeLock(rq['page'])
|
||||
if inPopup: return back
|
||||
return self.goto(urlBack)
|
||||
|
||||
# Object for storing validation errors
|
||||
|
@ -433,17 +438,17 @@ class BaseMixin:
|
|||
if not msg: msg = self.translate('object_saved')
|
||||
# If the object has already been deleted (ie, it is a kind of transient
|
||||
# object like a one-shot form and has already been deleted in method
|
||||
# onEdit), redirect to the main site page.
|
||||
if not getattr(obj.getParentNode().aq_base, obj.id, None):
|
||||
return self.goto(tool.getSiteUrl(), msg)
|
||||
# If the user can't access the object anymore, redirect him to its home
|
||||
# page.
|
||||
if not obj.mayView(): return self.goto(tool.getHomePage(), msg)
|
||||
# onEdit) or if the user can't access the object anymore, redirect him
|
||||
# to the user's home page.
|
||||
if not getattr(obj.getParentNode().aq_base, obj.id, None) or \
|
||||
not obj.mayView():
|
||||
if inPopup: return tool.backFromPopup()
|
||||
return self.goto(tool.getHomePage(), msg)
|
||||
if (buttonClicked == 'save') or saveConfirmed:
|
||||
obj.say(msg)
|
||||
if inPopup: return tool.backFromPopup()
|
||||
if isNew and initiator:
|
||||
return self.goto(initiator.getUrl(page=initiatorPage, nav=''))
|
||||
else:
|
||||
return self.goto(obj.getUrl())
|
||||
if buttonClicked == 'previous':
|
||||
# Go to the previous page for this object.
|
||||
|
@ -462,29 +467,30 @@ class BaseMixin:
|
|||
# I do not use gotoEdit here because I really need to
|
||||
# redirect the user to the edit page. Indeed, the object
|
||||
# edit URL may have moved from temp_folder to another place.
|
||||
return self.goto(obj.getUrl(mode='edit', page=pageName))
|
||||
return self.goto(obj.getUrl(mode='edit', page=pageName,
|
||||
inPopup=inPopup))
|
||||
else:
|
||||
return self.goto(obj.getUrl(page=pageName))
|
||||
return self.goto(obj.getUrl(page=pageName, inPopup=inPopup))
|
||||
else:
|
||||
obj.say(msg)
|
||||
return self.goto(obj.getUrl())
|
||||
return self.goto(obj.getUrl(inPopup=inPopup))
|
||||
if buttonClicked == 'next':
|
||||
# Go to the next page for this object.
|
||||
# We remember page name, because the next method may set a new
|
||||
# current page if the current one is not visible anymore.
|
||||
pageName = rq['page']
|
||||
phaseObj = self.getAppyPhases(currentOnly=True, layoutType='edit')
|
||||
pageName, pageInfo = phaseObj.getNextPage(pageName)
|
||||
pageName, pageInfo = phaseObj.getNextPage(rq['page'])
|
||||
if pageName:
|
||||
# Return to the edit or view page?
|
||||
if pageInfo.showOnEdit:
|
||||
# Same remark as above (click on "previous").
|
||||
return self.goto(obj.getUrl(mode='edit', page=pageName))
|
||||
return self.goto(obj.getUrl(mode='edit', page=pageName,
|
||||
inPopup=inPopup))
|
||||
else:
|
||||
return self.goto(obj.getUrl(page=pageName))
|
||||
return self.goto(obj.getUrl(page=pageName, inPopup=inPopup))
|
||||
else:
|
||||
obj.say(msg)
|
||||
return self.goto(obj.getUrl())
|
||||
return self.goto(obj.getUrl(inPopup=inPopup))
|
||||
return obj.gotoEdit()
|
||||
|
||||
def reindex(self, indexes=None, unindex=False):
|
||||
|
@ -1326,11 +1332,15 @@ class BaseMixin:
|
|||
return layoutType in showValue
|
||||
|
||||
getUrlDefaults = {'page':True, 'nav':True}
|
||||
def getUrl(self, base=None, mode='view', **kwargs):
|
||||
def getUrl(self, base=None, mode='view', inPopup=False, **kwargs):
|
||||
'''Returns an URL for this object.
|
||||
* If p_base is None, it will be the base URL for this object
|
||||
(ie, Zope self.absolute_url()).
|
||||
* p_mode can be "edit", "view" or "raw" (a non-param, base URL)
|
||||
* If p_inPopup is True, the link will be opened in the Appy iframe.
|
||||
An additional param "popup=1" will be added to URL params, in order
|
||||
to tell Appy that the link target will be shown in a popup, in a
|
||||
minimalistic way (no portlet...).
|
||||
* p_kwargs can store additional parameters to add to the URL.
|
||||
In this dict, every value that is a string will be added to the
|
||||
URL as-is. Every value that is True will be replaced by the value
|
||||
|
@ -1355,6 +1365,7 @@ class BaseMixin:
|
|||
if not kwargs: kwargs = self.getUrlDefaults
|
||||
if 'page' not in kwargs: kwargs['page'] = True
|
||||
if 'nav' not in kwargs: kwargs['nav'] = True
|
||||
kwargs['popup'] = inPopup and '1' or '0'
|
||||
# Create URL parameters from kwargs
|
||||
params = []
|
||||
for name, value in kwargs.iteritems():
|
||||
|
@ -1392,18 +1403,19 @@ class BaseMixin:
|
|||
return False
|
||||
if parent.meta_type not in ('Folder', 'Temporary Folder'): return parent
|
||||
|
||||
def getBreadCrumb(self):
|
||||
def getBreadCrumb(self, inPopup=False):
|
||||
'''Gets breadcrumb info about this object and its parents (if it must
|
||||
be shown).'''
|
||||
# Return an empty breadcrumb if it must not be shown.
|
||||
klass = self.getClass()
|
||||
if hasattr(klass, 'breadcrumb') and not klass.breadcrumb: return ()
|
||||
# Compute the breadcrumb
|
||||
res = [Object(url=self.absolute_url(),
|
||||
res = [Object(url=self.getUrl(inPopup=inPopup),
|
||||
title=self.getFieldValue('title', layoutType='view'))]
|
||||
# In a popup: limit the breadcrumb to the current object.
|
||||
if inPopup: return res
|
||||
parent = self.getParent()
|
||||
if parent:
|
||||
res = parent.getBreadCrumb() + res
|
||||
if parent: res = parent.getBreadCrumb() + res
|
||||
return res
|
||||
|
||||
def index_html(self):
|
||||
|
|
|
@ -714,3 +714,7 @@ msgstr ""
|
|||
#. Default: "You are not allowed to consult this."
|
||||
msgid "unauthorized"
|
||||
msgstr ""
|
||||
|
||||
#. Default: "Close"
|
||||
msgid "window_close"
|
||||
msgstr ""
|
||||
|
|
|
@ -74,6 +74,7 @@ input.buttonSmall { width: 100px !important; font-size: 85%; height: 18px;
|
|||
width: 600px; border: 1px #F0C36D solid; padding: 6px;
|
||||
background-color: #F9EDBE; text-align: center; margin-left: -300px;
|
||||
border-radius: 2px 2px 2px 2px; box-shadow: 0 2px 4px #A9A9A9 }
|
||||
.messagePopup { width: 80%; top: 0; left: 0; margin-left: 10px }
|
||||
.focus { font-size: 90%; margin: 7px 0 7px 0; padding: 7px;
|
||||
background-color: #d7dee4; border-radius: 2px 2px 2px 2px;
|
||||
box-shadow: 0 2px 4px #A9A9A9 }
|
||||
|
@ -101,6 +102,8 @@ td.search { padding-top: 8px }
|
|||
font-weight: normal; text-align: left; z-index: 2 }
|
||||
.dropdownMenu { cursor: pointer; font-size: 93%; position: relative }
|
||||
.dropdown a:hover { text-decoration: underline }
|
||||
.addForm { display: inline }
|
||||
.addFormMenu { display: table-cell; padding-left: 7px }
|
||||
.list { margin-bottom: 3px }
|
||||
.list td, .list th { border: 3px solid #ededed; color: grey;
|
||||
padding: 3px 5px 3px 5px }
|
||||
|
@ -127,8 +130,8 @@ td.search { padding-top: 8px }
|
|||
.odd { background-color: #f6f6f6 }
|
||||
.odd2 { background-color: #f2f2f2 }
|
||||
.refMenuItem { border-top: grey 1px dashed; margin: 3px 0; padding-top: 3px }
|
||||
.summary { margin-bottom: 5px; background-color: #f9f9f9;
|
||||
border: 2px solid #f9f9f9 }
|
||||
.summary { margin-bottom: 5px; background-color: whitesmoke;
|
||||
border: 3px solid white }
|
||||
.by { padding: 5px; color: grey; font-size: 97% }
|
||||
.underline { border-bottom: 1px dotted grey }
|
||||
.state { font-weight: bold; border-bottom: 1px dashed grey }
|
||||
|
|
|
@ -703,7 +703,7 @@ function protectAppyForm() {
|
|||
}
|
||||
|
||||
// Functions for opening and closing a popup
|
||||
function openPopup(popupId, msg) {
|
||||
function openPopup(popupId, msg, width, height) {
|
||||
// Put the message into the popup
|
||||
if (msg) {
|
||||
var msgHook = (popupId == 'alertPopup')? 'appyAlertText': 'appyConfirmText';
|
||||
|
@ -715,13 +715,35 @@ function openPopup(popupId, msg) {
|
|||
// Put it at the right place on the screen
|
||||
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || 0;
|
||||
popup.style.top = (scrollTop + 150) + 'px';
|
||||
if (width) popup.style.width = width + 'px';
|
||||
if (popupId == 'iframePopup') {
|
||||
// Initialize iframe's width.
|
||||
var iframe = document.getElementById('appyIFrame');
|
||||
iframe.style.width = (width-20) + 'px';
|
||||
if (height) iframe.style.height = height + 'px';
|
||||
}
|
||||
popup.style.display = 'block';
|
||||
}
|
||||
|
||||
function closePopup(popupId) {
|
||||
// Close the popup
|
||||
var popup = document.getElementById(popupId);
|
||||
var container = window.parent.document;
|
||||
var popup = container.getElementById(popupId);
|
||||
popup.style.display = 'none';
|
||||
popup.style.width = null;
|
||||
if (popupId == 'iframePopup') {
|
||||
// Reinitialise the enclosing iframe.
|
||||
var iframe = container.getElementById('appyIFrame');
|
||||
iframe.style.width = null;
|
||||
iframe.innerHTML = '';
|
||||
// Leave the form silently if we are on an edit page
|
||||
iframe.contentWindow.onbeforeunload = null;
|
||||
}
|
||||
}
|
||||
|
||||
function backFromPopup() {
|
||||
closePopup('iframePopup');
|
||||
window.parent.location = window.parent.location;
|
||||
}
|
||||
|
||||
// Function triggered when an action needs to be confirmed by the user
|
||||
|
@ -734,8 +756,8 @@ function askConfirm(actionType, action, msg, showComment) {
|
|||
confirmForm.actionType.value = actionType;
|
||||
confirmForm.action.value = action;
|
||||
var commentArea = document.getElementById('commentArea');
|
||||
if (showComment) commentArea.style.display = "block";
|
||||
else commentArea.style.display = "none";
|
||||
if (showComment) commentArea.style.display = 'block';
|
||||
else commentArea.style.display = 'none';
|
||||
openPopup("confirmActionPopup", msg);
|
||||
}
|
||||
|
||||
|
@ -762,6 +784,12 @@ function doConfirm() {
|
|||
// We must execute Javascript code in "action"
|
||||
eval(action);
|
||||
}
|
||||
else if (actionType == 'form+script') {
|
||||
var elems = action.split('+');
|
||||
// Submit the form in elems[0] and execute the JS code in elems[1]
|
||||
document.getElementById(elems[0]).submit();
|
||||
eval(elems[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Function triggered when the user asks password reinitialisation
|
||||
|
@ -952,4 +980,4 @@ function onSelectDate(cal) {
|
|||
if (update && p.singleClick && cal.dateClicked) {
|
||||
cal.callCloseHandler();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
BIN
gen/ui/close.png
BIN
gen/ui/close.png
Binary file not shown.
Before Width: | Height: | Size: 226 B After Width: | Height: | Size: 219 B |
|
@ -176,22 +176,24 @@ class ToolWrapper(AbstractWrapper):
|
|||
'current' or ''">::_(className + '_plural')</a>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<x var="mayCreate=ztool.userMayCreate(rootClass);
|
||||
createMeans=ztool.getCreateMeans(rootClass)">
|
||||
<!-- Create a new object from a web form. -->
|
||||
<input type="button" class="buttonSmall button"
|
||||
if="mayCreate and ('form' in createMeans)"
|
||||
var2="label=_('query_create')" value=":label"
|
||||
<!-- Create instances of this class -->
|
||||
<form if="ztool.userMayCreate(rootClass) and \
|
||||
('form' in ztool.getCreateMeans(rootClass))" class="addForm"
|
||||
var2="target=ztool.getLinksTargetInfo(rootClass)"
|
||||
action=":'%s/do' % toolUrl" target=":target.target">
|
||||
<input type="hidden" name="action" value="Create"/>
|
||||
<input type="hidden" name="className" value=":className"/>
|
||||
<input type="hidden" name="popup"
|
||||
value=":(inPopup or (target.target != '_self')) and '1' or '0'"/>
|
||||
<input type="submit" class="buttonSmall button"
|
||||
var="label=_('query_create')" value=":label"
|
||||
onclick=":target.openPopup"
|
||||
style=":'%s; %s' % (url('add', bg=True), \
|
||||
ztool.getButtonWidth(label))"
|
||||
onclick=":'goto(%s)' % \
|
||||
q('%s/do?action=Create&className=%s' % \
|
||||
(toolUrl, className))"/>
|
||||
</x>
|
||||
ztool.getButtonWidth(label))"/>
|
||||
</form>
|
||||
|
||||
<!-- Searches -->
|
||||
<x if="ztool.advancedSearchEnabledFor(rootClass)">
|
||||
|
||||
<!-- Live search -->
|
||||
<form action=":'%s/do' % toolUrl">
|
||||
<input type="hidden" name="action" value="SearchObjects"/>
|
||||
|
@ -232,7 +234,8 @@ class ToolWrapper(AbstractWrapper):
|
|||
|
||||
# The message that is shown when a user triggers an action.
|
||||
pxMessage = Px('''
|
||||
<div var="messages=ztool.consumeMessages()" if="messages" class="message">
|
||||
<div var="messages=ztool.consumeMessages()" if="messages"
|
||||
class=":inPopup and 'messagePopup message' or 'message'">
|
||||
<!-- The icon for closing the message -->
|
||||
<img src=":url('close')" align=":dright" class="clickable"
|
||||
onclick="this.parentNode.style.display='none'"/>
|
||||
|
@ -308,8 +311,11 @@ class ToolWrapper(AbstractWrapper):
|
|||
(className, searchName, startNumber+currentNumber, totalNumber);
|
||||
cssClass=zobj.getCssFor('title')">
|
||||
<x>::zobj.getSupTitle(navInfo)</x>
|
||||
<a href=":zobj.getUrl(nav=navInfo, page=zobj.getDefaultViewPage())"
|
||||
if="enableLinks" class=":cssClass">:zobj.Title()</a><span
|
||||
<a if="enableLinks" class=":cssClass"
|
||||
var2="linkInPopup=inPopup or (target.target != '_self')"
|
||||
target=":target.target" onclick=":target.openPopup"
|
||||
href=":zobj.getUrl(nav=navInfo, page=zobj.getDefaultViewPage(), \
|
||||
inPopup=linkInPopup)">:zobj.Title()</a><span
|
||||
if="not enableLinks" class=":cssClass">:zobj.Title()</span><span
|
||||
style=":showSubTitles and 'display:inline' or 'display:none'"
|
||||
name="subTitle">::zobj.getSubTitle()</span>
|
||||
|
@ -320,9 +326,11 @@ class ToolWrapper(AbstractWrapper):
|
|||
<!-- Edit -->
|
||||
<td if="zobj.mayEdit()">
|
||||
<a var="navInfo='search.%s.%s.%d.%d' % \
|
||||
(className, searchName, loop.zobj.nb+1+startNumber, totalNumber)"
|
||||
(className, searchName, loop.zobj.nb+1+startNumber, totalNumber);
|
||||
linkInPopup=inPopup or (target.target != '_self')"
|
||||
target=":target.target" onclick=":target.openPopup"
|
||||
href=":zobj.getUrl(mode='edit', page=zobj.getDefaultEditPage(), \
|
||||
nav=navInfo)">
|
||||
nav=navInfo, inPopup=linkInPopup)">
|
||||
<img src=":url('edit')" title=":_('object_edit')"/></a>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -429,7 +437,8 @@ class ToolWrapper(AbstractWrapper):
|
|||
newSearchUrl='%s/search?className=%s%s' % \
|
||||
(ztool.absolute_url(), className, refUrlPart);
|
||||
showSubTitles=req.get('showSubTitles', 'true') == 'true';
|
||||
resultMode=ztool.getResultMode(className)">
|
||||
resultMode=ztool.getResultMode(className);
|
||||
target=ztool.getLinksTargetInfo(ztool.getAppyClass(className))">
|
||||
|
||||
<x if="zobjects">
|
||||
<!-- Display here POD templates if required. -->
|
||||
|
@ -532,6 +541,17 @@ class ToolWrapper(AbstractWrapper):
|
|||
</form>
|
||||
</x>''', template=AbstractWrapper.pxTemplate, hook='content')
|
||||
|
||||
pxBack = Px('''
|
||||
<html>
|
||||
<head>
|
||||
<script src=":ztool.getIncludeUrl('appy.js')" type="text/javascript">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">backFromPopup()</script>
|
||||
</body>
|
||||
</html>''')
|
||||
|
||||
def isManager(self):
|
||||
'''Some pages on the tool can only be accessed by managers.'''
|
||||
if self.user.has_role('Manager'): return 'view'
|
||||
|
|
|
@ -21,9 +21,9 @@ class AbstractWrapper(object):
|
|||
# Buttons for going to next/previous objects if this one is among bunch of
|
||||
# referenced or searched objects. currentNumber starts with 1.
|
||||
pxNavigateSiblings = Px('''
|
||||
<div if="req.get('nav', None)" var2="ni=ztool.getNavigationInfo()">
|
||||
<div if="req.get('nav', None)" var2="ni=ztool.getNavigationInfo(inPopup)">
|
||||
<!-- Go to the source URL (search or referred object) -->
|
||||
<a if="ni.sourceUrl" href=":ni.sourceUrl"><img
|
||||
<a if="not inPopup and ni.sourceUrl" href=":ni.sourceUrl"><img
|
||||
var="gotoSource=_('goto_source');
|
||||
goBack=ni.backText and ('%s - %s' % (ni.backText, gotoSource)) \
|
||||
or gotoSource"
|
||||
|
@ -52,7 +52,7 @@ class AbstractWrapper(object):
|
|||
<tr>
|
||||
<!-- Breadcrumb -->
|
||||
<td var="sup=zobj.getSupBreadCrumb();
|
||||
breadcrumb=zobj.getBreadCrumb();
|
||||
breadcrumb=zobj.getBreadCrumb(inPopup=inPopup);
|
||||
sub=zobj.getSubBreadCrumb()" class="breadcrumb">
|
||||
<x if="sup">::sup</x>
|
||||
<x for="bc in breadcrumb" var2="nb=loop.bc.nb">
|
||||
|
@ -84,7 +84,8 @@ class AbstractWrapper(object):
|
|||
dummy=setattr(req, 'pxContext', _ctx_);
|
||||
lang=ztool.getUserLanguage(); q=ztool.quote;
|
||||
layoutType=ztool.getLayoutType();
|
||||
showPortlet=ztool.showPortlet(obj, layoutType);
|
||||
inPopup=req.get('popup') == '1';
|
||||
showPortlet=not inPopup and ztool.showPortlet(obj, layoutType);
|
||||
dir=ztool.getLanguageDirection(lang);
|
||||
cfg=ztool.getProductConfig(True);
|
||||
dleft=(dir == 'ltr') and 'left' or 'right';
|
||||
|
@ -170,9 +171,17 @@ class AbstractWrapper(object):
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Popup containing the Appy iframe -->
|
||||
<div id="iframePopup" class="popup" if="not inPopup"
|
||||
style="background-color: #fbfbfb">
|
||||
<img align=":dright" src=":url('close')" class="clickable"
|
||||
onclick="closePopup('iframePopup')"/>
|
||||
<iframe id="appyIFrame" name="appyIFrame" frameborder="0"></iframe>
|
||||
</div>
|
||||
|
||||
<table class=":(cfg.skin == 'wide') and 'mainWide main' or 'main'"
|
||||
align="center" cellpadding="0">
|
||||
<tr class="top">
|
||||
<tr class="top" if="not inPopup">
|
||||
<!-- Top banner -->
|
||||
<td var="bannerName=(dir == 'ltr') and 'banner' or 'bannerrtl'"
|
||||
style=":url(bannerName, bg=True) + '; background-repeat:no-repeat;\
|
||||
|
@ -213,7 +222,7 @@ class AbstractWrapper(object):
|
|||
</tr>
|
||||
|
||||
<!-- The user strip -->
|
||||
<tr height=":cfg.discreetLogin and '5px' or '28px'">
|
||||
<tr height=":cfg.discreetLogin and '5px' or '28px'" if="not inPopup">
|
||||
<td>
|
||||
<table class="userStrip">
|
||||
<tr>
|
||||
|
@ -297,7 +306,7 @@ class AbstractWrapper(object):
|
|||
</td>
|
||||
</tr>
|
||||
<!-- Footer -->
|
||||
<tr height="26px"><td>:tool.pxFooter</td></tr>
|
||||
<tr height="26px" if="not inPopup"><td>:tool.pxFooter</td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>''', prologue=Px.xhtmlPrologue)
|
||||
|
@ -401,7 +410,7 @@ class AbstractWrapper(object):
|
|||
historyMaxPerPage=req.get('maxPerPage', 5);
|
||||
historyExpanded=req.get('appyHistory','collapsed')=='expanded';
|
||||
creator=zobj.Creator()">
|
||||
<table width="100%" class="summary">
|
||||
<table width="100%" class="summary" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td colspan="2" class="by">
|
||||
<!-- Plus/minus icon for accessing history -->
|
||||
|
@ -461,7 +470,8 @@ class AbstractWrapper(object):
|
|||
<tr valign="top">
|
||||
<!-- Refresh -->
|
||||
<td if="zobj.isDebug()">
|
||||
<a href=":zobj.getUrl(mode=layoutType, page=page, refresh='yes')">
|
||||
<a href=":zobj.getUrl(mode=layoutType, page=page, refresh='yes', \
|
||||
inPopup=inPopup)">
|
||||
<img title="Refresh" style="vertical-align:top" src=":url('refresh')"/>
|
||||
</a>
|
||||
</td>
|
||||
|
@ -479,7 +489,8 @@ class AbstractWrapper(object):
|
|||
<!-- Button on the view page -->
|
||||
<input if="not isEdit" type="button" class="button" value=":label"
|
||||
style=":'%s; %s' % (url('previous', bg=True), buttonWidth)"
|
||||
onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage))"/>
|
||||
onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage, \
|
||||
inPopup=inPopup))"/>
|
||||
</td>
|
||||
|
||||
<!-- Save -->
|
||||
|
@ -506,7 +517,8 @@ class AbstractWrapper(object):
|
|||
var="label=_('object_edit')" value=":label"
|
||||
style=":'%s; %s' % (url('edit', bg=True), \
|
||||
ztool.getButtonWidth(label))"
|
||||
onclick=":'goto(%s)' % q(zobj.getUrl(mode='edit', page=page))"/>
|
||||
onclick=":'goto(%s)' % q(zobj.getUrl(mode='edit', page=page, \
|
||||
inPopup=inPopup))"/>
|
||||
|
||||
<!-- Locked -->
|
||||
<a if="editable and locked">
|
||||
|
@ -536,7 +548,8 @@ class AbstractWrapper(object):
|
|||
<!-- Button on the view page -->
|
||||
<input if="not isEdit" type="button" class="button" value=":label"
|
||||
style=":'%s; %s' % (url('next', bg=True), buttonWidth)"
|
||||
onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage))"/>
|
||||
onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage, \
|
||||
inPopup=inPopup))"/>
|
||||
</td>
|
||||
|
||||
<!-- Workflow transitions -->
|
||||
|
@ -590,6 +603,7 @@ class AbstractWrapper(object):
|
|||
enctype="multipart/form-data" action=":zobj.absolute_url()+'/do'">
|
||||
<input type="hidden" name="action" value="Update"/>
|
||||
<input type="hidden" name="button" value=""/>
|
||||
<input type="hidden" name="popup" value=":inPopup and '1' or '0'"/>
|
||||
<input type="hidden" name="page" value=":page"/>
|
||||
<input type="hidden" name="nav" value=":req.get('nav', None)"/>
|
||||
<input type="hidden" name="confirmed" value="False"/>
|
||||
|
@ -615,6 +629,7 @@ class AbstractWrapper(object):
|
|||
lang=ztool.getUserLanguage(); q=ztool.quote;
|
||||
action=req.get('action', None);
|
||||
px=req['px'].split(':');
|
||||
inPopup=req.get('popup') == '1';
|
||||
className=(len(px) == 3) and px[0] or None;
|
||||
field=className and zobj.getAppyType(px[1], className) or None;
|
||||
field=(len(px) == 2) and zobj.getAppyType(px[0]) or field;
|
||||
|
|
Loading…
Reference in a new issue