/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.riotfamily.core.screen.form;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.riotfamily.common.util.FormatUtils;
import org.riotfamily.common.util.Generics;
import org.riotfamily.common.util.ResourceUtils;
import org.riotfamily.common.web.mvc.view.FlashScopeView;
import org.riotfamily.core.dao.InvalidPropertyValueException;
import org.riotfamily.core.dao.RiotDao;
import org.riotfamily.core.dao.RiotDaoException;
import org.riotfamily.core.screen.GroupScreen;
import org.riotfamily.core.screen.ItemScreen;
import org.riotfamily.core.screen.RiotScreen;
import org.riotfamily.core.screen.ScreenContext;
import org.riotfamily.core.screen.ScreenLink;
import org.riotfamily.core.screen.ScreenUtils;
import org.riotfamily.core.screen.Screenlet;
import org.riotfamily.forms.Form;
import org.riotfamily.forms.controller.AjaxFormController;
import org.riotfamily.forms.controller.FormContextFactory;
import org.riotfamily.forms.factory.FormRepository;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.support.RequestContextUtils;
public class FormScreen extends AjaxFormController
implements ItemScreen, BeanNameAware {
private static final DefaultTransactionDefinition TX_DEF =
new DefaultTransactionDefinition(
TransactionDefinition.PROPAGATION_REQUIRED);
private PlatformTransactionManager transactionManager;
private FormRepository formRepository;
private String viewName = ResourceUtils.getPath(
FormScreen.class, "form.ftl");
private String id;
private String[] formIds;
private String icon;
private RiotScreen parentScreen;
private Collection<RiotScreen> childScreens;
private Collection<Screenlet> screenlets;
public FormScreen(FormContextFactory formContextFactory,
FormRepository formRepository,
PlatformTransactionManager transactionManager) {
super(formContextFactory);
this.formRepository = formRepository;
this.transactionManager = transactionManager;
}
public void setViewName(String viewName) {
this.viewName = viewName;
}
public String getFormId(HttpServletRequest request, ScreenContext context) {
if (formIds == null) {
return getId();
}
else if (formIds.length == 1) {
return formIds[0];
}
else if (request.getParameter("formId") != null) {
String formId = request.getParameter("formId");
if (formId.contains(formId)) {
return formId;
}
}
else if (context.getObject() != null) {
for (String formId : formIds) {
Class<?> beanClass = formRepository.getBeanClass(formId);
if (beanClass.equals(context.getObject().getClass())) {
return formId;
}
}
}
return null;
}
public void setFormIds(String[] formIds) {
this.formIds = formIds;
}
public String[] getFormIds() {
return formIds;
}
public boolean contains(String formId) {
if (formIds == null) {
return formId.equals(getId());
}
return Arrays.asList(formIds).contains(formId);
}
public boolean isFormChooser() {
return formIds != null && formIds.length > 1;
}
public void setIcon(String icon) {
this.icon = icon;
}
public void setChildScreens(Collection<RiotScreen> childScreens) {
this.childScreens = childScreens;
if (childScreens != null) {
for (RiotScreen child : childScreens) {
child.setParentScreen(this);
}
}
}
public void setScreenlets(Collection<Screenlet> screenlets) {
this.screenlets = screenlets;
}
public void setBeanName(String beanName) {
if (id == null) {
id = beanName;
}
}
/**
* Returns the name of the attribute under which the {@link Form} is
* stored in the HTTP session. This implementation returns the
* requestURI with the controller's class name as prefix.
*/
@Override
protected String getSessionAttribute(HttpServletRequest request) {
return getClass().getName() + ":" + request.getRequestURI();
}
@Override
protected Form createForm(HttpServletRequest request) {
ScreenContext context = ScreenContext.Binding.get(request);
Form form = formRepository.createForm(getFormId(request, context));
form.addButton("save");
form.setAttribute("screenContext", context);
return form;
}
public static ScreenContext getScreenContext(Form form) {
return form.getAttribute("screenContext");
}
@Override
protected Object getFormBackingObject(HttpServletRequest request) {
return ScreenContext.Binding.get(request).getObject();
}
@Override
protected ModelAndView showForm(Form form, HttpServletRequest request,
HttpServletResponse response) {
StringWriter sw = new StringWriter();
renderForm(form, new PrintWriter(sw));
ModelAndView mv = new ModelAndView(viewName);
mv.addObject("form", sw.toString());
ScreenContext context = ScreenContext.Binding.get(request);
if (context.getObject() != null) {
if (childScreens != null) {
List<ScreenLink> childLinks = Generics.newArrayList();
for (RiotScreen screen : childScreens) {
childLinks.add(context.createChildContext(screen).getLink());
}
mv.addObject("childLinks", childLinks);
}
}
mv.addObject("listStateKey", ScreenUtils.getListScreen(this).getId());
return mv;
}
@Override
public final ModelAndView handleFormSubmission(Form form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
ScreenContext context = ScreenContext.Binding.get(request);
try {
return handleFormSubmissionInternal(form, request, response, context);
}
catch (InvalidPropertyValueException e) {
form.getErrors().rejectValue(e.getField(), e.getCode(),
e.getArguments(), e.getMessage());
return showForm(form, request, response);
}
catch (RiotDaoException e) {
form.getErrors().reject(e.getCode(), e.getArguments(), e.getMessage());
return showForm(form, request, response);
}
catch (Exception e) {
form.getErrors().reject("error.form.exception", new Object[] {e.getClass().getSimpleName(), e.getMessage()}, "An exception ({0}) occurred while saving. Exception message: {1}");
return showForm(form, request, response);
}
}
protected ModelAndView handleFormSubmissionInternal(Form form,
HttpServletRequest request, HttpServletResponse response,
ScreenContext context) throws Exception {
boolean save = form.isNew();
saveOrUpdate(form, context);
removeFormFromSession(request);
return afterSaveOrUpdate(form, request, context, save);
}
protected void saveOrUpdate(Form form, ScreenContext context)
throws Exception {
TransactionStatus status = transactionManager.getTransaction(TX_DEF);
RiotDao dao = context.getDao();
try {
if (form.isNew()) {
log.debug("Saving entity ...");
Object parent = context.getParent();
Object bean = form.populateBackingObject();
dao.save(bean, parent);
}
else {
log.debug("Updating entity ...");
Object bean = form.populateBackingObject();
dao.update(bean);
}
}
catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
transactionManager.commit(status);
}
protected ModelAndView afterSaveOrUpdate(
Form form, HttpServletRequest request,
ScreenContext context, boolean save) {
// Recreate context to make sure it includes the objectId (in case of newly created objects)
if (form.isNew()) {
context = context.createParentContext().createItemContext(form.getBackingObject());
}
String focus = request.getParameter("focus");
return reloadForm(form, context, focus)
.addObject("notification", new FormNotification(form)
.setIcon("save")
.setMessageKey("label.form.saved")
.setDefaultMessage("Your changes have been saved."));
}
protected ModelAndView showParentList(ScreenContext context) {
String listUrl = context.createParentContext().getLink().getUrl();
return new ModelAndView(new FlashScopeView(listUrl, true));
}
protected ModelAndView reloadForm(Form form, ScreenContext context, String focus) {
return new ModelAndView(new FlashScopeView(context.getLink().getUrl(), true))
.addObject("focus", focus);
}
// -----------------------------------------------------------------------
// Implementation of the RiotScreen interface
// -----------------------------------------------------------------------
public Collection<RiotScreen> getChildScreens() {
return childScreens;
}
public String getIcon() {
return icon;
}
public String getId() {
return id;
}
public RiotScreen getParentScreen() {
return parentScreen;
}
public void setParentScreen(RiotScreen parentScreen) {
if (this.parentScreen == null) {
this.parentScreen = parentScreen;
}
}
public String getTitle(ScreenContext context) {
Locale locale = RequestContextUtils.getLocale(context.getRequest());
if (parentScreen instanceof GroupScreen || parentScreen instanceof ItemScreen) {
String code = "screen." + getId();
String defaultTitle = FormatUtils.xmlToTitleCase(getId());
return getMessageSource().getMessage(code, null, defaultTitle, locale);
}
if (context.getObject() != null) {
return ScreenUtils.getLabel(context.getObject(), this);
}
return getMessageSource().getMessage("label.form.new", null, "New", locale);
}
public Collection<Screenlet> getScreenlets() {
return screenlets;
}
@Override
public String toString() {
return String.format("FormScreen[id=%s]", id);
}
}