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

View file

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

View file

@ -1242,13 +1242,13 @@ class ToolMixin(BaseMixin):
if len(label) < 15: return 'buttonFixed button' if len(label) < 15: return 'buttonFixed button'
return '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 '''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 either via the same browser window, or via a popup. This method
returns info about that, as an object having 2 attributes: returns info about that, as an object having 2 attributes:
- target is "_self" if the link leads to the same browser window, - target is "_self" if the link leads to the same browser window,
"appyIFrame" if the link must be opened in a popup; "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.''' Javascript code to open the popup.'''
res = Object(target='_self', openPopup='') res = Object(target='_self', openPopup='')
if hasattr(klass, 'popup'): if hasattr(klass, 'popup'):
@ -1256,10 +1256,14 @@ class ToolMixin(BaseMixin):
d = klass.popup d = klass.popup
if isinstance(d, basestring): if isinstance(d, basestring):
# Width only # Width only
params = int(d[:-2]) params = d[:-2]
else: else:
# Width and height # Width and height
params = "%s, %s" % (d[0][:-2], d[1][:-2]) 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 res.openPopup = "openPopup('iframePopup',null,%s)" % params
return res return res

View file

@ -151,17 +151,22 @@ class BaseMixin:
self.getParentNode().manage_delObjects([self.id]) self.getParentNode().manage_delObjects([self.id])
def onDelete(self): 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 rq = self.REQUEST
self.delete() tool = self.getTool()
if self.getUrl(rq['HTTP_REFERER'],mode='raw') ==self.getUrl(mode='raw'): 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 # We were consulting the object that has been deleted. Go back to
# the main page. # the main page.
urlBack = self.getTool().getSiteUrl() urlBack = tool.getSiteUrl()
else: else:
urlBack = self.getUrl(rq['HTTP_REFERER']) urlBack = obj.getUrl(rq['HTTP_REFERER'])
self.say(self.translate('action_done')) obj.say(msg)
self.goto(urlBack) obj.goto(urlBack)
def onDeleteEvent(self): def onDeleteEvent(self):
'''Called when an event (from object history) deletion is triggered '''Called when an event (from object history) deletion is triggered
@ -395,7 +400,7 @@ class BaseMixin:
else: else:
if isNew: urlBack = tool.getHomePage() # Go back to home page if isNew: urlBack = tool.getHomePage() # Go back to home page
else: 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') phaseObj = self.getAppyPhases(True, 'view')
pageInfo = phaseObj.getPageInfo(rq['page'], 'view') pageInfo = phaseObj.getPageInfo(rq['page'], 'view')
if not pageInfo: urlBack = tool.getHomePage() 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) } for (var i=0; i<scripts.length; i++) { eval(scripts[i].innerHTML) }
} }
function injectChunk(elem, content){ function injectChunk(elem, content, inner, searchTop){
if (!isIe) elem.innerHTML = content; var res = elem;
if (!isIe || (elem.tagName != 'TABLE')) {
if (inner) res.innerHTML = content;
else { 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 { else {
/* IE doesn't want to replace content of a table. Force it to do so via /* IE doesn't want to replace content of a table. Force it to do so via
a temporary DOM element. */ a temporary DOM element. */
@ -102,14 +110,15 @@ function injectChunk(elem, content){
temp.firstChild.id = elem.id; temp.firstChild.id = elem.id;
elem.parentNode.replaceChild(temp.firstChild, elem); 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 /* 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
the element in the top browser window, not in the current one that can be 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; if (!hookId) return;
var container = window.document; var container = window.document;
var startIndex = 0; var startIndex = 0;
@ -117,7 +126,10 @@ function getAjaxHook(hookId) {
container = window.top.document; container = window.top.document;
startIndex = 1; 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) { function getAjaxChunk(pos) {
@ -131,21 +143,18 @@ function getAjaxChunk(pos) {
// The request has been initialized: display the waiting radar // The request has been initialized: display the waiting radar
var hookElem = getAjaxHook(hook); var hookElem = getAjaxHook(hook);
if (hookElem) 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) { 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(hook);
if (hookElem) { 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 // Call a custom Javascript function if required
if (xhrObjects[pos].onGet) { if (xhrObjects[pos].onGet) {
xhrObjects[pos].onGet(xhrObjects[pos], hookElem); xhrObjects[pos].onGet(xhrObjects[pos], injected);
}
// Eval inner scripts if any
var innerScripts = getElementsHavingName('div', 'appyHook');
for (var i=0; i<innerScripts.length; i++) {
eval(innerScripts[i].innerHTML);
} }
// Display the Appy message if present // Display the Appy message if present
var msg = xhrObjects[pos].xhr.getResponseHeader('Appy-Message'); var msg = xhrObjects[pos].xhr.getResponseHeader('Appy-Message');
@ -156,7 +165,7 @@ function getAjaxChunk(pos) {
} }
} }
function askAjaxChunk(hook,mode,url,px,params,beforeSend,onGet) { function askAjaxChunk(hook, mode, url, px, params, beforeSend, onGet) {
/* This function will ask to get a chunk of XHTML on the server through a /* This function will ask to get a chunk of XHTML on the server through a
XMLHttpRequest. p_mode can be 'GET' or 'POST'. p_url is the URL of a XMLHttpRequest. p_mode can be 'GET' or 'POST'. p_url is the URL of a
given server object. On this object we will call method "ajax" that will given server object. On this object we will call method "ajax" that will
@ -245,10 +254,12 @@ function AjaxData(hook, px, params, parentHook, url, mode, beforeSend, onGet) {
function askAjax(hook, form, params) { function askAjax(hook, form, params) {
/* Call askAjaxChunk by getting an AjaxData instance from p_hook, a /* Call askAjaxChunk by getting an AjaxData instance from p_hook, a
potential action from p_form and additional parameters from p_param. */ 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 // Complete data with a parent data if present
if (d['parentHook']) { 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) { for (var key in parent) {
if (key == 'params') continue; // Will get a specific treatment herafter if (key == 'params') continue; // Will get a specific treatment herafter
if (!d[key]) d[key] = parent[key]; // Override if no value on child 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; else var mode = d.mode;
// Get p_params if given. Note that they override anything else. // Get p_params if given. Note that they override anything else.
if (params) { for (var key in params) d.params[key] = params[key]; } 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 /* The functions below wrap askAjaxChunk for getting specific content through
an Ajax request. */ an Ajax request. */
@ -658,10 +671,10 @@ function triggerTransition(formId, transitionId, msg, back) {
submitForm(formId, msg, true, back); submitForm(formId, msg, true, back);
} }
function onDeleteObject(objectUid) { function onDeleteObject(uid, back) {
f = document.getElementById('deleteForm'); var f = document.getElementById('deleteForm');
f.objectUid.value = objectUid; f.uid.value = uid;
askConfirm('form', 'deleteForm', action_confirm); submitForm('deleteForm', action_confirm, false, back);
} }
function onDeleteEvent(objectUid, eventTime) { function onDeleteEvent(objectUid, eventTime) {
@ -829,7 +842,7 @@ function protectAppyForm() {
} }
// Functions for opening and closing a popup // 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 // Put the message into the popup
if (msg) { if (msg) {
var msgHook = (popupId == 'alertPopup')? 'appyAlertText': 'appyConfirmText'; var msgHook = (popupId == 'alertPopup')? 'appyAlertText': 'appyConfirmText';
@ -844,7 +857,7 @@ function openPopup(popupId, msg, width, height) {
if (width) popup.style.width = width + 'px'; if (width) popup.style.width = width + 'px';
if (height) popup.style.height = height + 'px'; if (height) popup.style.height = height + 'px';
if (popupId == 'iframePopup') { if (popupId == 'iframePopup') {
// Initialize iframe's width. // Initialize iframe's width
var iframe = document.getElementById('appyIFrame'); var iframe = document.getElementById('appyIFrame');
if (!width) width = window.innerWidth - 200; if (!width) width = window.innerWidth - 200;
if (!height) { if (!height) {
@ -856,6 +869,7 @@ function openPopup(popupId, msg, width, height) {
iframe.style.width = (width-20) + 'px'; iframe.style.width = (width-20) + 'px';
popup.style.height = height + 'px'; popup.style.height = height + 'px';
iframe.style.height = (height-20) + 'px'; iframe.style.height = (height-20) + 'px';
popup['back'] = back;
} }
popup.style.display = 'block'; popup.style.display = 'block';
} }
@ -882,11 +896,13 @@ function closePopup(popupId, clean) {
// Leave the form silently if we are on an edit page // Leave the form silently if we are on an edit page
iframe.contentWindow.onbeforeunload = null; iframe.contentWindow.onbeforeunload = null;
} }
return popup;
} }
function backFromPopup() { function backFromPopup() {
closePopup('iframePopup'); var popup = closePopup('iframePopup');
window.parent.location = window.parent.location; if (popup['back']) askAjax(':'+popup['back']);
else window.parent.location = window.parent.location;
} }
function showAppyMessage(message) { function showAppyMessage(message) {

View file

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

View file

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