/*
* Copyright (C) 2005-2012 BetaCONCEPT Limited
*
* This file is part of Astroboa.
*
* Astroboa is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Astroboa 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Astroboa. If not, see <http://www.gnu.org/licenses/>.
*/
package org.betaconceptframework.astroboa.portal.form;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.model.BooleanProperty;
import org.betaconceptframework.astroboa.api.model.CalendarProperty;
import org.betaconceptframework.astroboa.api.model.ContentObject;
import org.betaconceptframework.astroboa.api.model.RepositoryUser;
import org.betaconceptframework.astroboa.api.model.StringProperty;
import org.betaconceptframework.astroboa.api.model.Topic;
import org.betaconceptframework.astroboa.api.model.TopicReferenceProperty;
import org.betaconceptframework.astroboa.api.model.definition.CmsPropertyDefinition;
import org.betaconceptframework.astroboa.api.model.io.FetchLevel;
import org.betaconceptframework.astroboa.api.model.io.ResourceRepresentationType;
import org.betaconceptframework.astroboa.client.AstroboaClient;
import org.betaconceptframework.astroboa.portal.utility.CalendarUtils;
import org.betaconceptframework.astroboa.portal.utility.CmsUtils;
import org.betaconceptframework.astroboa.portal.utility.PortalStringConstants;
import org.betaconceptframework.ui.jsf.utility.JSFUtilities;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.faces.Renderer;
import org.jboss.seam.international.LocaleSelector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Gregory Chomatas (gchomatas@betaconcept.com)
* @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
*
*/
public abstract class AbstractForm<T extends AstroboaClient> {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
private Map<String, Object> formParams = new HashMap<String, Object>();
/*
* This map can store values which do not correspond to content object properties,
* like password confirmation input field. It is not used by form infrastructure.
*
* It may be used from implementor as a container for any key, value pair
*/
private Map<String, Object> tempParams = new HashMap<String, Object>();
@Out(required=false)
protected ContentObject formContentObject = null;
@In(value="#{captcha.challengeText}", required=false)
private String challengeText;
@In(value="#{captcha.response}", required=false)
private String challengeResponse;
@In(create=true)
protected Renderer renderer;
@In
protected LocaleSelector localeSelector;
protected List<String> formSubmissionMessageList = new ArrayList<String>();
private Boolean successfulFormSubmission;
@In(create=true)
protected CmsUtils cmsUtils;
@In(create=true)
protected CalendarUtils calendarUtils;
private final static String STATUS_OF_USER_SUBMITTED_FORM = "submittedByExternalUser";
private final static String WORKFLOW_FOR_USER_SUBMITTED_FORM = "webPublishing";
public String submitForm() {
formContentObject = null;
try {
formSubmissionMessageList = new ArrayList<String>();
checkChallengeResponsePhase();
formContentObject = getFormsRepositoryClient().getCmsRepositoryEntityFactory().newObjectForType(getFormType());
applyDefaultValuesPhase(formParams, formContentObject);
validateAndApplyFormValuesPhase(formParams, formContentObject);
// run custom form post processing code before saving
postValidateAndApplyValuesPhase(formParams, formContentObject);
savePhase(formContentObject);
postSavePhase(formParams, formContentObject);
//return "/successfulFormSubmission.xhtml";
successfulFormSubmission = true;
return null;
}
catch (ChallengeValidationException e) {
logger.info("An null or invalid form challenge response was provided");
return null;
}
catch (FormValidationException e) {
logger.warn("An error occured during validation", e);
if (CollectionUtils.isEmpty(formSubmissionMessageList))
{
formSubmissionMessageList.add(JSFUtilities.getLocalizedMessage("form.unknown.error", null));
}
return null;
}
catch (Exception e) {
logger.error("Failed to save user submitted form", e);
formSubmissionMessageList.add(JSFUtilities.getLocalizedMessage("form.unknown.error", null));
if (formContentObject != null) {
formContentObject.setId(null);
}
return null;
}
}
private void savePhase(ContentObject formContentObject) throws Exception {
getFormsRepositoryClient().getContentService().save(formContentObject, false, true, null);
formSubmissionMessageList.add(JSFUtilities.getLocalizedMessage("form.successful.submission", null));
formSubmissionMessageList.add(JSFUtilities.getLocalizedMessage("form.successful.submission.key.message", new String[]{formContentObject.getId()}));
formSubmissionMessageList.add(JSFUtilities.getLocalizedMessage("form.successful.submission.info.message", null));
}
private void checkChallengeResponsePhase() throws ChallengeValidationException{
if (enableCheckForChallengeRespone())
{
if (StringUtils.isBlank(challengeResponse)) {
//String message = "Δεν συμπληρώσατε τα γράμματα που εμφανίζονται στην εικόνα. Παρακαλώ συμπληρώστε τα γράμματα που βλέπετε στην εικόνα.";
formSubmissionMessageList.add(JSFUtilities.getLocalizedMessage("form.empty.challenge.response", null));
throw new ChallengeValidationException("Null Challenge Responce");
}
if (challengeText != null && challengeResponse != null && !challengeResponse.equals(challengeText)) {
//String message = "Δεν συμπληρώσατε σωστά τα γράμματα που εμφανίζονται στην εικόνα. Παρακαλώ συμπληρώστε τα γράμματα που βλέπετε στην εικόνα.";
formSubmissionMessageList.add(JSFUtilities.getLocalizedMessage("form.empty.challenge.response", null));
throw new ChallengeValidationException("challengeText=" + challengeText + " ,challenge responce=" + challengeResponse + " - No Match");
}
}
}
private void validateAndApplyFormValuesPhase(Map<String,Object> formParams, ContentObject formContentObject) throws Exception {
for (String formParameter : formParams.keySet()) {
applyFormParameterToContentObjectProperty(formContentObject, formParams, formParameter);
}
}
private void applyDefaultValuesPhase(Map<String,Object> formParams, ContentObject formContentObject) throws Exception {
StringProperty statusProperty = (StringProperty) formContentObject.getCmsProperty("profile.contentObjectStatus");
statusProperty.setSimpleTypeValue(STATUS_OF_USER_SUBMITTED_FORM);
StringProperty workflowProperty = (StringProperty) formContentObject.getCmsProperty("workflow.managedThroughWorkflow");
workflowProperty.setSimpleTypeValue(WORKFLOW_FOR_USER_SUBMITTED_FORM);
// StringProperty commentsProperty = (StringProperty) formContentObject.addChildCmsPropertyTemplate("internalComments");
// commentsProperty.setSimpleTypeValue("External Submitter Name: " + firstName + " " + lastName + "\n" +
// "email: " + email + "\n" +
// "telephone: " + telephone + "\n" +
// "Organization Name: " + organizationName);
//Form submitter is ALWAYS SYSTEM Repository User
RepositoryUser formSubmitter = getFormsRepositoryClient().getRepositoryUserService().getSystemRepositoryUser();
if (formSubmitter == null) {
logger.error("Retrieved a null User for form submitter. Cannot proceed to save submitted event");
formSubmissionMessageList.add(JSFUtilities.getLocalizedMessage("form.empty.challenge.response", null));
throw new Exception("Retrieved a null User for form submitter. Cannot proceed to save submitted event");
}
formContentObject.setOwner(formSubmitter);
Calendar now = GregorianCalendar.getInstance(JSFUtilities.getTimeZone(), JSFUtilities.getLocale());
CalendarProperty createdProperty = (CalendarProperty) formContentObject.getCmsProperty("profile.created");
createdProperty.setSimpleTypeValue(now);
CalendarProperty modifiedProperty = (CalendarProperty) formContentObject.getCmsProperty("profile.modified");
modifiedProperty.setSimpleTypeValue(now);
// supply a default title, language and author
// these may be overwritten by formPostProcessing method
StringProperty titleProperty = (StringProperty) formContentObject.getCmsProperty("profile.title");
titleProperty.setSimpleTypeValue(formContentObject.getTypeDefinition().getDisplayName().getLocalizedLabelForLocale(JSFUtilities.getLocaleAsString()) + " " + calendarUtils.convertDateToString(now.getTime(), PortalStringConstants.DATE_FORMAT_PATTERN));
StringProperty languageProperty = (StringProperty) formContentObject.getCmsProperty("profile.language");
languageProperty.addSimpleTypeValue(JSFUtilities.getLocaleAsString());
StringProperty creatorProperty = (StringProperty) formContentObject.getCmsProperty("profile.creator");
creatorProperty.addSimpleTypeValue("anonymous");
applyAccessibilityPropertiesPhase(formContentObject);
}
private void applyFormParameterToContentObjectProperty(ContentObject formContentObject, Map<String, Object> formParameters, String formParameter) throws Exception, FormValidationException {
CmsPropertyDefinition formParameterDefinition =
(CmsPropertyDefinition) getFormsRepositoryClient().getDefinitionService().getCmsDefinition(formContentObject.getContentObjectType()+"."+formParameter, ResourceRepresentationType.DEFINITION_INSTANCE,false);
// check if field is defined
if (formParameterDefinition == null) {
String errorMessage = "For the provided form parameter: " +
formParameter +
", there is no corresponding property in the ContentObject Type:" +
formContentObject.getContentObjectType() +
"which models the form";
throw new FormValidationException(errorMessage);
}
Object formParameterValue = formParameters.get(formParameter);
String requiredFieldErrorMessage = "Το πεδίο " + "'" + formParameterDefinition.getDisplayName().getLocalizedLabelForLocale(JSFUtilities.getLocaleAsString()) + "'" +
" είναι υποχρεωτικό. Παρακαλώ βάλτε τιμή";
switch (formParameterDefinition.getValueType()) {
case String:
{
// check if form value is of the appropriate type
if (!(formParameterValue instanceof String)) {
throw new FormValidationException("Invalid form field value type for property:" + formParameter + " A String value was expected");
}
// check if no value has been provided for a mandatory property
if (formParameterDefinition.isMandatory() && StringUtils.isBlank((String) formParameterValue)) {
formSubmissionMessageList.add(requiredFieldErrorMessage);
throw new FormValidationException(requiredFieldErrorMessage);
}
StringProperty formParameterProperty = (StringProperty) formContentObject.getCmsProperty(formParameter);
if (StringUtils.isBlank((String) formParameterValue)) {
formParameterProperty.setSimpleTypeValue(null);
}
else {
formParameterProperty.setSimpleTypeValue((String)formParameterValue);
}
break;
}
case Date:
{
// check if form value is of the appropriate type
if (!(formParameterValue instanceof Date)) {
throw new FormValidationException("Invalid form field value type for property:" + formParameter + " A Date value was expected");
}
// check if no value has been provided for a mandatory property
if (formParameterDefinition.isMandatory() && formParameterValue == null) {
formSubmissionMessageList.add(requiredFieldErrorMessage);
throw new FormValidationException(requiredFieldErrorMessage);
}
CalendarProperty formParameterProperty = (CalendarProperty) formContentObject.getCmsProperty(formParameter);
if (formParameterValue == null) {
formParameterProperty.setSimpleTypeValue(null);
}
else {
Calendar calendar = GregorianCalendar.getInstance(JSFUtilities.getTimeZone(), JSFUtilities.getLocale());
calendar.setTime(((Date)formParameters.get(formParameter)));
formParameterProperty.setSimpleTypeValue(calendar);
}
break;
}
case Boolean:
{
// check if form value is of the appropriate type
if (!(formParameterValue instanceof Boolean)) {
throw new FormValidationException("Invalid form field value type for property:" + formParameter + " A Boolean value was expected");
}
// check if no value has been provided for a mandatory property
if (formParameterDefinition.isMandatory() && formParameterValue == null) {
formSubmissionMessageList.add(requiredFieldErrorMessage);
throw new FormValidationException(requiredFieldErrorMessage);
}
BooleanProperty formParameterProperty = (BooleanProperty) formContentObject.getCmsProperty(formParameter);
if (formParameterValue == null) {
formParameterProperty.setSimpleTypeValue(null);
}
else {
formParameterProperty.setSimpleTypeValue((Boolean)formParameters.get(formParameter));
}
break;
}
case TopicReference:
{
// check if form value is of the appropriate type
// we expect a string which corresponds to the id of an existing topic
if (!(formParameterValue instanceof String)) {
throw new FormValidationException("Invalid form field value type for property:" + formParameter + " A string value corresponding to the id of an existing topic was expected");
}
// check if no value has been provided for a mandatory property
if (formParameterDefinition.isMandatory() && StringUtils.isBlank((String) formParameterValue)) {
formSubmissionMessageList.add(requiredFieldErrorMessage);
throw new FormValidationException(requiredFieldErrorMessage);
}
TopicReferenceProperty formParameterProperty = (TopicReferenceProperty) formContentObject.getCmsProperty(formParameter);
if (StringUtils.isBlank((String)formParameterValue)) {
formParameterProperty.setSimpleTypeValue(null);
}
else {
Topic retrievedTopic = getFormsRepositoryClient().getTopicService().getTopic((String) formParameterValue, ResourceRepresentationType.TOPIC_INSTANCE, FetchLevel.ENTITY, false);
if (retrievedTopic == null) {
throw new Exception("Not topic found for the provided topic id:" + formParameterValue);
}
formParameterProperty.setSimpleTypeValue(retrievedTopic);
}
break;
}
default:
throw new Exception("Not supported value type:" + formParameterDefinition.getValueType() + " for parameter:" + formParameter);
}
}
// run custom validation rules and do custom processing to formContentObject or formParams
public abstract void postValidateAndApplyValuesPhase(Map<String,Object> formParams, ContentObject formContentObject) throws Exception;
// run additional actions after successful form saving, e.g. send email, start a workflow, etc.
public abstract void postSavePhase(Map<String,Object> formParams, ContentObject formContentObject) throws Exception;
// called inside applyDefaultValuesPhase and permit custom accessibility setup
protected abstract void applyAccessibilityPropertiesPhase(ContentObject formContentObject);
//called inside checkChallengeResponsePhase and allow users to disable challenge response in case they do
//not display captcha in their form
protected abstract boolean enableCheckForChallengeRespone();
public Map<String, Object> getFormParams() {
return formParams;
}
public abstract String getFormType();
protected abstract T getFormsRepositoryClient();
public ContentObject getFormContentObject() {
return formContentObject;
}
public List<String> getFormSubmissionMessageList() {
return formSubmissionMessageList;
}
public Boolean getSuccessfulFormSubmission() {
return successfulFormSubmission;
}
public void setSuccessfulFormSubmission(Boolean successfulFormSubmission) {
this.successfulFormSubmission = successfulFormSubmission;
}
public Map<String, Object> getTempParams() {
return tempParams;
}
}