Added 'confirm' param to Action fields. If True, a confirmation popup will be shown before triggering the action.

This commit is contained in:
Gaetan Delannay 2010-03-29 20:44:28 +02:00
parent 3a7b5be03b
commit 0f2c4a1e34
9 changed files with 89 additions and 130 deletions

View file

@ -403,7 +403,7 @@ class Action(Type):
page='main', group=None, move=0, indexed=False,
searchable=False, specificReadPermission=False,
specificWritePermission=False, width=None, height=None,
action=None, result='computation', master=None,
action=None, result='computation', confirm=False, master=None,
masterValue=None, focus=False, historized=False):
Type.__init__(self, None, (0,1), index, default, optional,
False, show, page, group, move, indexed, False,
@ -414,6 +414,8 @@ class Action(Type):
# compute things and redirect the user to the same page, with some
# status message about execution of the action. 'file' means that the
# result is the binary content of a file that the user will download.
self.confirm = confirm # If True, a popup will ask the user if she is
# really sure about triggering this action.
def __call__(self, obj):
'''Calls the action on p_obj.'''

View file

@ -142,6 +142,9 @@ class Generator(AbstractGenerator):
msg('goto_last', '', msg.GOTO_LAST),
msg('goto_source', '', msg.GOTO_SOURCE),
msg('whatever', '', msg.WHATEVER),
msg('confirm', '', msg.CONFIRM),
msg('yes', '', msg.YES),
msg('no', '', msg.NO),
]
# Create basic files (config.py, Install.py, etc)
self.generateTool()

View file

