[gen] FInalized ajaxification of search rows and rows of tied objects in Refs.

This commit is contained in:
Gaetan Delannay 2015-02-04 18:33:27 +01:00
parent f44bb4e06b
commit a6645f33ff
7 changed files with 87 additions and 58 deletions

View file

@ -115,10 +115,12 @@ class Ref(Field):
<img src=":url('edit')" title=":_('object_edit')"/>
</a>
<!-- Delete -->
<img var="mayDeleteViaField=inPickList and True or field.delete"
<img var="mayDeleteViaField=inPickList and True or field.delete;
back=(inMenu and (layoutType=='buttons')) and \
q(zobj.id) or 'null'"
if="mayEdit and mayDeleteViaField and tied.o.mayDelete()"
class="clickable" title=":_('object_delete')" src=":url('delete')"
onclick=":'onDeleteObject(%s)' % q(tiedUid)"/>
onclick=":'onDeleteObject(%s,%s)' % (q(tiedUid), back)"/>
<!-- Unlink -->
<img if="mayUnlink and field.mayUnlinkElement(obj, tied)"
var2="imgName=linkList and 'unlinkUp' or 'unlink'; action='unlink'"
@ -313,7 +315,8 @@ class Ref(Field):
batchNumber=len(objects);
tiedClassName=tiedClassName|ztool.getPortalType(field.klass);
tiedClassLabel=tiedClassLabel|_(tiedClassName);
target=ztool.getLinksTargetInfo(field.klass);
backHook=(layoutType == 'cell') and zobj.id or None;
target=ztool.getLinksTargetInfo(field.klass, backHook);
mayEdit=mayEdit|\
not field.isBack and zobj.mayEdit(field.writePermission);
mayAdd=False;
@ -411,7 +414,8 @@ class Ref(Field):
folder=zobj.getCreateFolder();
tiedClassName=ztool.getPortalType(field.klass);
tiedClassLabel=_(tiedClassName);
target=ztool.getLinksTargetInfo(field.klass);
backHook=(layoutType == 'cell') and zobj.id or None;
target=ztool.getLinksTargetInfo(field.klass, backHook);
mayEdit=not field.isBack and zobj.mayEdit(field.writePermission);
mayAdd=mayEdit and field.mayAdd(zobj, checkMayEdit=False);
mayLink=mayEdit and field.mayAdd(zobj, mode='link', \
@ -1274,7 +1278,7 @@ class Ref(Field):
params = sutils.getStringDict(params)
px = hook.endswith('_poss') and 'pxViewPickList' or 'pxView'
px = '%s:%s' % (self.name, px)
return "document.getElementById('%s')['ajax']=new AjaxData('%s', " \
return "getAjaxHook('%s',true)['ajax']=new AjaxData('%s', " \
"'%s', %s, null, '%s')" % \
(hook, hook, px, params, zobj.absolute_url())
@ -1282,7 +1286,7 @@ class Ref(Field):
'''Initializes an AjaxData object on the DOM node corresponding to
p_hook = a row within the list of referred objects.'''
hook = obj.id
return "document.getElementById('%s')['ajax']=new AjaxData('%s', " \
return "getAjaxHook('%s',true)['ajax']=new AjaxData('%s', " \
"'pxViewAsTiedFromAjax',%s,'%s','%s')" % \
(hook, hook, sutils.getStringDict(params), parentHook, obj.url)

View file

@ -400,7 +400,7 @@ class UiSearch:
params['className'] = self.className
params['searchName'] = self.name
params = sutils.getStringDict(params)
return "document.getElementById('%s')['ajax']=new AjaxData('%s', " \
return "getAjaxHook('%s',true)['ajax']=new AjaxData('%s', " \
"'pxResult', %s, null, '%s')" % \
(hook, hook, params, ztool.absolute_url())
@ -408,7 +408,7 @@ class UiSearch:
'''Initializes an AjaxData object on the DOM node corresponding to
p_hook = a row within the list of results.'''
hook = zobj.id
return "document.getElementById('%s')['ajax']=new AjaxData('%s', " \
return "getAjaxHook('%s',true)['ajax']=new AjaxData('%s', " \
"'pxViewAsResultFromAjax',%s,'%s','%s')" % \
(hook, hook, sutils.getStringDict(params), parentHook,
zobj.absolute_url())

View file

@ -1242,13 +1242,13 @@ class ToolMixin(BaseMixin):
if len(label) < 15: return 'buttonFixed button'
return 'button'
def getLinksTargetInfo(self, klass):
def getLinksTargetInfo(self, klass, back=None):
'''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
- openPopup is unused if target is "_self" or contains the
Javascript code to open the popup.'''
res = Object(target='_self', openPopup='')
if hasattr(klass, 'popup'):
@ -1256,10 +1256,14 @@ class ToolMixin(BaseMixin):
d = klass.popup
if isinstance(d, basestring):
# Width only
params = int(d[:-2])
params = d[:-2]
else:
# Width and height
params = "%s, %s" % (d[0][:-2], d[1][:-2])
# If "back" is specified, it corresponds to some tag on the main
# page, that must be ajax-refreshed when coming back from the popup.
# Else, when back, the entire page will be reloaded.
if back: params += ", '%s'" % back
res.openPopup = "openPopup('iframePopup',null,%s)" % params
return res

View file

@ -151,17 +151,22 @@ class BaseMixin:
self.getParentNode().manage_delObjects([self.id])
def onDelete(self):
'''Called when an object deletion is triggered from the ui.'''
'''Called when an object deletion is triggered from the ui'''
rq = self.REQUEST
self.delete()
if self.getUrl(rq['HTTP_REFERER'],mode='raw') ==self.getUrl(mode='raw'):
tool = self.getTool()
obj = tool.getObject(rq['uid'])
obj.delete()
msg = obj.translate('action_done')
# If we are called from an Ajax request, simply return msg
if hasattr(rq, 'pxContext') and rq.pxContext['ajax']: return msg
if obj.getUrl(rq['HTTP_REFERER'], mode='raw') == obj.getUrl(mode='raw'):
# We were consulting the object that has been deleted. Go back to
# the main page.
urlBack = self.getTool().getSiteUrl()
urlBack = tool.getSiteUrl()
else:
urlBack = self.getUrl(rq['HTTP_REFERER'])
self.say(self.translate('action_done'))
self.goto(urlBack)
urlBack = obj.getUrl(rq['HTTP_REFERER'])
obj.say(msg)
obj.goto(urlBack)
def onDeleteEvent(self):
'''Called when an event (from object history) deletion is triggered
@ -395,7 +400,7 @@ class BaseMixin:
else:
if isNew: urlBack = tool.getHomePage() # Go back to home page
else:
# Return to the same page, excepted if unshowable on view.
# Return to the same page, excepted if unshowable on view
phaseObj = self.getAppyPhases(True, 'view')
pageInfo = phaseObj.getPageInfo(rq['page'], 'view')
if not pageInfo: urlBack = tool.getHomePage()

View file

@ -90,10 +90,18 @@ function evalInnerScripts(xhrObject, hookElem) {
for (var i=0; i<scripts.length; i++) { eval(scripts[i].innerHTML) }
}
function injectChunk(elem, content){
if (!isIe) elem.innerHTML = content;
function injectChunk(elem, content, inner, searchTop){
var res = elem;
if (!isIe || (elem.tagName != 'TABLE')) {
if (inner) res.innerHTML = content;
else {
if (elem.tagName != 'TABLE') elem.innerHTML = content;
// Replace p_elem with a new node filled with p_content and return it
var id = elem.id;
if (searchTop) id = ':' + id;
elem.outerHTML = content;
res = getAjaxHook(id); // Get the new element
}
}
else {
/* IE doesn't want to replace content of a table. Force it to do so via
a temporary DOM element. */
@ -102,14 +110,15 @@ function injectChunk(elem, content){
temp.firstChild.id = elem.id;
elem.parentNode.replaceChild(temp.firstChild, elem);
}
}
return res;
}
function getAjaxHook(hookId) {
function getAjaxHook(hookId, forceTop) {
/* 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
the element in the top browser window, not in the current one that can be
an iframe.*/
an iframe. If p_forceTop is true, even if hookId does not start with ':',
if the elem is not found we will search in the top browser window. */
if (!hookId) return;
var container = window.document;
var startIndex = 0;
@ -117,7 +126,10 @@ function getAjaxHook(hookId) {
container = window.top.document;
startIndex = 1;
}
return container.getElementById(hookId.slice(startIndex));
var id = hookId.slice(startIndex);
var res = container.getElementById(id);
if (!res && forceTop) res = window.top.document.getElementById(id);
return res;
}
function getAjaxChunk(pos) {
@ -131,21 +143,18 @@ function getAjaxChunk(pos) {
// 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>");
injectChunk(hookElem, "<div align=\"center\"><img src=\"ui/waiting.gif\"/><\/div>", true);
}
if (xhrObjects[pos].xhr.readyState == 4) {
// We have received the HTML chunk
var hookElem = getAjaxHook(hook);
if (hookElem) {
injectChunk(hookElem, xhrObjects[pos].xhr.responseText);
var content = xhrObjects[pos].xhr.responseText;
var searchTop = hook[0] == ':';
var injected = injectChunk(hookElem, content, false, searchTop);
// Call a custom Javascript function if required
if (xhrObjects[pos].onGet) {
xhrObjects[pos].onGet(xhrObjects[pos], hookElem);
}
// Eval inner scripts if any
var innerScripts = getElementsHavingName('div', 'appyHook');
for (var i=0; i<innerScripts.length; i++) {
eval(innerScripts[i].innerHTML);
xhrObjects[pos].onGet(xhrObjects[pos], injected);
}
// Display the Appy message if present
var msg = xhrObjects[pos].xhr.getResponseHeader('Appy-Message');
@ -245,10 +254,12 @@ function AjaxData(hook, px, params, parentHook, url, mode, beforeSend, onGet) {
function askAjax(hook, form, params) {
/* Call askAjaxChunk by getting an AjaxData instance from p_hook, a
potential action from p_form and additional parameters from p_param. */
var d = document.getElementById(hook)['ajax'];
var d = getAjaxHook(hook)['ajax'];
// Complete data with a parent data if present
if (d['parentHook']) {
var parent = document.getElementById(d['parentHook'])['ajax'];
var parentHook = d['parentHook'];
if (hook[0] == ':') parentHook = ':' + parentHook;
var parent = getAjaxHook(parentHook)['ajax'];
for (var key in parent) {
if (key == 'params') continue; // Will get a specific treatment herafter
if (!d[key]) d[key] = parent[key]; // Override if no value on child
@ -278,7 +289,9 @@ function askAjax(hook, form, params) {
else var mode = d.mode;
// Get p_params if given. Note that they override anything else.
if (params) { for (var key in params) d.params[key] = params[key]; }
askAjaxChunk(d.hook,mode,d.url,d.px,d.params,d.beforeSend,d.onGet) }
askAjaxChunk(hook, mode, d.url, d.px, d.params, d.beforeSend,
evalInnerScripts);
}
/* The functions below wrap askAjaxChunk for getting specific content through
an Ajax request. */
@ -658,10 +671,10 @@ function triggerTransition(formId, transitionId, msg, back) {
submitForm(formId, msg, true, back);
}
function onDeleteObject(objectUid) {
f = document.getElementById('deleteForm');
f.objectUid.value = objectUid;
askConfirm('form', 'deleteForm', action_confirm);
function onDeleteObject(uid, back) {
var f = document.getElementById('deleteForm');
f.uid.value = uid;
submitForm('deleteForm', action_confirm, false, back);
}
function onDeleteEvent(objectUid, eventTime) {
@ -829,7 +842,7 @@ function protectAppyForm() {
}
// Functions for opening and closing a popup
function openPopup(popupId, msg, width, height) {
function openPopup(popupId, msg, width, height, back) {
// Put the message into the popup
if (msg) {
var msgHook = (popupId == 'alertPopup')? 'appyAlertText': 'appyConfirmText';
@ -844,7 +857,7 @@ function openPopup(popupId, msg, width, height) {
if (width) popup.style.width = width + 'px';
if (height) popup.style.height = height + 'px';
if (popupId == 'iframePopup') {
// Initialize iframe's width.
// Initialize iframe's width
var iframe = document.getElementById('appyIFrame');
if (!width) width = window.innerWidth - 200;
if (!height) {
@ -856,6 +869,7 @@ function openPopup(popupId, msg, width, height) {
iframe.style.width = (width-20) + 'px';
popup.style.height = height + 'px';
iframe.style.height = (height-20) + 'px';
popup['back'] = back;
}
popup.style.display = 'block';
}
@ -882,11 +896,13 @@ function closePopup(popupId, clean) {
// Leave the form silently if we are on an edit page
iframe.contentWindow.onbeforeunload = null;
}
return popup;
}
function backFromPopup() {
closePopup('iframePopup');
window.parent.location = window.parent.location;
var popup = closePopup('iframePopup');
if (popup['back']) askAjax(':'+popup['back']);
else window.parent.location = window.parent.location;
}
function showAppyMessage(message) {

View file

@ -106,9 +106,8 @@ class ToolWrapper(AbstractWrapper):
<script>::ztool.getJavascriptMessages()</script>
<!-- Global form for deleting an object -->
<form id="deleteForm" method="post" action="do">
<input type="hidden" name="action" value="Delete"/>
<input type="hidden" name="objectUid"/>
<form id="deleteForm" method="post" action=":'%s/onDelete' % tool.url">
<input type="hidden" name="uid"/>
</form>
<!-- Global form for deleting an event from an object's history -->
<form id="deleteEventForm" method="post" action="do">
@ -133,7 +132,7 @@ class ToolWrapper(AbstractWrapper):
</form>
<!-- Global form for generating/freezing a document from a pod template -->
<form id="podForm" name="podForm" method="post"
action=":ztool.absolute_url() + '/doPod'">
action=":'%s/doPod' % tool.url">
<input type="hidden" name="objectUid"/>
<input type="hidden" name="fieldName"/>
<input type="hidden" name="template"/>

View file

@ -602,13 +602,14 @@ class AbstractWrapper(object):
tied=obj;
zobj=ztool.getObject(sourceId);
obj=zobj.appy();
inMenu=False;
field=zobj.getAppyType(refFieldName);
layoutType='view';
render=field.getRenderMode(layoutType);
linkList=field.link == 'list';
numberWidth=len(str(totalNumber));
tiedClassName=ztool.getPortalType(field.klass);
target=ztool.getLinksTargetInfo(field.klass);
target=ztool.getLinksTargetInfo(field.klass, zobj.id);
mayEdit=not field.isBack and zobj.mayEdit(field.writePermission);
mayLink=not inPickList and mayEdit and \
field.mayAdd(zobj, mode='link', checkMayEdit=False);