/////////////////////////////////////////////////////////////////////////////
//
// Project ProjectForge Community Edition
// www.projectforge.org
//
// Copyright (C) 2001-2014 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 org.apache.log4j.Logger;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
import org.projectforge.core.AbstractBaseDO;
import org.projectforge.core.BaseDao;
import org.projectforge.core.UserException;
import org.projectforge.web.wicket.bootstrap.GridBuilder;
import org.projectforge.web.wicket.components.SingleButtonPanel;
import org.projectforge.web.wicket.flowlayout.MyComponentsRepeater;
public abstract class AbstractEditForm<O extends AbstractBaseDO< ? >, P extends AbstractEditPage< ? , ? , ? >> extends
AbstractSecuredForm<O, P>
{
private static final long serialVersionUID = -6707610179583359099L;
protected O data;
/**
* List to create content menu in the desired order before creating the RepeatingView.
*/
protected MyComponentsRepeater<SingleButtonPanel> actionButtons;
protected Button cancelButton;
protected Button createButton;
protected SingleButtonPanel createButtonPanel;
protected Button updateButton;
protected Button updateAndNextButton;
protected SingleButtonPanel updateButtonPanel;
protected SingleButtonPanel updateAndNextButtonPanel;
protected SingleButtonPanel deleteButtonPanel;
protected SingleButtonPanel markAsDeletedButtonPanel;
protected Button undeleteButton;
protected SingleButtonPanel undeleteButtonPanel;
protected FeedbackPanel feedbackPanel;
protected GridBuilder gridBuilder;
private O origData;
/**
* If set and supported by the edit page, the user is able to accept or discard changes.
*/
protected O oldData;
public AbstractEditForm(final P parentPage, final O data)
{
super(parentPage);
this.data = data;
}
/**
* @param oldData the oldData to set
* @return this for chaining.
*/
public AbstractEditForm<O, P> setOldData(final O oldData)
{
this.oldData = oldData;
return this;
}
@SuppressWarnings("serial")
@Override
protected void init()
{
super.init();
feedbackPanel = createFeedbackPanel();
add(feedbackPanel);
gridBuilder = newGridBuilder(this, "flowform");
actionButtons = new MyComponentsRepeater<SingleButtonPanel>("buttons");
add(actionButtons.getRepeatingView());
{
cancelButton = new Button("button", new Model<String>("cancel")) {
@Override
public final void onSubmit()
{
try {
parentPage.cancel();
} catch (final UserException ex) {
AbstractEditForm.this.error(parentPage.translateParams(ex));
}
}
};
cancelButton.setDefaultFormProcessing(false); // No validation of the
final SingleButtonPanel cancelButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), cancelButton, getString("cancel"),
SingleButtonPanel.CANCEL);
actionButtons.add(cancelButtonPanel);
}
{
final Button markAsDeletedButton = new Button("button", new Model<String>("markAsDeleted")) {
@Override
public final void onSubmit()
{
try {
parentPage.markAsDeleted();
} catch (final UserException ex) {
AbstractEditForm.this.error(parentPage.translateParams(ex));
}
}
};
markAsDeletedButton.add(AttributeModifier.replace("onclick", "return showDeleteQuestionDialog();"));
markAsDeletedButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), markAsDeletedButton, getString("markAsDeleted"),
SingleButtonPanel.DELETE);
actionButtons.add(markAsDeletedButtonPanel);
}
{
final Button deleteButton = new Button("button", new Model<String>("delete")) {
@Override
public final void onSubmit()
{
try {
parentPage.delete();
} catch (final UserException ex) {
AbstractEditForm.this.error(parentPage.translateParams(ex));
}
}
};
deleteButton.add(AttributeModifier.replace("onclick", "return showDeleteQuestionDialog();"));
deleteButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), deleteButton, getString("delete"), SingleButtonPanel.DELETE);
deleteButton.setDefaultFormProcessing(false);
actionButtons.add(deleteButtonPanel);
}
{
final Button resetButton = new Button("button", new Model<String>("reset")) {
@Override
public final void onSubmit()
{
try {
parentPage.reset();
} catch (final UserException ex) {
AbstractEditForm.this.error(parentPage.translateParams(ex));
}
}
};
final SingleButtonPanel resetButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), resetButton, getString("reset"),
SingleButtonPanel.RESET);
resetButtonPanel.setVisible(false);
actionButtons.add(resetButtonPanel);
}
{
updateButton = new Button("button", new Model<String>("update")) {
@Override
public final void onSubmit()
{
try {
parentPage.update();
} catch (final UserException ex) {
AbstractEditForm.this.error(parentPage.translateParams(ex));
}
}
};
updateButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), updateButton, getString("update"));
actionButtons.add(updateButtonPanel);
}
{
updateAndNextButton = new Button("button", new Model<String>("updateAndNext")) {
@Override
public final void onSubmit()
{
try {
parentPage.updateAndNext();
} catch (final UserException ex) {
AbstractEditForm.this.error(parentPage.translateParams(ex));
}
}
};
updateAndNextButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), updateAndNextButton, getString("updateAndNext"));
actionButtons.add(updateAndNextButtonPanel);
}
{
createButton = new Button("button", new Model<String>("create")) {
@Override
public final void onSubmit()
{
try {
parentPage.create();
} catch (final UserException ex) {
AbstractEditForm.this.error(parentPage.translateParams(ex));
}
}
};
createButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), createButton, getString("create"));
actionButtons.add(createButtonPanel);
}
{
undeleteButton = new Button("button", new Model<String>("undelete")) {
@Override
public final void onSubmit()
{
try {
parentPage.undelete();
} catch (final UserException ex) {
AbstractEditForm.this.error(parentPage.translateParams(ex));
}
}
};
undeleteButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), undeleteButton, getString("undelete"));
actionButtons.add(undeleteButtonPanel);
}
markDefaultButtons();
updateButtonVisibility();
}
@Override
public void onBeforeRender()
{
actionButtons.render();
updateButtonVisibility();
super.onBeforeRender();
}
@SuppressWarnings("unchecked")
protected BaseDao<O> getBaseDao()
{
return (BaseDao<O>) parentPage.getBaseDao();
}
/**
* Sets the visibility of buttons update, create and markAsDeleted in dependency of the isNew() function. Currently used by
* TimesheetEdit's clone function for redraw the buttons correctly after clone.
*/
protected void updateButtonVisibility()
{
try {
final BaseDao<O> baseDao = getBaseDao();
if (isNew() == true) {
updateButtonPanel.setVisible(false);
updateAndNextButtonPanel.setVisible(false);
undeleteButtonPanel.setVisible(false);
markAsDeletedButtonPanel.setVisible(false);
deleteButtonPanel.setVisible(false);
createButtonPanel.setVisible(baseDao.hasLoggedInUserInsertAccess());
if (createButtonPanel.isVisible() == true) {
setDefaultButton(createButton);
} else {
setDefaultButton(cancelButton);
}
} else {
if (origData == null) {
origData = getBaseDao().getById(getData().getId());
}
createButtonPanel.setVisible(false);
if (getData().isDeleted() == true) {
undeleteButtonPanel.setVisible(baseDao.hasLoggedInUserUpdateAccess(origData, origData, false));
if (undeleteButtonPanel.isVisible() == true) {
setDefaultButton(undeleteButton);
}
markAsDeletedButtonPanel.setVisible(false);
deleteButtonPanel.setVisible(false);
updateButtonPanel.setVisible(false);
updateAndNextButtonPanel.setVisible(false);
} else {
undeleteButtonPanel.setVisible(false);
if (parentPage.getBaseDao().isHistorizable() == true) {
deleteButtonPanel.setVisible(false);
markAsDeletedButtonPanel.setVisible(baseDao.hasLoggedInUserDeleteAccess(origData, origData, false));
} else {
deleteButtonPanel.setVisible(baseDao.hasLoggedInUserDeleteAccess(origData, origData, false));
markAsDeletedButtonPanel.setVisible(false);
}
updateButtonPanel.setVisible(baseDao.hasLoggedInUserUpdateAccess(origData, origData, false));
if (parentPage.isUpdateAndNextSupported() == true) {
updateAndNextButtonPanel.setVisible(updateButtonPanel.isVisible());
} else {
updateAndNextButton.setVisible(false);
}
if (updateButtonPanel.isVisible() == true) {
setDefaultButton(updateButton);
} else {
setDefaultButton(cancelButton);
}
}
}
} catch (final RuntimeException ex) {
// It's possible that an exception is thrown by the dao (e. g. Exception in TaskDao if a cyclic reference was detected).
if (ex instanceof UserException) {
// If an UserException was thrown then try to show the message as validation error:
final String i18nKey = ((UserException) ex).getI18nKey();
if (i18nKey != null) {
addError(i18nKey);
}
} else {
throw ex;
}
}
}
/**
* Adds a clone button (only if the data isn't new) and calls {@link AbstractEditPage#cloneData()}.
*/
@SuppressWarnings("serial")
protected void addCloneButton() {
if (isNew() == false) {
// Clone button for existing and not deleted invoices:
final Button cloneButton = new Button(SingleButtonPanel.WICKET_ID, new Model<String>("clone")) {
@Override
public final void onSubmit()
{
parentPage.cloneData();
}
};
final SingleButtonPanel cloneButtonPanel = new SingleButtonPanel(actionButtons.newChildId(), cloneButton, getString("clone")) {
/**
* @see org.apache.wicket.Component#isVisible()
*/
@Override
public boolean isVisible()
{
return isNew() == false;
}
};
actionButtons.add(2, cloneButtonPanel);
}
}
/**
* Set the style class for the default buttons. Overwrite this, if you have a different default button than create, update or undelete
* (don't call super!).
*/
protected void markDefaultButtons()
{
createButtonPanel.setClassnames(SingleButtonPanel.DEFAULT_SUBMIT);
updateButtonPanel.setClassnames(SingleButtonPanel.DEFAULT_SUBMIT);
updateAndNextButtonPanel.setClassnames(SingleButtonPanel.DEFAULT_SUBMIT);
undeleteButtonPanel.setClassnames(SingleButtonPanel.DEFAULT_SUBMIT);
}
/**
* @return true, if id of data is null (id not yet exists).
*/
public boolean isNew()
{
return data.getId() == null;
}
public O getData()
{
return this.data;
}
/** This class uses the logger of the extended class. */
protected abstract Logger getLogger();
public FeedbackPanel getFeedbackPanel()
{
return feedbackPanel;
}
}