[gen] Anti-double-click system.

This commit is contained in:
Gaetan Delannay 2015-02-10 17:20:50 +01:00
parent f38792a5b9
commit 7c58582b9a
13 changed files with 117 additions and 75 deletions

View file

@ -66,8 +66,8 @@ class Pod(Field):
<img var="iconSuffix=frozen and 'Frozen' or ''" <img var="iconSuffix=frozen and 'Frozen' or ''"
src=":url(fmt + iconSuffix)" class="clickable" src=":url(fmt + iconSuffix)" class="clickable"
title=":field.getIconTitle(obj, fmt, frozen)" title=":field.getIconTitle(obj, fmt, frozen)"
onclick=":'generatePod(%s,%s,%s,%s,%s,null,%s)' % (q(uid), q(name), \ onclick=":'generatePod(this,%s,%s,%s,%s,%s,null,%s)' % (q(uid), \
q(info.template), q(fmt), q(ztool.getQueryInfo()), gc)"/>''') q(name), q(info.template), q(fmt), q(ztool.getQueryInfo()), gc)"/>''')
pxView = pxCell = Px(''' pxView = pxCell = Px('''
<x var="uid=obj.uid; <x var="uid=obj.uid;
@ -125,7 +125,7 @@ class Pod(Field):
<tr for="mailing in mailings[fmt]" valign="top" <tr for="mailing in mailings[fmt]" valign="top"
var2="mailingName=field.getMailingName(obj, mailing)"> var2="mailingName=field.getMailingName(obj, mailing)">
<td colspan="2"> <td colspan="2">
<a var="js='generatePod(%s,%s,%s,%s,%s,null,%s,%s)' % \ <a var="js='generatePod(this,%s,%s,%s,%s,%s,null,%s,%s)' % \
(q(uid), q(name), q(info.template), q(fmt), \ (q(uid), q(name), q(info.template), q(fmt), \
q(ztool.getQueryInfo()), gc, q(mailing))" q(ztool.getQueryInfo()), gc, q(mailing))"
onclick=":'askConfirm(%s,%s)' % (q('script'), q(js, False))" onclick=":'askConfirm(%s,%s)' % (q('script'), q(js, False))"
@ -738,21 +738,21 @@ class Pod(Field):
'''This method is called when an action tied to this pod field '''This method is called when an action tied to this pod field
(generate, freeze, upload...) is triggered from the user (generate, freeze, upload...) is triggered from the user
interface.''' interface.'''
# What is the action to perform? # What is the action to perform ?
action = rq.get('action', 'generate') action = rq.get('action', 'generate')
# Security check. # Security check
obj.o.mayView(self.readPermission, raiseError=True) obj.o.mayView(self.readPermission, raiseError=True)
# Perform the requested action. # Perform the requested action
tool = obj.tool.o tool = obj.tool.o
template = rq.get('template') template = rq.get('template')
format = rq.get('podFormat') format = rq.get('podFormat')
if action == 'generate': if action == 'generate':
# Generate a (or get a frozen) document. # Generate a (or get a frozen) document
res = self.getValue(obj, template=template, format=format, res = self.getValue(obj, template=template, format=format,
queryData=rq.get('queryData'), queryData=rq.get('queryData'),
customContext=self.getCustomContext(obj, rq)) customContext=self.getCustomContext(obj, rq))
if isinstance(res, basestring): if isinstance(res, basestring):
# An error has occurred, and p_res contains the error message. # An error has occurred, and p_res contains the error message
obj.say(res) obj.say(res)
return tool.goto(rq.get('HTTP_REFERER')) return tool.goto(rq.get('HTTP_REFERER'))
# res contains a FileInfo instance. # res contains a FileInfo instance.
@ -763,35 +763,36 @@ class Pod(Field):
# With disposition=inline, Google Chrome and IE may launch a PDF # With disposition=inline, Google Chrome and IE may launch a PDF
# viewer that triggers one or many additional crashing HTTP GET # viewer that triggers one or many additional crashing HTTP GET
# requests. # requests.
rq.RESPONSE.setCookie('podDownload', 'true', path='/')
res.writeResponse(rq.RESPONSE, disposition='attachment') res.writeResponse(rq.RESPONSE, disposition='attachment')
return return
else: else:
# Send the email(s). # Send the email(s)
msg = self.sendMailing(obj, template, mailing, res) msg = self.sendMailing(obj, template, mailing, res)
obj.say(obj.translate(msg)) obj.say(obj.translate(msg))
return tool.goto(rq.get('HTTP_REFERER')) return tool.goto(rq.get('HTTP_REFERER'))
# Performing any other action requires write access to p_obj. # Performing any other action requires write access to p_obj
obj.o.mayEdit(self.writePermission, raiseError=True) obj.o.mayEdit(self.writePermission, raiseError=True)
msg = 'action_done' msg = 'action_done'
if action == 'freeze': if action == 'freeze':
# (Re-)freeze a document in the database. # (Re-)freeze a document in the database
self.freeze(obj, template, format, noSecurity=False, self.freeze(obj, template, format, noSecurity=False,
freezeOdtOnError=False) freezeOdtOnError=False)
elif action == 'unfreeze': elif action == 'unfreeze':
# Unfreeze a document in the database. # Unfreeze a document in the database
self.unfreeze(obj, template, format, noSecurity=False) self.unfreeze(obj, template, format, noSecurity=False)
elif action == 'upload': elif action == 'upload':
# Ensure a file from the correct type has been uploaded. # Ensure a file from the correct type has been uploaded
upload = rq.get('uploadedFile') upload = rq.get('uploadedFile')
if not upload or not upload.filename or \ if not upload or not upload.filename or \
not upload.filename.endswith('.%s' % format): not upload.filename.endswith('.%s' % format):
# A wrong file has been uploaded (or no file at all) # A wrong file has been uploaded (or no file at all)
msg = 'upload_invalid' msg = 'upload_invalid'
else: else:
# Store the uploaded file in the database. # Store the uploaded file in the database
self.freeze(obj, template, format, noSecurity=False, self.freeze(obj, template, format, noSecurity=False,
upload=upload) upload=upload)
# Return a message to the user interface. # Return a message to the user interface
obj.say(obj.translate(msg)) obj.say(obj.translate(msg))
return tool.goto(rq.get('HTTP_REFERER')) return tool.goto(rq.get('HTTP_REFERER'))
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -164,6 +164,7 @@ class UiSearch:
<a href=":'%s?className=%s&amp;search=%s' % \ <a href=":'%s?className=%s&amp;search=%s' % \
(queryUrl, className, search.name)" (queryUrl, className, search.name)"
class=":(search.name == currentSearch) and 'current' or ''" class=":(search.name == currentSearch) and 'current' or ''"
onclick="clickOn(this)"
title=":search.translatedDescr">:search.translated</a> title=":search.translatedDescr">:search.translated</a>
</div>''') </div>''')

View file

@ -472,10 +472,10 @@ class UiTransition:
css=ztool.getButtonCss(label, inButtons)"> css=ztool.getButtonCss(label, inButtons)">
<!-- Real button --> <!-- Real button -->
<input if="transition.mayTrigger" type="button" class=":css" <input if="transition.mayTrigger" type="button" class=":css"
var="back=inButtons and q(zobj.id) or 'null'" var="back=inButtons and q(zobj.id) or 'null'" id=":transition.name"
style=":url(transition.icon, bg=True)" value=":label" style=":url(transition.icon, bg=True)" value=":label"
onclick=":'triggerTransition(%s,%s,%s,%s)' % (q(formId), \ onclick=":'triggerTransition(%s,this,%s,%s)' % \
q(transition.name), q(transition.confirm), back)"/> (q(formId), q(transition.confirm), back)"/>
<!-- Fake button, explaining why the transition can't be triggered --> <!-- Fake button, explaining why the transition can't be triggered -->
<input if="not transition.mayTrigger" type="button" <input if="not transition.mayTrigger" type="button"

View file

@ -1006,10 +1006,9 @@ class BaseMixin:
if mode == 'link': if mode == 'link':
inPopup = inPopup or (target.target != '_self') inPopup = inPopup or (target.target != '_self')
url = self.getUrl(page=page, nav=nav, inPopup=inPopup) url = self.getUrl(page=page, nav=nav, inPopup=inPopup)
onClick = target.openPopup and \ onClick = target.openPopup or 'clickOn(this)'
(' onclick="%s"' % target.openPopup) or '' res = '<a href="%s" class="%s" onclick="%s" target="%s">%s</a>' % \
res = '<a href="%s" class="%s" target="%s"%s>%s</a>' % \ (url, cssClass, onClick, target.target, title)
(url, cssClass, target.target, onClick, title)
elif mode == 'select': elif mode == 'select':
res = '<span class="%s clickable" onclick="%s">%s</span>' % \ res = '<span class="%s clickable" onclick="%s">%s</span>' % \
(cssClass, selectJs, title) (cssClass, selectJs, title)

View file

@ -13,6 +13,7 @@ h5 { font-size: 10pt; margin:0; font-style: italic; font-weight: normal;
h6 { font-size: 9pt; margin:0; font-weight: bold } h6 { font-size: 9pt; margin:0; font-weight: bold }
a { text-decoration: none; color: #114353 } a { text-decoration: none; color: #114353 }
a:visited { color: #114353 } a:visited { color: #114353 }
.unclickable { pointer-events: none; color: grey !important }
table { font-size: 100%; border-spacing: 0px; border-collapse:collapse } table { font-size: 100%; border-spacing: 0px; border-collapse:collapse }
form { margin: 0; padding: 0 } form { margin: 0; padding: 0 }
p { margin: 0 0 5px 0 } p { margin: 0 0 5px 0 }

View file

@ -1,4 +1,9 @@
var wrongTextInput = '#F9EDBE none'; var wrongTextInput = '#F9EDBE none';
var loadingLink = '<img src="ui/loading.gif"/>';
var loadingButton = '<img align="center" src="ui/loadingBtn.gif"/>';
var loadingZone = '<div align="center"><img src="ui/loadingBig.gif"/></div>';
var lsTimeout; // Timout for the live search
var podTimeout; // Timeout for checking status of pod downloads
// Functions related to user authentication // Functions related to user authentication
function cookiesAreEnabled() { function cookiesAreEnabled() {
@ -97,9 +102,9 @@ function injectChunk(elem, content, inner, searchTop){
else { else {
// Replace p_elem with a new node filled with p_content and return it // Replace p_elem with a new node filled with p_content and return it
var id = elem.id; var id = elem.id;
if (searchTop) id = ':' + id; if (id && searchTop) id = ':' + id;
elem.outerHTML = content; elem.outerHTML = content;
res = getAjaxHook(id); // Get the new element if (id) res = getAjaxHook(id); // Get the new element
} }
} }
else { else {
@ -113,6 +118,25 @@ function injectChunk(elem, content, inner, searchTop){
return res; return res;
} }
function clickOn(node) {
// If node is a form, disable all form buttons
if (node.tagName == 'FORM') {
var i = node.elements.length -1;
while (i >= 0) {
if (node.elements[i].type == 'button') { clickOn(node.elements[i]); }
i = i - 1;
}
return;
}
// Disable any click on p_node to be protected against double-click
var cn = (node.className)? 'unclickable ' + node.className : 'unclickable';
node.className = cn;
/* For a button, show the preloader directly. For a link, show it only after
a while, if the target page is still not there. */
if (node.tagName != 'A') injectChunk(node, loadingButton);
else setTimeout(function(){injectChunk(node, loadingLink)}, 700);
}
function getAjaxHook(hookId, forceTop) { function getAjaxHook(hookId, forceTop) {
/* Gets the XHTML element whose ID is p_hookId: it will be the placeholder /* Gets the XHTML element whose ID is p_hookId: it will be the placeholder
for the result of an ajax request. If p_hookId starts with ':', we search for the result of an ajax request. If p_hookId starts with ':', we search
@ -136,32 +160,22 @@ function getAjaxChunk(pos) {
// This function is the callback called by the AJAX machinery (see function // This function is the callback called by the AJAX machinery (see function
// askAjaxChunk below) when an Ajax response is available. // askAjaxChunk below) when an Ajax response is available.
// First, find back the correct XMLHttpRequest object // First, find back the correct XMLHttpRequest object
if ( (typeof(xhrObjects[pos]) != 'undefined') && var rq = xhrObjects[pos];
(xhrObjects[pos].freed == 0)) { if ( (typeof(rq) != 'undefined') && (rq.freed == 0)) {
var hook = xhrObjects[pos].hook; if ((!rq.hook) || (rq.xhr.readyState != 4)) return;
if (hook && (xhrObjects[pos].xhr.readyState == 1)) {
// The request has been initialized: display the waiting radar
var hookElem = getAjaxHook(hook);
if (hookElem)
injectChunk(hookElem, "<div align=\"center\"><img src=\"ui/waiting.gif\"/><\/div>", true);
}
if (xhrObjects[pos].xhr.readyState == 4) {
// We have received the HTML chunk // We have received the HTML chunk
var hookElem = getAjaxHook(hook); var hookElem = getAjaxHook(rq.hook);
if (hookElem) { if (hookElem) {
var content = xhrObjects[pos].xhr.responseText; var content = rq.xhr.responseText;
var searchTop = hook[0] == ':'; var searchTop = rq.hook[0] == ':';
var injected = injectChunk(hookElem, content, false, searchTop); var injected = injectChunk(hookElem, content, false, searchTop);
// Call a custom Javascript function if required // Call a custom Javascript function if required
if (xhrObjects[pos].onGet) { if (rq.onGet) rq.onGet(rq, injected);
xhrObjects[pos].onGet(xhrObjects[pos], injected);
}
// Display the Appy message if present // Display the Appy message if present
var msg = xhrObjects[pos].xhr.getResponseHeader('Appy-Message'); var msg = rq.xhr.getResponseHeader('Appy-Message');
if (msg) showAppyMessage(decodeURIComponent(escape(msg))); if (msg) showAppyMessage(decodeURIComponent(escape(msg)));
} }
xhrObjects[pos].freed = 1; rq.freed = 1;
}
} }
} }
@ -217,6 +231,8 @@ function askAjaxChunk(hook, mode, url, px, params, beforeSend, onGet) {
if (mode == 'GET') { if (mode == 'GET') {
urlFull = urlFull + '?' + paramsFull; urlFull = urlFull + '?' + paramsFull;
} }
// Display the preloader
injectChunk(getAjaxHook(rq.hook), loadingZone, true);
// Perform the asynchronous HTTP GET or POST // Perform the asynchronous HTTP GET or POST
rq.xhr.open(mode, urlFull, true); rq.xhr.open(mode, urlFull, true);
if (mode == 'POST') { if (mode == 'POST') {
@ -651,8 +667,8 @@ function initSlaves(objectUrl, layoutType, requestValues, errors) {
function submitAppyForm(button) { function submitAppyForm(button) {
var f = document.getElementById('appyForm'); var f = document.getElementById('appyForm');
// On which button has the user clicked ? // On which button has the user clicked ?
f.button.value = button; f.button.value = button.id;
f.submit(); f.submit(); clickOn(button);
} }
function submitForm(formId, msg, showComment, back) { function submitForm(formId, msg, showComment, back) {
@ -661,8 +677,8 @@ function submitForm(formId, msg, showComment, back) {
/* Submit the form and either refresh the entire page (back is null) /* Submit the form and either refresh the entire page (back is null)
or ajax-refresh a given part only (p_back corresponds to the id of the or ajax-refresh a given part only (p_back corresponds to the id of the
DOM node to be refreshed. */ DOM node to be refreshed. */
if (back) askAjax(back, formId); if (back) { askAjax(back, formId); }
else f.submit(); else { f.submit(); clickOn(f) }
} }
else { else {
// Ask a confirmation to the user before proceeding // Ask a confirmation to the user before proceeding
@ -674,9 +690,9 @@ function submitForm(formId, msg, showComment, back) {
} }
// Function used for triggering a workflow transition // Function used for triggering a workflow transition
function triggerTransition(formId, transitionId, msg, back) { function triggerTransition(formId, node, msg, back) {
var f = document.getElementById(formId); var f = document.getElementById(formId);
f.transition.value = transitionId; f.transition.value = node.id;
submitForm(formId, msg, true, back); submitForm(formId, msg, true, back);
} }
@ -789,8 +805,17 @@ function toggleCookie(cookieId, display, defaultValue) {
createCookie(cookieId, newState); createCookie(cookieId, newState);
} }
function podDownloadStatus(node, data) {
// Checks the status of cookie "podDownload"
var status = readCookie('podDownload');
// Stop the timeout if the download is complete
if (status == 'false') return;
clearInterval(podTimeout);
for (var key in data) node.setAttribute(key, data[key]);
}
// Function that allows to generate a document from a pod template // Function that allows to generate a document from a pod template
function generatePod(uid, fieldName, template, podFormat, queryData, function generatePod(node, uid, fieldName, template, podFormat, queryData,
customParams, getChecked, mailing) { customParams, getChecked, mailing) {
var f = document.getElementById('podForm'); var f = document.getElementById('podForm');
f.objectUid.value = uid; f.objectUid.value = uid;
@ -814,7 +839,22 @@ function generatePod(uid, fieldName, template, podFormat, queryData,
f.checkedSem.value = node['_appy_objs_sem']; f.checkedSem.value = node['_appy_objs_sem'];
} }
} }
// Submitting the form at the end blocks the animated gifs on FF
f.submit(); f.submit();
// If p_node is an image, replace it with a preloader to prevent double-clicks
if (node.tagName == 'IMG') {
var data = {'src': node.src, 'class': node.className,
'onclick': node.attributes.onclick.value};
node.setAttribute('onclick', '');
node.className = '';
var src2 = node.src.replace(podFormat + '.png', 'loadingPod.gif');
node.setAttribute('src', src2);
// Initialize the pod download cookie. "false" means: not downloaded yet
createCookie('podDownload', 'false');
// Set a timer that will check the cookie value
podTimeout = window.setInterval(function(){
podDownloadStatus(node, data)}, 700);
}
} }
// Function that allows to (un-)freeze a document from a pod template // Function that allows to (un-)freeze a document from a pod template
@ -961,7 +1001,7 @@ function doConfirm() {
from the popup when relevant. */ from the popup when relevant. */
var f = document.getElementById(action); var f = document.getElementById(action);
transferComment(confirmForm, f); transferComment(confirmForm, f);
f.submit(); f.submit(); clickOn(f);
} }
else if (actionType == 'url') { goto(action) } // Go to some URL else if (actionType == 'url') { goto(action) } // Go to some URL
else if (actionType == 'script') { eval(action) } // Exec some JS code else if (actionType == 'script') { eval(action) } // Exec some JS code
@ -970,7 +1010,7 @@ function doConfirm() {
var f = document.getElementById(elems[0]); var f = document.getElementById(elems[0]);
// Submit the form in elems[0] and execute the JS code in elems[1] // Submit the form in elems[0] and execute the JS code in elems[1]
transferComment(confirmForm, f); transferComment(confirmForm, f);
f.submit(); f.submit(); clickOn(f);
eval(elems[1]); eval(elems[1]);
} }
else if (actionType == 'form-script') { else if (actionType == 'form-script') {
@ -1031,13 +1071,13 @@ function manageTab(tabId, action) {
function showTab(tabId) { function showTab(tabId) {
// 1st, show the tab to show // 1st, show the tab to show
manageTab(tabId, 'show'); manageTab(tabId, 'show');
// Compute the number of tabs. // Compute the number of tabs
var idParts = tabId.split('_'); var idParts = tabId.split('_');
var prefix = idParts[0] + '_'; var prefix = idParts[0] + '_';
// Store the currently selected tab in a cookie. // Store the currently selected tab in a cookie
createCookie('tab_' + idParts[0], tabId); createCookie('tab_' + idParts[0], tabId);
var nbOfTabs = idParts[2]*1; var nbOfTabs = idParts[2]*1;
// Then, hide the other tabs. // Then, hide the other tabs
for (var i=0; i<nbOfTabs; i++) { for (var i=0; i<nbOfTabs; i++) {
var idTab = prefix + (i+1) + '_' + nbOfTabs; var idTab = prefix + (i+1) + '_' + nbOfTabs;
if (idTab != tabId) { if (idTab != tabId) {
@ -1238,7 +1278,6 @@ function reindexObject(indexName){
} }
// Live-search-related functions (LS) // Live-search-related functions (LS)
var lsTimeout;
function detectEventType(event) { function detectEventType(event) {
/* After p_event occurred on a live search input field, must we trigger a /* After p_event occurred on a live search input field, must we trigger a
search (a new char has been added), move up/down within the search search (a new char has been added), move up/down within the search

BIN
gen/ui/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
gen/ui/loadingBig.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
gen/ui/loadingBtn.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
gen/ui/loadingPod.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -223,6 +223,7 @@ class ToolWrapper(AbstractWrapper):
searchInfo.default.name or ''" searchInfo.default.name or ''"
href=":'%s?className=%s&amp;search=%s' % \ href=":'%s?className=%s&amp;search=%s' % \
(queryUrl, className, queryParam)" (queryUrl, className, queryParam)"
onclick="clickOn(this)"
class=":(not currentSearch and (currentClass==className) and \ class=":(not currentSearch and (currentClass==className) and \
(currentPage=='query')) and \ (currentPage=='query')) and \
'current' or ''">::_(className + '_plural')</a> 'current' or ''">::_(className + '_plural')</a>

View file

@ -457,35 +457,35 @@ class AbstractWrapper(object):
css=ztool.getButtonCss(label, small=False)"> css=ztool.getButtonCss(label, small=False)">
<!-- Button on the edit page --> <!-- Button on the edit page -->
<x if="isEdit"> <x if="isEdit">
<input type="button" class=":css" value=":label" <input type="button" class=":css" value=":label" id="previous"
onclick="submitAppyForm('previous')" onclick="submitAppyForm(this)"
style=":url('previous', bg=True)"/> style=":url('previous', bg=True)"/>
<input type="hidden" name="previousPage" value=":previousPage"/> <input type="hidden" name="previousPage" value=":previousPage"/>
</x> </x>
<!-- Button on the view page --> <!-- Button on the view page -->
<input if="not isEdit" type="button" class=":css" value=":label" <input if="not isEdit" type="button" class=":css" value=":label"
style=":url('previous', bg=True)" style=":url('previous', bg=True)" id="previous"
onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage, \ onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage, \
inPopup=inPopup))"/> inPopup=inPopup))"/>
</x> </x>
<!-- Save --> <!-- Save -->
<input if="isEdit and pageInfo.showSave" type="button" <input if="isEdit and pageInfo.showSave" type="button" id="save"
var2="label=_('object_save'); var2="label=_('object_save');
css=ztool.getButtonCss(label, small=False)" css=ztool.getButtonCss(label, small=False)"
class=":css" onclick="submitAppyForm('save')" class=":css" onclick="submitAppyForm(this)"
value=":label" style=":url('save', bg=True)" /> value=":label" style=":url('save', bg=True)" />
<!-- Cancel --> <!-- Cancel -->
<input if="isEdit and pageInfo.showCancel" type="button" <input if="isEdit and pageInfo.showCancel" type="button" id="cancel"
var2="label=_('object_cancel'); var2="label=_('object_cancel');
css=ztool.getButtonCss(label, small=False)" css=ztool.getButtonCss(label, small=False)"
class=":css" onclick="submitAppyForm('cancel')" value=":label" class=":css" onclick="submitAppyForm(this)" value=":label"
style=":url('cancel', bg=True)"/> style=":url('cancel', bg=True)"/>
<x if="not isEdit" <x if="not isEdit"
var2="locked=zobj.isLocked(user, page); var2="locked=zobj.isLocked(user, page);
editable=pageInfo.showOnEdit and pageInfo.showEdit and \ editable=pageInfo.showOnEdit and pageInfo.showEdit and \
mayAct and zobj.mayEdit()"> mayAct and zobj.mayEdit()">
<!-- Edit --> <!-- Edit -->
<input if="editable and not locked" type="button" <input if="editable and not locked" type="button" id="edit"
var="label=_('object_edit'); var="label=_('object_edit');
css=ztool.getButtonCss(label, small=False)" css=ztool.getButtonCss(label, small=False)"
value=":label" class=":css" style=":url('edit', bg=True)" value=":label" class=":css" style=":url('edit', bg=True)"
@ -511,18 +511,18 @@ class AbstractWrapper(object):
value=":label" class=":css" style=":url('delete', bg=True)" value=":label" class=":css" style=":url('delete', bg=True)"
onclick=":'onDeleteObject(%s)' % q(zobj.id)"/> onclick=":'onDeleteObject(%s)' % q(zobj.id)"/>
<!-- Next --> <!-- Next -->
<x if="nextPage and pageInfo.showNext" <x if="nextPage and pageInfo.showNext" id="next"
var2="label=_('page_next'); var2="label=_('page_next');
css=ztool.getButtonCss(label, small=False)"> css=ztool.getButtonCss(label, small=False)">
<!-- Button on the edit page --> <!-- Button on the edit page -->
<x if="isEdit"> <x if="isEdit">
<input type="button" class=":css" onclick="submitAppyForm('next')" <input type="button" class=":css" onclick="submitAppyForm(this)"
style=":url('next', bg=True)" value=":label"/> id="next" style=":url('next', bg=True)" value=":label"/>
<input type="hidden" name="nextPage" value=":nextPage"/> <input type="hidden" name="nextPage" value=":nextPage"/>
</x> </x>
<!-- Button on the view page --> <!-- Button on the view page -->
<input if="not isEdit" type="button" class=":css" value=":label" <input if="not isEdit" type="button" class=":css" value=":label"
style=":url('next', bg=True)" style=":url('next', bg=True)" id="next"
onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage, \ onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage, \
inPopup=inPopup))"/> inPopup=inPopup))"/>
</x> </x>