@ -544,9 +544,10 @@ class AbstractMixin:
res.append(transition)
return res
def getAppyPage(self, isEdit, phaseInfo, appyName=True):
def getAppyPage(self, isEdit, phaseInfo, appyPages, appyName=True):
'''On which page am I? p_isEdit indicates if the current page is an
edit or consult view. p_phaseInfo indicates the current phase.'''
edit or consult view. p_phaseInfo indicates the current phase.
p_appyPages is the list of displayable appy pages.'''
pageAttr = 'pageName'
if isEdit:
pageAttr = 'fieldset' # Archetypes page name
@ -555,6 +556,11 @@ class AbstractMixin:
res = self.REQUEST.get(pageAttr, default)
if appyName and (res == 'default'):
res = 'main'
# If the page is not among currently displayable pages, return the
# default page
if res not in appyPages:
res = 'main'
if not appyName: res = 'default'
return res
def getAppyPages(self, phase='main'):

View file

@ -17,7 +17,8 @@
js python:contextObj.getUniqueWidgetAttr(fields, 'helper_js');
phaseInfo python: contextObj.getAppyPhases(fieldset=fieldset, forPlone=True);
phase request/phase|phaseInfo/name;
pageName python: contextObj.getAppyPage(isEdit, phaseInfo);">
appyPages python: contextObj.getAppyPages(phase);
pageName python: contextObj.getAppyPage(isEdit, phaseInfo, appyPages);">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"

View file

@ -85,11 +85,15 @@
<div metal:define-macro="showActionField">
<form name="executeAppyAction"
tal:attributes="action python: contextObj.absolute_url()+'/skyn/do'">
tal:define="formId python: '%s_%s' % (contextObj.UID(), field.getName())"
tal:attributes="id formId; action python: contextObj.absolute_url()+'/skyn/do'">
<input type="hidden" name="action" value="ExecuteAppyAction"/>
<input type="hidden" name="objectUid" tal:attributes="value contextObj/UID"/>
<input type="hidden" name="fieldName" tal:attributes="value field/getName"/>
<input type="submit" name="do" tal:attributes="value label" onClick="javascript:;"/>
<input type="button" tal:condition="appyType/confirm"
tal:attributes="value label; onClick python: 'javascript:askConfirm(\'%s\')' % formId"/>
<input type="submit" name="do" tal:condition="not: appyType/confirm"
tal:attributes="value label" onClick="javascript:;"/>
<tal:comment replace="nothing">The previous onClick is simply used to prevent Plone
from adding a CSS class that displays a popup when the user triggers the form multiple
times.</tal:comment>
@ -506,6 +510,42 @@
}
theForm.submit();
}
// Functions for opening and closing a popup
function openPopup(popupId) {
// Open the popup
var popup = document.getElementById(popupId);
// Put it at the right place on the screen
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || 0;
popup.style.top = (scrollTop + 150) + 'px';
popup.style.display = "block";
// Show the greyed zone
var greyed = document.getElementById('appyGrey');
greyed.style.top = scrollTop + 'px';
greyed.style.display = "block";
}
function closePopup(popupId) {
// Close the popup
var popup = document.getElementById(popupId);
popup.style.display = "none";
// Hide the greyed zone
var greyed = document.getElementById('appyGrey');
greyed.style.display = "none";
}
// Function triggered when an action needs to be confirmed by the user
function askConfirm(formId) {
// Store the ID of the form to send if the users confirms.
var confirmForm = document.getElementById('confirmActionForm');
confirmForm.actionFormId.value = formId;
openPopup("confirmActionPopup");
}
// Function triggered when an action confirmed by the user must be performed
function doConfirm() {
// The user confirmed: retrieve the form to send and send it.
var confirmForm = document.getElementById('confirmActionForm');
var actionFormId = confirmForm.actionFormId.value;
var actionForm = document.getElementById(actionFormId);
actionForm.submit();
}
-->
</script>
<tal:comment replace="nothing">Global form for deleting an object</tal:comment>
@ -526,8 +566,7 @@
</div>
<div metal:define-macro="showPageHeader"
tal:define="appyPages python: contextObj.getAppyPages(phase);
showCommonInfo python: not isEdit;
tal:define="showCommonInfo python: not isEdit;
hasHistory contextObj/hasHistory;
historyExpanded python: tool.getCookieValue('appyHistory', default='collapsed') == 'expanded';
creator contextObj/Creator"
@ -914,126 +953,6 @@
</form>
</metal:transitions>
<metal:portletContent define-macro="portletContent"
tal:define="queryUrl python: '%s/skyn/query' % appFolder.absolute_url();
currentSearch request/search|nothing;
currentType request/type_name|nothing;">
<tal:comment replace="nothing">Portlet title, with link to tool.</tal:comment>
<dt class="portletHeader">
<tal:comment replace="nothing">If there is only one flavour, clicking on the portlet
title allows to see all root objects in the database.</tal:comment>
<table cellpadding="0" cellspacing="0" width="100%">
<tr>
<td>
<a tal:condition="python: len(flavours)==1"
tal:attributes="href python:'%s?type_name=%s&flavourNumber=1' % (queryUrl, ','.join(rootClasses))"
tal:content="python: tool.translate(appName)"></a>
<span tal:condition="python: len(flavours)&gt;1"
tal:replace="python: tool.translate(appName)"/>
</td>
<td align="right">
<img style="cursor:pointer"
tal:condition="python: member.has_role('Manager')"
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/view\'' % tool.absolute_url();
title python: tool.translate('%sTool' % appName);
src string:$portal_url/skyn/appyConfig.gif"/>
</td>
</dt>
<tal:comment replace="nothing">TODO: implement a widget for selecting the needed flavour.</tal:comment>
<tal:comment replace="nothing">Create a section for every root class.</tal:comment>
<tal:section repeat="rootClass rootClasses"
define="flavourNumber python:1;
flavour python: tool.getFlavour('Dummy_%d' % flavourNumber)">
<tal:comment replace="nothing">Section title, with action icons</tal:comment>
<dt tal:attributes="class python:test(repeat['rootClass'].number()==1, 'portletAppyItem', 'portletAppyItem portletSep')">
<table width="100%" cellspacing="0" cellpadding="0" class="no-style-table">
<tr>
<td>
<a tal:attributes="href python: '%s?type_name=%s&flavourNumber=%s' % (queryUrl, rootClass, flavourNumber);
class python:test(not currentSearch and (currentType==rootClass), 'portletCurrent', '')"
tal:content="python: tool.translate(rootClass + '_plural')"></a>
</td>
<td align="right"
tal:define="addPermission python: '%s: Add %s' % (appName, rootClass);
userMayAdd python: member.has_permission(addPermission, appFolder);
createMeans python: tool.getCreateMeans(rootClass)">
<tal:comment replace="nothing">Create a new object from a web form</tal:comment>
<img style="cursor:pointer"
tal:condition="python: ('form' in createMeans) and userMayAdd"
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/do?action=Create&type_name=%s\'' % (appFolder.absolute_url(), rootClass);
src string: $portal_url/skyn/plus.png;
title python: tool.translate('query_create')"/>
<tal:comment replace="nothing">Create (a) new object(s) by importing data</tal:comment>
<img style="cursor:pointer"
tal:condition="python: ('import' in createMeans) and userMayAdd"
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/import?type_name=%s\'' % (appFolder.absolute_url(), rootClass);
src string: $portal_url/skyn/import.png;
title python: tool.translate('query_import')"/>
<tal:comment replace="nothing">Search objects of this type (todo: update flavourNumber)</tal:comment>
<img style="cursor:pointer"
tal:define="showSearch python: flavour.getAttr('enableAdvancedSearchFor%s' % rootClass)"
tal:condition="showSearch"
tal:attributes="onClick python: 'href: window.location=\'%s/skyn/search?type_name=%s&flavourNumber=1\'' % (appFolder.absolute_url(), rootClass);
src string: $portal_url/skyn/search.gif;
title python: tool.translate('search_objects')"/>
</td>
</tr>
</table>
</dt>
<tal:comment replace="nothing">Searches for this content type.</tal:comment>
<tal:searchOrGroup repeat="searchOrGroup python: tool.getSearches(rootClass)">
<tal:group condition="searchOrGroup/isGroup">
<tal:expanded define="group searchOrGroup;
expanded python: tool.getCookieValue(group['labelId'], default='collapsed') == 'expanded'">
<tal:comment replace="nothing">Group name</tal:comment>
<dt class="portletAppyItem portletGroup">
<img align="left" style="cursor:pointer"
tal:attributes="id python: '%s_img' % group['labelId'];
src python:test(expanded, 'skyn/collapse.gif', 'skyn/expand.gif');
onClick python:'javascript:toggleCookie(\'%s\')' % group['labelId']"/>&nbsp;
<span tal:replace="group/label"/>
</dt>
<tal:comment replace="nothing">Group searches</tal:comment>
<span tal:attributes="id group/labelId;
style python:test(expanded, 'display:block', 'display:none')">
<dt class="portletAppyItem portletSearch portletGroupItem" tal:repeat="search group/searches">
<a tal:attributes="href python: '%s?type_name=%s&flavourNumber=%s&search=%s' % (queryUrl, rootClass, flavourNumber, search['name']);
title search/descr;
class python: test(search['name'] == currentSearch, 'portletCurrent', '');"
tal:content="structure search/label"></a>
</dt>
</span>
</tal:expanded>
</tal:group>
<dt tal:define="search searchOrGroup" tal:condition="not: searchOrGroup/isGroup"
class="portletAppyItem portletSearch">
<a tal:attributes="href python: '%s?type_name=%s&flavourNumber=%s&search=%s' % (queryUrl, rootClass, flavourNumber, search['name']);
title search/descr;
class python: test(search['name'] == currentSearch, 'portletCurrent', '');"
tal:content="structure search/label"></a>
</dt>
</tal:searchOrGroup>
</tal:section>
<tal:comment replace="nothing">All objects in flavour</tal:comment>
<!--dt class="portletAppyItem" tal:define="flavourInfo python: flavours[0]">
<a tal:define="flavourNumber flavourInfo/number;
rootTypes python: test(flavourNumber==1, rootClasses, ['%s_%s' % (rc, flavourNumber) for rc in rootClasses]);
rootClassesQuery python:','.join(rootTypes)"
tal:content="flavourInfo/title"
tal:attributes="title python: tool.translate('query_consult_all');
href python:'%s?type_name=%s&flavourNumber=%d' % (queryUrl, rootClassesQuery, flavourNumber)"></a>
</dt-->
<dt class="portletAppyItem" tal:define="contextObj tool/getPublishedObject"
tal:condition="python: contextObj.meta_type in rootClasses">
<metal:phases use-macro="here/skyn/macros/macros/phases"/>
</dt>
</metal:portletContent>
<div metal:define-macro="appyNavigate" tal:condition="python: totalNumber &gt; batchSize" align="right">
<tal:comment replace="nothing">
Buttons for navigating among a list of elements (next, back, first, last, etc).

View file

@ -25,7 +25,8 @@
appName appFolder/id;
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, forPlone=False);
phase request/phase|phaseInfo/name;
pageName python: contextObj.getAppyPage(isEdit, phaseInfo);
appyPages python: contextObj.getAppyPages(phase);
pageName python: contextObj.getAppyPage(isEdit, phaseInfo, appyPages);
showWorkflow python: flavour.getAttr('showWorkflowFor' + contextObj.meta_type)">
<metal:prologue use-macro="here/skyn/macros/macros/pagePrologue"/>
<metal:header use-macro="here/skyn/macros/macros/showPageHeader"/>

View file

@ -12,7 +12,7 @@
appName string:<!applicationName!>;
appFolder tool/getAppFolder;
flavours tool/getFlavoursInfo" class="portlet">
<metal:content use-macro="here/skyn/macros/macros/portletContent"/>
<metal:content use-macro="here/skyn/portlet/macros/portletContent"/>
</dl>
</div>
</body>

View file

@ -232,6 +232,30 @@ fieldset {
.portletCurrent {
font-weight: bold;
}
div.appyGrey {
display: none;
position: absolute;
left: 0px;
top: 0px;
width:100%;
height:100%;
background:gray;
filter:alpha(Opacity=50);
opacity:0.5;
-moz-opacity:0.5;
-khtml-opacity:0.5
}
div.appyPopup {
display: none;
position: absolute;
top: 30%;
left: 35%;
width: 350px;
z-index : 100;
background: white;
padding: 8px;
border: 1px solid gray;
}
/* Uncomment this if you want to hide breadcrumbs */
/*

View file

@ -100,6 +100,9 @@ class PoMessage:
GOTO_LAST = 'Go to end'
GOTO_SOURCE = 'Go back'
WHATEVER = 'Whatever'
CONFIRM = 'Are you sure ?'
YES = 'Yes'
NO = 'No'
def __init__(self, id, msg, default, fuzzy=False, comments=[]):
self.id = id