org.devgateway.eudevfin.dim.pages.transaction.crs.TransactionPage.java Source code

Java tutorial

Introduction

Here is the source code for org.devgateway.eudevfin.dim.pages.transaction.crs.TransactionPage.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Development Gateway.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 *******************************************************************************/

package org.devgateway.eudevfin.dim.pages.transaction.crs;

import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationMessage;
import de.agilecoders.wicket.core.markup.html.bootstrap.common.NotificationPanel;

import org.apache.log4j.Logger;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.time.Duration;
import org.apache.wicket.util.visit.IVisit;
import org.devgateway.eudevfin.auth.common.domain.AuthConstants;
import org.devgateway.eudevfin.auth.common.domain.PersistedUser;
import org.devgateway.eudevfin.auth.common.service.PersistedUserService;
import org.devgateway.eudevfin.auth.common.util.AuthUtils;
import org.devgateway.eudevfin.dim.pages.HomePage;
import org.devgateway.eudevfin.dim.pages.transaction.custom.CustomTransactionPage;
import org.devgateway.eudevfin.financial.CustomFinancialTransaction;
import org.devgateway.eudevfin.financial.FinancialTransaction;
import org.devgateway.eudevfin.financial.Message;
import org.devgateway.eudevfin.financial.service.CurrencyMetadataService;
import org.devgateway.eudevfin.financial.service.FinancialTransactionService;
import org.devgateway.eudevfin.financial.service.MessageService;
import org.devgateway.eudevfin.financial.util.FinancialTransactionUtil;
import org.devgateway.eudevfin.ui.common.AttributePrepender;
import org.devgateway.eudevfin.ui.common.Constants;
import org.devgateway.eudevfin.ui.common.components.BootstrapCancelButton;
import org.devgateway.eudevfin.ui.common.components.BootstrapDeleteButton;
import org.devgateway.eudevfin.ui.common.components.BootstrapSubmitButton;
import org.devgateway.eudevfin.ui.common.components.tabs.BootstrapJSTabbedPanel;
import org.devgateway.eudevfin.ui.common.components.tabs.DefaultTabWithKey;
import org.devgateway.eudevfin.ui.common.components.tabs.ITabWithKey;
import org.devgateway.eudevfin.ui.common.components.util.MondrianCDACacheUtil;
import org.devgateway.eudevfin.ui.common.pages.HeaderFooter;
import org.devgateway.eudevfin.ui.common.permissions.PermissionAwarePage;
import org.devgateway.eudevfin.ui.common.permissions.RoleActionMapping;
import org.joda.time.LocalDateTime;
import org.wicketstuff.annotation.mount.MountPath;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.MissingResourceException;

@MountPath(value = "/transaction")
@AuthorizeInstantiation(AuthConstants.Roles.ROLE_USER)
public class TransactionPage extends HeaderFooter<FinancialTransaction> implements PermissionAwarePage {

    private static final long serialVersionUID = -3616689887136295555L;

    private static final Logger logger = Logger.getLogger(TransactionPage.class);

    public static final String PARAM_TRANSACTION_ID = "transactionId";
    public static final String PARAM_REUSE = "reuse";

    public final String onUnloadScript;

    @SpringBean
    private FinancialTransactionService financialTransactionService;

    @SpringBean
    private CurrencyMetadataService currencyMetadaService;

    @SpringBean
    MondrianCDACacheUtil mondrianCacheUtil;

    @SpringBean
    private MessageService mxService;

    @SpringBean
    private PersistedUserService userService;

    private static final CRSTransactionPermissionProvider componentPermissions = new CRSTransactionPermissionProvider();

    protected final NotificationPanel feedbackPanel;

    protected Form form;
    protected Label note;
    protected Label subnote;
    protected TransactionPageSubmitButton submitButton;

