org.projectforge.web.wicket.AbstractEditPage.java Source code

Java tutorial

Introduction

Here is the source code for org.projectforge.web.wicket.AbstractEditPage.java

Source

/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
//         www.projectforge.org
//
// Copyright (C) 2001-2013 Kai Reinhard (k.reinhard@micromata.de)
//
// ProjectForge is dual-licensed.
//
// This community edition is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation; version 3 of the License.
//
// This community edition 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 General Public License along
// with this program; if not, see http://www.gnu.org/licenses/.
//
/////////////////////////////////////////////////////////////////////////////

package org.projectforge.web.wicket;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
import org.apache.wicket.extensions.markup.html.repeater.data.table.DataTable;
import org.apache.wicket.extensions.markup.html.repeater.data.table.HeadersToolbar;
import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.OddEvenItem;
import org.apache.wicket.markup.repeater.data.IDataProvider;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.projectforge.common.DateFormatType;
import org.projectforge.common.DateFormats;
import org.projectforge.core.AbstractBaseDO;
import org.projectforge.core.BaseDao;
import org.projectforge.core.DisplayHistoryEntry;
import org.projectforge.core.ExtendedBaseDO;
import org.projectforge.core.ModificationStatus;
import org.projectforge.user.UserGroupCache;
import org.projectforge.web.admin.WizardPage;
import org.projectforge.web.calendar.DateTimeFormatter;
import org.projectforge.web.task.TaskTreePage;
import org.projectforge.web.user.UserFormatter;
import org.projectforge.web.user.UserPropertyColumn;
import org.projectforge.web.wicket.flowlayout.DiffTextPanel;

