package er.modern.directtoweb.components.buttons; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import com.webobjects.appserver.WOActionResults; import com.webobjects.appserver.WOContext; import com.webobjects.directtoweb.ConfirmPageInterface; import com.webobjects.directtoweb.D2W; import com.webobjects.directtoweb.D2WPage; import com.webobjects.eocontrol.EOClassDescription; import com.webobjects.eocontrol.EODataSource; import com.webobjects.eocontrol.EODetailDataSource; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOEnterpriseObject; import com.webobjects.foundation.NSMutableDictionary; import com.webobjects.foundation.NSNotificationCenter; import com.webobjects.foundation.NSValidation; import er.directtoweb.delegates.ERDDeletionDelegate; import er.directtoweb.delegates.ERDPageDelegate; import er.extensions.eof.ERXEC; import er.extensions.eof.ERXEOControlUtilities; import er.extensions.eof.ERXGuardedObjectInterface; import er.extensions.localization.ERXLocalizer; /** * Delete button for repetitions. * * @binding object * @binding dataSource * @binding displayGroup * @binding d2wContext * * @d2wKey deleteButtonLabel * @d2wKey classForDeleteObjButton * @d2wKey classForDisabledDeleteObjButton * @d2wKey cancelButtonLabel * @d2wKey classForCancelDialogButton * @d2wKey classForDeleteDialogButton * @d2wKey confirmDeleteMessage * * @author davidleber */ public class ERMDDeleteButton extends ERMDActionButton { private static final long serialVersionUID = 1L; public final static String DisplayGroupObjectDeleted = "DisplayGroupObjectDeleted"; public interface Keys extends ERMDActionButton.Keys { public static final String deleteButtonLabel = "deleteButtonLabel"; public static final String classForDeleteObjButton = "classForDeleteObjButton"; public static final String classForDisabledDeleteObjButton = "classForDisabledDeleteObjButton"; public static final String objectPendingDeletion = "objectPendingDeletion"; public static final String cancelButtonLabel = "cancelButtonLabel"; public static final String classForCancelDialogButton = "classForCancelDialogButton"; public static final String classForDeleteDialogButton = "classForDeleteDialogButton"; public static final String confirmDeleteMessage = "confirmDeleteMessage"; } private String _cancelButtonLabel; private String _cancelButtonClass; private String _deleteButtonClass; private String _localUpdateContainer; protected String _dialogMessage; protected String _disabledButtonClass; public ERMDDeleteButton(WOContext context) { super(context); } // ACTIONS /** * Deletes the current object. Behaviour is dependent on the d2wContext useAjaxControls flag. * * if true: Display an in-line confirmation dialog and update the pages main update container. * if false: Take user to the confirmation page. * */ public WOActionResults buttonAction() { WOActionResults result = null; if (shouldUseAjax()) { EOEditingContext ec = ERXEC.newEditingContext(object().editingContext()); EOEnterpriseObject localObj = ERXEOControlUtilities.localInstanceOfObject(ec, object()); d2wContext().takeValueForKey(localObj, Keys.objectPendingDeletion); } else { ConfirmPageInterface nextPage = (ConfirmPageInterface)D2W.factory().pageForConfigurationNamed((String)valueForBinding("confirmDeleteConfigurationName"), session()); nextPage.setConfirmDelegate(new ERDDeletionDelegate(object(), dataSource(), context().page())); nextPage.setCancelDelegate(new ERDPageDelegate(context().page())); D2WPage d2wPage = ((D2WPage)nextPage); String message = ERXLocalizer.currentLocalizer().localizedTemplateStringForKeyWithObject("ERDTrashcan.confirmDeletionMessage", d2wContext()); nextPage.setMessage(message); d2wPage.setObject(object()); result = (WOActionResults)nextPage; } return result; } /** * Delete action for component button */ public WOActionResults deleteAction() { return deleteObjectWithFinalCommit(true); } /** * Performs the in-line delete and purges object pending deletion from the d2wContext to hide the * in-line confirmation dialog. Calls saveChanges on the parent ec if the finalCommit flag is true. */ public WOActionResults deleteObjectWithFinalCommit(boolean finalCommit) { EOEnterpriseObject obj = (EOEnterpriseObject)d2wContext().valueForKey(Keys.objectPendingDeletion); EODataSource ds = dataSource(); // check whether the relationship is marked "owns destination" boolean isOwnsDestination = false; if (ds != null && ds instanceof EODetailDataSource) { EODetailDataSource dds = (EODetailDataSource) ds; EOClassDescription masterClassDescription = dds.masterClassDescription(); isOwnsDestination = masterClassDescription .ownsDestinationObjectsForRelationshipKey(dds.detailKey()); } try { // with EODetailDatasource, calling deleteObject // will only remove the object from the relationship ds.deleteObject(object()); // for "owns destination" relationships, the following would // fail as the object will already be marked as deleted in the // parent EC if (!isOwnsDestination) { // actually delete the object in the nested EC obj.editingContext().deleteObject(obj); obj.editingContext().saveChanges(); } if (displayGroup() != null && displayGroup().displayedObjects().count() == 0) { displayGroup().displayPreviousBatch(); } if (finalCommit && !ERXEOControlUtilities.isNewObject(object())) { // if we are editing a new object, then don't save object().editingContext().saveChanges(); } d2wContext().takeValueForKey(null, Keys.objectPendingDeletion); postDeleteNotification(); } catch(NSValidation.ValidationException e) { parent().validationFailedWithException(e, e.object(), e.key()); } return null; } /** * Reverts the ec, and purges the objectPendingDeletion in the d2wContext to hide the in-line * confirmation dialog. */ public WOActionResults cancelAction() { EOEnterpriseObject obj = (EOEnterpriseObject)d2wContext().valueForKey(Keys.objectPendingDeletion); obj.editingContext().revert(); d2wContext().takeValueForKey(null, Keys.objectPendingDeletion); return null; } /** * Utility method to post the delete notification to the parent component */ public void postDeleteNotification() { Object obj = parentD2WPage(); String OBJECT_KEY = "object"; NSMutableDictionary<String, Object> userInfo = new NSMutableDictionary<String, Object>(obj, OBJECT_KEY); if (dataSource() instanceof EODetailDataSource) { EODetailDataSource dds = (EODetailDataSource)dataSource(); userInfo.setObjectForKey(dds.masterObject(), OBJECT_KEY); userInfo.setObjectForKey(dds.detailKey(), "propertyKey"); } NSNotificationCenter.defaultCenter().postNotification(BUTTON_PERFORMED_DELETE_ACTION, obj, userInfo); } // OTHERS /** * Boolean used to hide/show the in-line confirm delete dialog. */ public boolean canDelete() { return object() != null && object() instanceof ERXGuardedObjectInterface ? ((ERXGuardedObjectInterface)object()).canDelete() : true; } /** * Label for the Delete button. * <p> * Defaults to "Delete" */ public String buttonLabel() { if (_buttonLabel == null) { _buttonLabel = stringValueForBinding(Keys.deleteButtonLabel, "Delete"); } return _buttonLabel; } /** * Label for the Cancel button. * <p> * Defaults to "Cancel" */ public String cancelButtonLabel() { if (_cancelButtonLabel == null) { _cancelButtonLabel = stringValueForBinding(Keys.cancelButtonLabel, "Cancel"); } return _cancelButtonLabel; } /** * CSS class for the Delete button. */ public String buttonClass() { String result = null; if (canDelete() && !showDialog()) { result = activeButtonClass(); } else { result = disabledButtonClass(); } return result; } /** * CSS class for the Delete button when active. * <p> * Defaults to "Button ObjButton DeleteObjButton" */ public String activeButtonClass() { if (_buttonClass == null) { _buttonClass = stringValueForBinding(Keys.classForDeleteObjButton, "Button ObjButton DeleteObjButton"); } return _buttonClass; } /** * CSS class for the delete button when disabled. * <p> * Defaults to "Button ObjButton DisabledObjButton DisabledDeleteObjButton" */ public String disabledButtonClass() { if (_disabledButtonClass == null) { _disabledButtonClass = stringValueForBinding(Keys.classForDisabledDeleteObjButton, "Button ObjButton DisabledObjButton DisabledDeleteObjButton"); } return _disabledButtonClass; } /** * CSS class for the in-line dialog's Cancel button. * <p> * Defaults to "Button DialogButton CancelDialogButton" */ public String cancelButtonClass() { if (_cancelButtonClass == null) { _cancelButtonClass = stringValueForBinding(Keys.classForCancelDialogButton, "Button DialogButton CancelDialogButton"); } return _cancelButtonClass; } /** * CSS class for the in-line dialog's Delete button. * <p> * Defaults to "Button DialogButton DeleteDialogButton" */ public String deleteButtonClass() { if (_deleteButtonClass == null) { _deleteButtonClass = stringValueForBinding(Keys.classForDeleteDialogButton, "Button DialogButton DeleteDialogButton"); } return _deleteButtonClass; } /** * Used to show/hide the confirmation dialog */ public boolean showDialog() { boolean show = object() != null && ERXEOControlUtilities.eoEquals(object(), (EOEnterpriseObject)d2wContext().valueForKey(Keys.objectPendingDeletion)); return show; } /** * Determines whether to use an in-line confirmation dialog with ajax behaviour or a separate * confirmation page. * * Based on the value of the useAjax binding (or d2wContext key). */ @Override public Boolean useAjax() { if (_useAjax == null) { _useAjax = Boolean.valueOf(shouldUseAjax()); } return _useAjax; } /** * Returns a unique id for this control's update container */ public String localUpdateContainer() { if (_localUpdateContainer == null) { _localUpdateContainer = (String)valueForBinding("idForPropertyContainer"); _localUpdateContainer = _localUpdateContainer + "_" + object().hashCode(); } return _localUpdateContainer; } /** * String to display in the in-line confirmation dialog. * * Obtained from the bindings or d2wContext via this key: * * confirmDeleteMessage */ public String dialogMessage() { if (_dialogMessage == null) { _dialogMessage = (String)valueForBinding(Keys.confirmDeleteMessage); } return _dialogMessage; } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); EOEnterpriseObject eo = (EOEnterpriseObject) d2wContext().valueForKey(Keys.objectPendingDeletion); out.writeObject(eo); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); EOEnterpriseObject eo = (EOEnterpriseObject) in.readObject(); d2wContext().takeValueForKey(eo, Keys.objectPendingDeletion); } }