    private Message prepareMessage(FinancialTransaction financialTransaction) {
        boolean tlEditing = AuthUtils.currentUserHasRole(AuthConstants.Roles.ROLE_TEAMLEAD);

        if (financialTransaction.getId() == null) {
            //new transaction
            if (!tlEditing) {
                //we will send message to the team leader
                Message msg = newSystemMessage(AuthUtils.currentUsersTeamLead(), financialTransaction);
                final String description = financialTransaction.getShortDescription();
                msg.setSubject("New Transaction"
                        + (description != null && !description.trim().equals("") ? ": " + description : ""));
                return msg;
            }
        } else {
            String creator = financialTransaction.getCreatedBy();
            PersistedUser currentUser = AuthUtils.getCurrentUser();
            //if editor is the same as creator
            boolean creatorEditing = creator.equals(currentUser.getUsername());

            if (tlEditing && creatorEditing)
                return null; //no point in creating a message if tl is editing his transaction

            //load current transaction state in the db
            FinancialTransaction oldFTState = financialTransactionService.findOne(financialTransaction.getId())
                    .getEntity();
            if (oldFTState == null) {
                logger.error(
                        "Can't find transaction with id " + financialTransaction.getId() + " in the database!");
                return null;
            }

            //compute needed variables
            boolean customFT = false;
            boolean transactionFinalized = false;
            boolean transactionApproved = false;
            if (financialTransaction instanceof CustomFinancialTransaction) {
                customFT = true;
                CustomFinancialTransaction customFinancialTransaction = (CustomFinancialTransaction) financialTransaction;
                //this shouldn't fail; means we have a transaction edited with CRS Form and with the Custom CRS Form
                CustomFinancialTransaction customOldFTState = (CustomFinancialTransaction) oldFTState;
                transactionFinalized = !customFinancialTransaction.getDraft() && customOldFTState.getDraft();
                transactionApproved = customFinancialTransaction.getApproved() && !customOldFTState.getApproved();
            }

            if (!tlEditing) {
                //Editing user is not team leader, but creator of the transaction
                //we will send message to the team leader
                PersistedUser toUser = AuthUtils.currentUsersTeamLead();
                Message msg = newSystemMessage(toUser, financialTransaction);

                if (customFT && transactionFinalized) {
                    //Generate Finalized Transaction Message
                    msg.setSubject("<span style=\"color: red;\">Transaction Finalized</span>: "
                            + financialTransaction.getShortDescription());
                    return msg;
                }
                //Generate Regular Transaction Edit message
                msg.setSubject("Transaction Edited: " + financialTransaction.getShortDescription());
                return msg;
            } else {
                //Current user is TL but not the creator of the transaction
                //we will send message to the creator of the transaction
                String toUser = financialTransaction.getCreatedBy();
                Message msg = newSystemMessage(toUser, financialTransaction);

                //we must check if the transaction is of type CustomFinancialTransaction
                //else we can't get the approved/final states
                if (msg != null && customFT && transactionApproved) {
                    //Generate Approved Message
                    msg.setSubject("<span style=\"color: green;\">Transaction Approved</span>: "
                            + financialTransaction.getShortDescription());
                    return msg;
                }
            }
        }
        return null;
    }

    private Message newSystemMessage(PersistedUser user, FinancialTransaction transaction) {
        Message ret = new Message();
        ret.setSendDate(LocalDateTime.now());
        ret.setFrom(AuthUtils.getCurrentUser());
        ArrayList<PersistedUser> to = new ArrayList<>();
        to.add(user);
        ret.setTo(to);

        ret.setMessage(buildTransactionLinkMsg(transaction));

        return ret;
    }

    private Message checkNewId(Message message, FinancialTransaction transaction) {
        if (message == null)
            return null;
        message.setMessage(buildTransactionLinkMsg(transaction));
        return message;
    }

