/*==========================================================================*\ | $Id: ConfirmingAction.java,v 1.6 2010/11/03 19:36:56 aallowat Exp $ |*-------------------------------------------------------------------------*| | Copyright (C) 2009 Virginia Tech | | This file is part of Web-CAT. | | Web-CAT is free software; you can redistribute it and/or modify | it under the terms of the GNU Affero General Public License as published | by the Free Software Foundation; either version 3 of the License, or | (at your option) any later version. | | Web-CAT is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | You should have received a copy of the GNU Affero General Public License | along with Web-CAT; if not, see <http://www.gnu.org/licenses/>. \*==========================================================================*/ package org.webcat.core; import org.webcat.ui.WCButton; import org.webcat.ui.WCForm; import org.webcat.ui.generators.JavascriptFunction; import org.webcat.ui.generators.JavascriptGenerator; import org.webcat.ui.util.JSHash; import com.webobjects.appserver.WOActionResults; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WORequest; import er.extensions.appserver.ERXWOContext; //------------------------------------------------------------------------- /** * <p> * An action wrapper that allows the user to confirm whether they want an * action to be invoked. To use this functionality, assign a {@link WCButton} * with {@code remote=true} to an action on your component, and inside your * action, do the following: * </p> * <pre> * public WOActionResults myAction() * { * return new ConfirmingAction(this, false) * { * protected String confirmationMessage() * { * return "Are you sure you want to do this?"; * } * * protected WOActionResults actionWasConfirmed() * { * // logic to be performed if "Yes" was clicked * return pageWithName(SomeNewPage.class); // or null * } * }; * } * </pre> * <p> * Clicking the button will invoke this action remotely, which will push the * form values into their bindings, and then this class will construct a * confirmation dialog that will be presented to the user. If the user selects * "Yes" in this dialog, then {@link #actionWasInvoked()} will be called * so that the appropriate action can be taken. * </p> * * @author Tony Allevato * @version $Id: ConfirmingAction.java,v 1.6 2010/11/03 19:36:56 aallowat Exp $ */ public abstract class ConfirmingAction extends DualAction { //~ Constructors .......................................................... // ---------------------------------------------------------- /** * Initializes a new instance of the ConfirmingAction class. * * @param component the component on which the action is being invoked * @param isRemote true if the action that should be carried out if the * user clicks "Yes" should be executed remotely; false if it should be * executed as a standard page-load action */ public ConfirmingAction(WOComponent component, boolean isRemote) { super(component); // We have to retrieve this information at the time that this action is // constructed inside the action method on the component, not later on // when the action response is actually being generated, because at // that time the context will not be the correct one. WOContext currentContext = ERXWOContext.currentContext(); formName = WCForm.formName(currentContext, null); elementID = currentContext.elementID(); this.request = currentContext.request(); this.isRemote = isRemote; } //~ Methods ............................................................... // ---------------------------------------------------------- /** * Subclasses can override this method to change the title of the * confirmation dialog. * * @return the title of the confirmation dialog */ protected String confirmationTitle() { return "Confirm Action"; } // ---------------------------------------------------------- /** * Subclasses must override this method to provide the text that will * appear in the confirmation dialog when the action is invoked. * * @return the message that will appear in the confirmation dialog */ protected abstract String confirmationMessage(); // ---------------------------------------------------------- /** * When the action is invoked as an Ajax request, this method displays the * confirmation dialog to the user. The dialog is configured so that when * the Yes button is clicked, this action is invoked again, this time as a * standard action. * * @return a JavascriptGenerator that displays the confirmation dialog */ @Override protected WOActionResults performRemoteAction() { JavascriptGenerator page = new JavascriptGenerator(); if (!showedConfirmationDialog()) { page.confirm(confirmationTitle(), confirmationMessage(), new JavascriptFunction() { @Override public void generate(JavascriptGenerator g) { generateYesHandler(g); } }); } else { // We're in the second remote phase of an action where the use // clicked yes. Call the appropriate method. return actionWasConfirmed(); } return page; } // ---------------------------------------------------------- /** * Indicates whether the confirmation dialog has been shown to the user. * This is only needed in the case where the post-confirmation phase of * the request is also performed remotely, in order to differentiate * between the first phase (displaying the dialog) and the * post-confirmation phase (executing some server-side action). * * @return the action's current state */ private boolean showedConfirmationDialog() { Boolean wasConfirmed = Boolean.parseBoolean( (String) request.formValueForKey( SHOWED_CONFIRMATION_DIALOG_KEY)); if (wasConfirmed == null) { return false; } else { return wasConfirmed; } } // ---------------------------------------------------------- /** * This method calls {@link #actionWasConfirmed()}. Subclasses should * override that method to provide behavior when the dialog is confirmed, * not this method. * * @return the results of the action */ @Override protected WOActionResults performStandardAction() { return actionWasConfirmed(); } // ---------------------------------------------------------- /** * <p> * Generates the Javascript code that should be called immediately after * the user clicks "Yes", but before the associated server-side action is * executed. * </p><p> * The default implementation does nothing; subclasses can override this to * prepare for a potentially long-running operation, by starting a progress * spinner for example. * </p> * * @param page the JavascriptGenerator that will contain code to be * executed immediately when the "Yes" button is clicked */ protected void beforeActionWasConfirmed(JavascriptGenerator page) { // Default implementation does nothing. } // ---------------------------------------------------------- /** * <p> * Generates the Javascript code that should be executed on the client * if the "Yes" button on the confirmation dialog was selected. The * default behavior is to execute a Javascript statement that will cause * the form to be submitted as a standard page-load action (if the action * is not remote) or to re-execute the action remotely (if the action is * remote). Both will cause the {@link #actionWasConfirmed()} method to be * invoked.</p> * * @param page the JavascriptGenerator used to provide the client-side * behavior */ private void generateYesHandler(JavascriptGenerator page) { beforeActionWasConfirmed(page); if (isRemote) { JSHash options = new JSHash(); options.put("url", request.uri()); options.put("submit", JSHash.code("null")); options.put("handleAs", "javascript"); options.put("sender", elementID); JSHash content = new JSHash(); content.put(SHOWED_CONFIRMATION_DIALOG_KEY, true); options.put("content", content); page.remoteSubmit(options); } else { page.submit(formName, elementID); } } // ---------------------------------------------------------- /** * Subclasses should override this in order to execute an action once, and * if, the user selected "Yes" in the confirmation dialog. The default * implementation simply returns null in order to reload the page (in the * case of a non-remote action) or to do nothing (in the case of a remote * action). * * @return the results of the action */ protected WOActionResults actionWasConfirmed() { return null; } //~ Static/instance variables ............................................. private static final String SHOWED_CONFIRMATION_DIALOG_KEY = "org.webcat.core.ConfirmingAction.showedConfirmationDialog"; private boolean isRemote; private WORequest request; private String formName; private String elementID; }