public abstract class AbstractEditPage<O extends AbstractBaseDO<?>, F extends AbstractEditForm<O, ?>, D extends BaseDao<O>>
        extends AbstractSecuredPage implements IEditPage<O, D> {
    private static final long serialVersionUID = 8283877351980165438L;

    public static final String PARAMETER_KEY_ID = "id";

    public static final String PARAMETER_KEY_DATA_PRESET = "__data";

    protected F form;

    protected List<DisplayHistoryEntry> historyEntries;

    protected boolean showHistory = getBaseDao().isHistorizable();

    protected boolean showModificationTimes = true;

    protected String i18nPrefix;

    protected WebMarkupContainer topMenuPanel;

    protected WebMarkupContainer bottomPanel;

    @SpringBean(name = "userGroupCache")
    protected UserGroupCache userGroupCache;

    @SpringBean(name = "userFormatter")
    protected UserFormatter userFormatter;

    @SpringBean(name = "dateTimeFormatter")
    protected DateTimeFormatter dateTimeFormatter;

    protected EditPageSupport<O, D> editPageSupport;

    public AbstractEditPage(final PageParameters parameters, final String i18nPrefix) {
        super(parameters);
        this.i18nPrefix = i18nPrefix;
    }

    protected void init() {
        init(null);
    }

    @SuppressWarnings({ "serial", "unchecked" })
    protected void init(O data) {
        final StringBuffer buf = new StringBuffer();
        buf.append("function showDeleteQuestionDialog() {\n").append("  return window.confirm('");
        if (getBaseDao().isHistorizable() == true) {
            buf.append(getString("question.markAsDeletedQuestion"));
        } else {
            buf.append(getString("question.deleteQuestion"));
        }
        buf.append("');\n}\n");
        body.add(new Label("showDeleteQuestionDialog", buf.toString()).setEscapeModelStrings(false));
        final Integer id = WicketUtils.getAsInteger(getPageParameters(), PARAMETER_KEY_ID);
        if (data == null) {
            if (id != null) {
                data = getBaseDao().getById(id);
            }
            if (data == null) {
                data = (O) WicketUtils.getAsObject(getPageParameters(), PARAMETER_KEY_DATA_PRESET,
                        getBaseDao().newInstance().getClass());
                if (data == null) {
                    data = getBaseDao().newInstance();
                }
            }
        }
        form = newEditForm(this, data);

        body.add(form);
        form.init();
        if (form.isNew() == true) {
            showHistory = false;
            showModificationTimes = false;
        }
        body.add(new Label("tabTitle", getTitle()).setRenderBodyOnly(true));
        final List<IColumn<DisplayHistoryEntry, String>> columns = new ArrayList<IColumn<DisplayHistoryEntry, String>>();
        final CellItemListener<DisplayHistoryEntry> cellItemListener = new CellItemListener<DisplayHistoryEntry>() {
            public void populateItem(final Item<ICellPopulator<DisplayHistoryEntry>> item, final String componentId,
                    final IModel<DisplayHistoryEntry> rowModel) {
                // Later a link should show the history entry as popup.
                item.add(AttributeModifier.append("class", new Model<String>("notrlink")));
            }
        };
        final DatePropertyColumn<DisplayHistoryEntry> timestampColumn = new DatePropertyColumn<DisplayHistoryEntry>(
                dateTimeFormatter, getString("timestamp"), null, "timestamp", cellItemListener);
        timestampColumn.setDatePattern(DateFormats.getFormatString(DateFormatType.TIMESTAMP_SHORT_MINUTES));
        columns.add(timestampColumn);
        columns.add(new UserPropertyColumn<DisplayHistoryEntry>(getString("user"), null, "user", cellItemListener)
                .withUserFormatter(userFormatter));
        columns.add(new CellItemListenerPropertyColumn<DisplayHistoryEntry>(getString("history.entryType"), null,
                "entryType", cellItemListener));
        columns.add(new CellItemListenerPropertyColumn<DisplayHistoryEntry>(getString("history.propertyName"), null,
                "propertyName", cellItemListener));
        columns.add(new CellItemListenerPropertyColumn<DisplayHistoryEntry>(getString("history.newValue"), null,
                "newValue", cellItemListener) {
            @Override
            public void populateItem(final Item<ICellPopulator<DisplayHistoryEntry>> item, final String componentId,
                    final IModel<DisplayHistoryEntry> rowModel) {
                final DisplayHistoryEntry historyEntry = rowModel.getObject();
                item.add(new DiffTextPanel(componentId, Model.of(historyEntry.getNewValue()),
                        Model.of(historyEntry.getOldValue())));
                cellItemListener.populateItem(item, componentId, rowModel);
            }
        });
        final IDataProvider<DisplayHistoryEntry> dataProvider = new ListDataProvider<DisplayHistoryEntry>(
                getHistory());
        final DataTable<DisplayHistoryEntry, String> dataTable = new DataTable<DisplayHistoryEntry, String>(
                "historyTable", columns, dataProvider, 100) {
            @Override
            protected Item<DisplayHistoryEntry> newRowItem(final String id, final int index,
                    final IModel<DisplayHistoryEntry> model) {
                return new OddEvenItem<DisplayHistoryEntry>(id, index, model);
            }

            @Override
            public boolean isVisible() {
                return showHistory;
            }
        };
        final HeadersToolbar<String> headersToolbar = new HeadersToolbar<String>(dataTable, null);
        dataTable.addTopToolbar(headersToolbar);
        body.add(dataTable);
        final Label timeOfCreationLabel = new Label("timeOfCreation",
                dateTimeFormatter.getFormattedDateTime(data.getCreated()));
        timeOfCreationLabel.setRenderBodyOnly(true);
        body.add(timeOfCreationLabel);
        final Label timeOfLastUpdateLabel = new Label("timeOfLastUpdate",
                dateTimeFormatter.getFormattedDateTime(data.getLastUpdate()));
        timeOfLastUpdateLabel.setRenderBodyOnly(true);
        body.add(timeOfLastUpdateLabel);
        onPreEdit();
        evaluateInitialPageParameters(getPageParameters());
        this.editPageSupport = new EditPageSupport<O, D>(this, getBaseDao());
    }

    protected List<DisplayHistoryEntry> getHistory() {
        if (historyEntries == null) {
            historyEntries = getBaseDao().getDisplayHistoryEntries(getData());
        }
        return historyEntries;
    }

    /**
     * Override this method if some initial data or fields have to be set. onPreEdit will be called on both, on adding new data objects and on
     * updating existing data objects. The decision on adding or updating depends on getData().getId() != null.
     */
    protected void onPreEdit() {
    }

    /**
     * Will be called before the data object will be stored. Does nothing at default. Any return value is not yet supported.
     */
    public AbstractSecuredBasePage onSaveOrUpdate() {
        // Do nothing at default
        return null;
    }

    /**
     * Will be called before the data object will be deleted or marked as deleted. Here you can add validation errors manually. If this method
     * returns a resolution then a redirect to this resolution without calling the baseDao methods will done. <br/>
     * Here you can do validations with add(Global)Error or manipulate the data object before storing to the database etc.
     */
    public AbstractSecuredBasePage onDelete() {
        // Do nothing at default
        return null;
    }

    /**
     * Will be called before the data object will be restored (undeleted). Here you can add validation errors manually. If this method returns
     * a resolution then a redirect to this resolution without calling the baseDao methods will done. <br/>
     * Here you can do validations with add(Global)Error or manipulate the data object before storing to the database etc.
     */
    public AbstractSecuredBasePage onUndelete() {
        // Do nothing at default
        return null;
    }

    /**
     * Will be called directly after storing the data object (insert, update, delete). If any page is returned then proceed a redirect to this
     * given page.
     */
    public AbstractSecuredBasePage afterSaveOrUpdate() {
        // Do nothing at default.
        return null;
    }

    /**
     * Will be called directly after storing the data object (insert). Any return value is not yet supported.
     */
    public AbstractSecuredBasePage afterSave() {
        // Do nothing at default.
        return null;
    }

    /**
     * Will be called directly after storing the data object (update).
     * @param modificationStatus MINOR or MAJOR, if the object was modified, otherwise NONE. If a not null web page is returned, then the web
     *          page will be set as response page.
     * @see BaseDao#update(ExtendedBaseDO)
     */
    public AbstractSecuredBasePage afterUpdate(final ModificationStatus modificationStatus) {
        // Do nothing at default.
        return null;
    }

    /**
     * Will be called directly after deleting the data object (delete or update deleted=true). Any return value is not yet supported.
     */
    @Override
    public WebPage afterDelete() {
        // Do nothing at default.
        return null;
    }

    /**
     * Will be called directly after un-deleting the data object (update deleted=false). Any return value is not yet supported.
     */
    @Override
    public WebPage afterUndelete() {
        // Do nothing at default.
        return null;
    }

    /**
     * Will be called by clone button. Sets the id of the form data object to null and deleted to false.
     */
    protected void cloneData() {
        final O data = getData();
        getLogger().info("Clone of data chosen: " + data);
        data.setId(null);
        data.setDeleted(false);
    }

    /**
     * If user tried to add a new object and an error was occurred the edit page is shown again and the object id is cleared (set to null).
     */
    public void clearIds() {
        getData().setId(null);
    }

    @Override
    public void setResponsePageAndHighlightedRow(final WebPage page) {
        if (getData().getId() != null) {
            if (page instanceof AbstractListPage<?, ?, ?>) {
                // Force reload/refresh of calling AbstractListPage, otherwise the data object will not be updated.
                ((AbstractListPage<?, ?, ?>) page).setHighlightedRowId(getHighlightedRowId());
                ((AbstractListPage<?, ?, ?>) page).refresh();
            } else if (returnToPage instanceof TaskTreePage) {
                // Force reload/refresh of calling AbstractListPage, otherwise the data object will not be updated.
                ((TaskTreePage) page).setHighlightedRowId((Integer) getHighlightedRowId());
                ((TaskTreePage) page).refresh();
            } else if (returnToPage instanceof WizardPage) {
                ((WizardPage) returnToPage).setCreatedObject(getData());
            }
        }
        setResponsePage(page);
    }

    /**
     * Overwrite this, if getData().getId() should not be used.
     */
    protected Serializable getHighlightedRowId() {
        return getData().getId();
    }

    protected void cancel() {
        getLogger().debug("onCancel");
        setResponsePage();
    }

    /**
     * User has clicked the save button for storing a new item.
     */
    protected void create() {
        this.editPageSupport.create();
    }

    /**
     * User has clicked the update button for updating an existing item.
     */
    protected void update() {
        this.editPageSupport.update();
    }

    /**
     * User has clicked the update button for updating an existing item.
     */
    protected void updateAndNext() {
        this.editPageSupport.updateAndNext();
    }

    protected void undelete() {
        this.editPageSupport.undelete();
    }

    protected void markAsDeleted() {
        this.editPageSupport.markAsDeleted();
    }

    protected void delete() {
        this.editPageSupport.delete();
    }

    protected void reset() {
        getLogger().debug("onReset");
        // Later: Clearing all fields and restoring data base object.
        throw new UnsupportedOperationException("Reset button not supported.");
    }

    /**
     * Sets the list page (declared as annotation) as response or, if given, the returnToPage.
     */
    public void setResponsePage() {
        if (this.returnToPage != null) {
            setResponsePageAndHighlightedRow(this.returnToPage);
        } else {
            final EditPage ann = getClass().getAnnotation(EditPage.class);
            final Class<? extends WebPage> redirectPage;
            if (ann != null && ann.defaultReturnPage() != null) {
                redirectPage = getClass().getAnnotation(EditPage.class).defaultReturnPage();
            } else {
                redirectPage = WicketUtils.getDefaultPage();
            }
            final PageParameters params = new PageParameters();
            if (getData().getId() != null) {
                params.add(AbstractListPage.PARAMETER_HIGHLIGHTED_ROW, getData().getId());
            }
            setResponsePage(redirectPage, params);
        }
    }

    /**
     * @return false, if not overridden.
     */
    public boolean isUpdateAndNextSupported() {
        return false;
    }

    /**
     * Convenience method.
     * @see AbstractEditForm#getData()
     */
    @Override
    public O getData() {
        if (form == null || form.getData() == null) {
            getLogger().error(
                    "Data of form is null. Maybe you have forgotten to call AbstractEditPage.init() in constructor.");
        }
        return form.getData();
    }

    /**
     * Checks weather the id of the data object is given or not.
     * @return true if the user wants to create a new data object or false for an already existing object.
     */
    public boolean isNew() {
        if (form == null) {
            getLogger().error(
                    "Data of form is null. Maybe you have forgotten to call AbstractEditPage.init() in constructor.");
        }
        return (getData() == null || getData().getId() == null);
    }

    /**
     * Calls getString(key) with key "[i18nPrefix].title.edit" or "[i18nPrefix].title.add" dependent weather the data object is already
     * existing or new.
     * @see org.projectforge.web.wicket.AbstractUnsecurePage#getTitle()
     */
    @Override
    protected String getTitle() {
        return getString(getTitleKey(i18nPrefix, isNew()));
    }

    /**
     * @param i18nPrefix
     * @param isNew
     * @return i18nPrefix + ".title.add" if isNew is true or i18nPrefix + ".title.edit" otherwise.
     */
    public static String getTitleKey(final String i18nPrefix, final boolean isNew) {
        if (isNew == true) {
            return i18nPrefix + ".title.add";
        } else {
            return i18nPrefix + ".title.edit";
        }
    }

    /**
     * Removes id from the initial parameters set.
     * @see org.projectforge.web.wicket.AbstractSecuredPage#getBookmarkableInitialParameters()
     */
    @Override
    public PageParameters getBookmarkableInitialParameters() {
        if (isNew() == true) {
            return new PageParameters();
        }
        final PageParameters pageParameters = super.getBookmarkableInitialParameters();
        pageParameters.remove("id"); // Don't show id if other extended parameters are given.
        return pageParameters;
    }

    /**
     * @see org.projectforge.web.wicket.AbstractSecuredPage#getDataObjectForInitialParameters()
     */
    @Override
    protected Object getDataObjectForInitialParameters() {
        return getData();
    }

    /**
     * @see org.projectforge.web.wicket.AbstractSecuredPage#getTitleKey4BookmarkableInitialParameters()
     */
    @Override
    public String getTitleKey4BookmarkableInitialParameters() {
        return "bookmark.directPageExtendedLink.editPage";
    }

    @Override
    public boolean isAlreadySubmitted() {
        return alreadySubmitted;
    }

    @Override
    public void setAlreadySubmitted(final boolean alreadySubmitted) {
        this.alreadySubmitted = alreadySubmitted;
    }

    /**
     * @return the form
     */
    public F getForm() {
        return form;
    }

    protected abstract D getBaseDao();

    protected abstract Logger getLogger();

    protected abstract F newEditForm(AbstractEditPage<?, ?, ?> parentPage, O data);
}