    private String buildTransactionLinkMsg(FinancialTransaction transaction) {
        if (transaction.getId() == null)
            return null;
        String link = "?&transactionId=" + transaction.getId();
        if (transaction instanceof CustomFinancialTransaction)
            link = "." + CustomTransactionPage.class.getAnnotation(MountPath.class).value() + link
                    + "&transactionType=" + ((CustomFinancialTransaction) transaction).getFormType();
        else
            link = "." + TransactionPage.class.getAnnotation(MountPath.class).value() + link;

        return "Click <a href=\"" + link + "\">here</a> to see the transaction!";
    }

    private Message newSystemMessage(String username, FinancialTransaction transaction) {
        PersistedUser user = userService.findByUsername(username).getEntity();
        if (user == null) {
            //throw new AssertionError("User not found in the database!");
            return null; // we do nothing if the user is not found/deleted.
        }
        return newSystemMessage(user, transaction);
    }

    public class TransactionPageSubmitButton extends BootstrapSubmitButton {
        private static final long serialVersionUID = -8310280845870280505L;

        public TransactionPageSubmitButton(String id, IModel<String> model) {
            super(id, model);
        }

        Model<Boolean> shownFirstSection = null;

        @Override
        protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
            FinancialTransaction transaction = (FinancialTransaction) form.getInnermostModel().getObject();
            logger.debug("Object:" + transaction);
            logger.info("Trying to save!");
            try {
                FinancialTransaction saved = financialTransactionService.save(transaction).getEntity();
                TransactionPage.this.getModel().setObject(saved);

                // clear the mondrian cache
                mondrianCacheUtil.flushMondrianCDACache();

                info(new NotificationMessage(
                        new StringResourceModel("notification.saved", TransactionPage.this, null, null)));
                target.add(feedbackPanel);
            } catch (Exception e) {
                logger.error("Exception while trying to save:", e);
                error(new NotificationMessage(
                        new StringResourceModel("notification.error", TransactionPage.this, null, null))
                                .hideAfter(Duration.NONE));
                target.add(feedbackPanel);
                return;
            }
            logger.info("Saved ok!");
        }

        @Override
        protected void onError(AjaxRequestTarget target, Form<?> form) {
            shownFirstSection = Model.of(Boolean.FALSE);
            super.onError(target, form);
            error(new NotificationMessage(
                    new StringResourceModel("notification.validationerrors", TransactionPage.this, null, null)));
            target.add(feedbackPanel);
        }

