[gen] Class.popup: finalized the development of 'popup' classes.

This commit is contained in:
Gaetan Delannay 2014-06-16 00:58:45 +02:00
parent ef68bb420b
commit e11e754305
11 changed files with 286 additions and 121 deletions

View file

@ -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 = ''

View file

@ -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"

View file

@ -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 &gt;1) and changeOrder and not inPickList"
<td if="(totalNumber &gt;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"
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&amp;className=%s&amp;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"/>''')
<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"
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.
# 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>
<!-- Nb of objects in the menu -->
<x>:len(menu.objects)</x>''')
pxViewMenus = Px('''
<div if="objects"
for="menu in field.getLinkedObjectsByMenu(obj, objects)"
var2="dtc='display: table-cell'"
<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">
<!-- 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) &gt; 1" class="dropdownMenu"
var2="dropdownId='%s_%s_%d' % (zobj.id, name, loop.menu.nb)"
<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'))">
<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) &gt; 1">:len(menu.objects)</x>
<!-- The dropdown menu containing annexes -->
<!-- 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.

View file

@ -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("'", "&apos;")
repl = escapeWithEntity and '&apos;' 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})
# ------------------------------------------------------------------------------

View file

@ -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,18 +438,18 @@ 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())
return self.goto(obj.getUrl())
if buttonClicked == 'previous':
# Go to the previous page for this object.
# We recompute the list of phases and pages because things
@ -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):

View file

@ -714,3 +714,7 @@ msgstr ""
#. Default: "You are not allowed to consult this."
msgid "unauthorized"
msgstr ""
#. Default: "Close"
msgid "window_close"
msgstr ""

View file

@ -62,7 +62,7 @@ input.button { color: #666666; height: 20px; margin-bottom: 5px;
background-color: white; background-repeat: no-repeat;
background-position: 8px 25%; box-shadow: 2px 2px 2px #888888}
input.buttonSmall { width: 100px !important; font-size: 85%; height: 18px;
margin-bottom: 3px}
margin-bottom: 3px }
.fake { background-color: #e6e6e6 !important ; cursor:help !important }
.xhtml { background-color: white; padding: 6px; font-size: 95% }
.xhtml img { margin-right: 5px }
@ -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 }

View file

@ -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();
}
};
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 219 B

View file

@ -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&amp;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'

View file

@ -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;