        @Override
        public void componentVisitor(AjaxRequestTarget target, FormComponent component, IVisit<Void> visit) {
            // TODO Auto-generated method stub
            super.componentVisitor(target, component, visit);
            if ((!component.isValid()) && (!shownFirstSection.getObject())) {
                target.focusComponent(component);
                target.appendJavaScript("$('#" + component.getMarkupId()
                        + "').parents('[class~=\"tab-pane\"]').siblings().attr(\"class\", \"tab-pane\");");
                target.appendJavaScript("$('#" + component.getMarkupId()
                        + "').parents('[class~=\"tab-pane\"]').attr(\"class\", \"tab-pane active\");");

                target.appendJavaScript("$('#" + component.getMarkupId()
                        + "').parents('[class~=\"tabbable\"]').children('ul').find('li').attr('class', '');");
                target.appendJavaScript("var idOfSection = $('#" + component.getMarkupId()
                        + "').parents('[class~=\"tab-pane\"]').attr('id');$('#" + component.getMarkupId()
                        + "').parents('[class~=\"tabbable\"]').children('ul').find('a[href=\"#' + idOfSection + '\"]').parent().attr('class', 'active');");

                shownFirstSection.setObject(Boolean.TRUE);
            }
        }
    }

    public class TransactionPageDeleteButton extends BootstrapDeleteButton {
        private static final long serialVersionUID = 1076134119844959564L;

        public TransactionPageDeleteButton(String id, IModel<String> model) {
            super(id, model);
            // TODO Auto-generated constructor stub
        }

        @Override
        protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
            FinancialTransaction transaction = (FinancialTransaction) form.getInnermostModel().getObject();
            logger.debug("Object:" + transaction);
            logger.info("Deleting!");
            setResponsePage(HomePage.class);
            try {
                if (transaction.getId() == null)
                    return;
                financialTransactionService.delete(transaction);
                info(new NotificationMessage(
                        new StringResourceModel("notification.deleted", TransactionPage.this, null, null)));
                target.add(feedbackPanel);
                // clear the mondrian cache
                mondrianCacheUtil.flushMondrianCDACache();
            } catch (Exception e) {
                logger.error("Exception while trying to delete:", e);
                return;
            }
            logger.info("Deleted ok!");
        }

    }

    /**
     * Initialized a previously freshly constructed {@link FinancialTransaction}
     *
     * @param transaction
     * @param parameters  the {@link PageParameters}
     */
    public void initializeFinancialTransaction(FinancialTransaction transaction, PageParameters parameters) {
        PersistedUser user = AuthUtils.getCurrentUser();
        FinancialTransactionUtil.initializeFinancialTransaction(transaction, this.currencyMetadaService,
                AuthUtils.getOrganizationForCurrentUser(), AuthUtils.getIsoCountryForCurrentUser());
    }

    @SuppressWarnings("unchecked")
    public TransactionPage(final PageParameters parameters) {
        super(parameters);

        //override the title
        pageTitle.setDefaultModel(new StringResourceModel(
                parameters.get(Constants.PARAM_TRANSACTION_TYPE).toString(""), this, null, null));

        try {
            // check if the key is missing in the resource file
            getString(parameters.get(Constants.PARAM_TRANSACTION_TYPE).toString("") + ".note");
            note = new Label("note", new StringResourceModel(
                    parameters.get(Constants.PARAM_TRANSACTION_TYPE).toString("") + ".note", this, null, null));
        } catch (MissingResourceException mre) {
            note = new Label("note", new Model<String>(" "));
            note.add(new AttributeAppender("class", new Model<String>("hide"), " "));
        }
        add(note.setEscapeModelStrings(false));
        try {
            // check if the key is missing in the resource file
            getString(parameters.get(Constants.PARAM_TRANSACTION_TYPE).toString("") + ".subnote");
            subnote = new Label("subnote", new StringResourceModel(
                    parameters.get(Constants.PARAM_TRANSACTION_TYPE).toString("") + ".subnote", this, null, null));
        } catch (MissingResourceException mre) {
            subnote = new Label("subnote", new Model<String>(" "));
            subnote.add(new AttributeAppender("class", new Model<String>("hide"), " "));
        }
        add(subnote.setEscapeModelStrings(false));

        onUnloadScript = "window.onbeforeunload = function(e) {\n" + "   var message = '"
                + new StringResourceModel("leaveMessage", this, null, null).getObject() + "';\n"
                + "   e = e || window.event;\n" + "   if(e) {\n" + "       e.returnValue = message;\n" + // For IE 8 and old Firefox
                "   }\n" + "   return message;\n" + "};";

        // TODO: check that transactionType in the request parameters is the
        // same as the loaded transaction's type
        FinancialTransaction financialTransaction = null;

        if (!parameters.get(PARAM_TRANSACTION_ID).isNull()) {
            long transactionId = parameters.get(PARAM_TRANSACTION_ID).toLong();
            financialTransaction = financialTransactionService.findOne(transactionId).getEntity();
        } else {
            financialTransaction = getFinancialTransaction();
            initializeFinancialTransaction(financialTransaction, parameters);
        }

        CompoundPropertyModel<FinancialTransaction> model = new CompoundPropertyModel<FinancialTransaction>(
                financialTransaction);

        setModel(model);

        form = new Form("form");
        add(form);

        List<ITabWithKey> tabList = populateTabList(parameters);

        BootstrapJSTabbedPanel<ITabWithKey> bc = new BootstrapJSTabbedPanel<>("bc", tabList)
                .positionTabs(BootstrapJSTabbedPanel.Orientation.RIGHT);
        form.add(bc);

        submitButton = new TransactionPageSubmitButton("submit",
                new StringResourceModel("button.submit", this, null, null)) {
            private static final long serialVersionUID = -1909494416938537482L;

            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                logger.info("Submit pressed");
                FinancialTransaction transaction = (FinancialTransaction) form.getInnermostModel().getObject();
                Message message = prepareMessage(transaction);
                super.onSubmit(target, form);
                message = checkNewId(message, transaction);

                if (!form.hasError()) {
                    //send the message if any
                    if (message != null)
                        mxService.save(message);
                    setResponsePage(HomePage.class);
                }
            }

            @Override
            protected void onError(AjaxRequestTarget target, Form<?> form) {
                super.onError(target, form);
                target.appendJavaScript(onUnloadScript);
            }
        };

        submitButton
                .add(new AttributePrepender("onclick", new Model<String>("window.onbeforeunload = null;"), " "));
        form.add(submitButton);

        TransactionPageSubmitButton saveButton = new TransactionPageSubmitButton("save",
                new StringResourceModel("button.save", this, null, null)) {
            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                if (TransactionPage.this instanceof CustomTransactionPage) {
                    CustomTransactionPage ctp = (CustomTransactionPage) TransactionPage.this;
                    ctp.getApproved().getField().setModelObject(false);
                    ctp.getDraft().getField().setModelObject(true);
                    target.add(ctp.getDraft().getField());
                    target.add(ctp.getApproved().getField());
                }

                super.onSubmit(target, form);
            }
        };
        saveButton.setDefaultFormProcessing(false);
        form.add(saveButton);

        TransactionPageDeleteButton transactionPageDeleteButton = new TransactionPageDeleteButton("delete",
                new StringResourceModel("button.delete", this, null, null));
        MetaDataRoleAuthorizationStrategy.authorize(transactionPageDeleteButton, Component.ENABLE,
                AuthConstants.Roles.ROLE_TEAMLEAD);
        form.add(transactionPageDeleteButton);

        form.add(new BootstrapCancelButton("cancel", new StringResourceModel("button.cancel", this, null, null)) {
            private static final long serialVersionUID = -3097577464142022353L;

            @Override
            protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
                logger.info("Cancel pressed");
                setResponsePage(HomePage.class);
            }

        });

        feedbackPanel = new NotificationPanel("feedback");
        feedbackPanel.setOutputMarkupId(true);
        feedbackPanel.hideAfter(Duration.seconds(5));
        add(feedbackPanel);
    }

    protected FinancialTransaction getFinancialTransaction() {
        return new FinancialTransaction();
    }

    private List<ITabWithKey> populateTabList(PageParameters parameters) {
        List<Class<? extends Panel>> tabClasses = getTabs();
        ArrayList<ITabWithKey> tabs = new ArrayList<>();
        for (final Class<? extends Panel> p : tabClasses) {
            tabs.add(DefaultTabWithKey.of(p, this, parameters));
        }
        return tabs;
    }

    protected List<Class<? extends Panel>> getTabs() {
        List<Class<? extends Panel>> tabList = new ArrayList<>();
        tabList.add(IdentificationDataTab.class);
        tabList.add(BasicDataTab.class);
        tabList.add(SupplementaryDataTab.class);
        tabList.add(VolumeDataTab.class);
        tabList.add(ForLoansOnlyTab.class);
        return tabList;
    }

    @Override
    protected void onAfterRenderChildren() {
        super.onAfterRenderChildren();

    }

    @Override
    public HashMap<String, RoleActionMapping> getPermissions() {
        return componentPermissions.permissions();
    }

    @Override
    public void renderHead(IHeaderResponse response) {
        super.renderHead(response);
        response.render(OnDomReadyHeaderItem.forScript(onUnloadScript));
    }
}