/*
* File : $Source: /usr/local/cvs/alkacon/com.alkacon.opencms.formgenerator/src/com/alkacon/opencms/formgenerator/CmsFormHandler.java,v $
* Date : $Date: 2011-05-17 12:36:06 $
* Version: $Revision: 1.25 $
*
* This file is part of the Alkacon OpenCms Add-On Module Package
*
* Copyright (c) 2010 Alkacon Software GmbH (http://www.alkacon.com)
*
* The Alkacon OpenCms Add-On Module Package 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, either version 3 of the License, or
* (at your option) any later version.
*
* The Alkacon OpenCms Add-On Module Package 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 the Alkacon OpenCms Add-On Module Package.
* If not, see http://www.gnu.org/licenses/.
*
* For further information about Alkacon Software GmbH, please see the
* company website: http://www.alkacon.com.
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org.
*/
package com.alkacon.opencms.formgenerator;
import com.alkacon.opencms.formgenerator.database.CmsFormDataAccess;
import com.alkacon.opencms.formgenerator.database.CmsFormDatabaseFilter;
import org.opencms.cache.CmsVfsMemoryObjectCache;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsProperty;
import org.opencms.file.CmsResource;
import org.opencms.i18n.CmsEncoder;
import org.opencms.i18n.CmsMessages;
import org.opencms.i18n.CmsMultiMessages;
import org.opencms.jsp.CmsJspActionElement;
import org.opencms.mail.CmsHtmlMail;
import org.opencms.mail.CmsSimpleMail;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.module.CmsModule;
import org.opencms.util.CmsByteArrayDataSource;
import org.opencms.util.CmsDateUtil;
import org.opencms.util.CmsMacroResolver;
import org.opencms.util.CmsRequestUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.workplace.CmsWorkplace;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.Writer;
import java.sql.SQLException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.logging.Log;
import org.antlr.stringtemplate.StringTemplate;
import org.antlr.stringtemplate.StringTemplateErrorListener;
import org.antlr.stringtemplate.StringTemplateGroup;
import org.antlr.stringtemplate.language.DefaultTemplateLexer;
/**
* The form handler controls the HTML or mail output of a configured email form.<p>
*
* Provides methods to determine the action that takes place and methods to create different
* output formats of a submitted form.<p>
*
* @author Andreas Zahner
* @author Thomas Weckert
* @author Jan Baudisch
*
* @version $Revision: 1.25 $
*
* @since 7.0.4
*/
public class CmsFormHandler extends CmsJspActionElement {
/** Request parameter value for the form action parameter: correct the input. */
public static final String ACTION_CONFIRMED = "confirmed";
/** Request parameter value for the form action parameter: correct the input. */
public static final String ACTION_CORRECT_INPUT = "correct";
/** Request parameter value for the form action parameter: Jump to the download page of the CSV data (allow only for offline project!!!). */
public static final String ACTION_DOWNLOAD_DATA_1 = "export1";
/** Request parameter value for the form action parameter: Download the CSV data. */
public static final String ACTION_DOWNLOAD_DATA_2 = "export2";
/** Request parameter value for the form action parameter: form submitted. */
public static final String ACTION_SUBMIT = "submit";
/** Name of the file item session attribute. */
public static final String ATTRIBUTE_FILEITEMS = "fileitems";
/** Form error: mandatory field not filled out. */
public static final String ERROR_MANDATORY = "mandatory";
/** Form error: unique error of input. */
public static final String ERROR_UNIQUE = "unique";
/** Form error: validation error of input. */
public static final String ERROR_VALIDATION = "validation";
/** Form info: mandatory upload field filled out already. */
public static final String INFO_UPLOAD_FIELD_MANDATORY_FILLED_OUT = "uploadfield_mandatory_filled_out";
/** Macro name for the date macro that can be used in mail text fields. */
public static final String MACRO_DATE = "date";
/** Macro name for the form data macro that can be used in mail text fields. */
public static final String MACRO_FORMDATA = "formdata";
/** Macro name for the locale macro that can be used in mail text fields. */
public static final String MACRO_LOCALE = "locale";
/** Macro name for the url macro that can be used in mail text fields. */
public static final String MACRO_URL = "url";
/** Request parameter name for the back parameter. */
public static final String PARAM_BACK = "back";
/** Request parameter name for the hidden form action parameter to determine the action. */
public static final String PARAM_FORMACTION = "formaction";
/** Request parameter name for the page parameter. */
public static final String PARAM_PAGE = "page";
/** Request parameter uri for the page parameter. */
public static final String PARAM_URI = "uri";
/** The log object for this class. */
private static final Log LOG = CmsLog.getLog(CmsFormHandler.class);
/** The entry id of the submitted form in the database. */
protected int m_entryId = 0;
/** Contains eventual validation errors. */
protected Map<String, String> m_errors;
/** The form configuration object. */
protected CmsForm m_formConfiguration;
/** Temporarily stores the fields as hidden fields in the String. */
protected String m_hiddenFields;
/** Contains eventual validation infos. */
protected Map<String, String> m_infos;
/** Flag indicating if the form is displayed for the first time. */
protected boolean m_initial;
/** Flag indicating if the form handler configuration has been initialized successfully. */
protected boolean m_initSuccess;
/** Boolean indicating if the form is validated correctly. */
protected Boolean m_isValidatedCorrect;
/** Needed to implant form fields into the mail text. */
protected transient CmsMacroResolver m_macroResolver;
/** The localized messages for the form handler. */
protected CmsMultiMessages m_messages;
/** The multipart file items. */
protected List<FileItem> m_multipartFileItems;
/** The templates for the web form output. */
protected StringTemplateGroup m_outputTemplates;
/** The map of request parameters. */
protected Map<String, String[]> m_parameterMap;
/**
* Empty constructor, be sure to call one of the available initialization methods afterwards.<p>
*
* Possible initialization methods are:<p>
* <ul>
* <li>{@link #init(PageContext, HttpServletRequest, HttpServletResponse)}</li>
* <li>{@link #init(PageContext, HttpServletRequest, HttpServletResponse, String)}</li>
* </ul>
*/
public CmsFormHandler() {
super();
m_errors = new HashMap<String, String>();
m_infos = new HashMap<String, String>();
}
/**
* Gets the truncated file item name. Some clients, such as the Opera browser, do include path information.
* This method only returns the the base file name.<p>
*
* @param name the file item name
*
* @return the truncated file item name
*/
public static String getTruncatedFileItemName(String name) {
// here is not used the File.separator, because there are problems is the
// OpenCms OS and the explores OS are different
// for example if you have an OpenCms on Unix and you use the
// formgenerator in a browser in Windows
if (name.contains("/")) {
int lastIndex = name.lastIndexOf("/");
if (name.length() > lastIndex) {
name = name.substring(lastIndex + 1);
return name;
}
} else if (name.contains("\\")) {
int lastIndex = name.lastIndexOf("\\");
if (name.length() > lastIndex) {
name = name.substring(lastIndex + 1);
return name;
}
}
return name;
}
/**
* Adds on the first position the given messages.<p>
*
* @param messages the localized messages
*/
public void addMessages(CmsMessages messages) {
CmsMultiMessages tmpOld = m_messages;
m_messages = new CmsMultiMessages(messages.getLocale());
m_messages.addMessages(messages);
if (tmpOld != null) {
m_messages.addMessages(tmpOld.getMessages());
}
tmpOld = null;
}
/**
* Replaces line breaks with html <br>.<p>
*
* @param value the value to substitute
* @return the substituted value
*/
public String convertToHtmlValue(String value) {
return convertValue(value, "html");
}
/**
* Replaces html <br> with line breaks.<p>
*
* @param value the value to substitute
* @return the substituted value
*/
public String convertToPlainValue(String value) {
return convertValue(value, "");
}
/**
* Converts a given String value to the desired output format.<p>
*
* The following output formats are possible:
* <ul>
* <li>"HTML" meaning that <br> tags are added</li>
* <li>"plain" or any other String value meaning that <br> tags are removed</li>
* </ul>
*
* @param value the String value to convert
* @param outputType the type of the resulting output
* @return the converted String in the desired output format
*/
public String convertValue(String value, String outputType) {
if ("html".equalsIgnoreCase(outputType)) {
// output should be HTML, add line break tags and characters
value = CmsStringUtil.escapeHtml(value);
} else {
// output should be plain, remove HTML line break tags and characters
value = CmsStringUtil.substitute(value, "<br>", "\n");
}
return value;
}
/**
* Creates the main web form output for usage on a JSP.<p>
*
* @throws IOException if an I/O error occurs
* @throws JspException if redirection fails
*/
public void createForm() throws IOException, JspException {
// the output writer
Writer out = getJspContext().getOut();
// the module path containing the resources
String modulePath = CmsWorkplace.VFS_PATH_MODULES + CmsForm.MODULE_NAME + "/";
if (showTemplate()) {
// we are not on the download page, include CSS and JavaScript
out.write(getStyleSheet());
// determine if the user session should be kept
String sessionJs = null;
if (isInitSuccess() && getFormConfiguration().isRefreshSession()) {
sessionJs = link(modulePath + "resources/js/keepsession.js");
}
// get the JS output template
StringTemplate sTemplate = getOutputTemplate("form_js");
sTemplate.setAttribute("formconfig", getFormConfiguration());
sTemplate.setAttribute("sessionjs", sessionJs);
sTemplate.setAttribute("sessionuri", link(modulePath + "elements/keepsession.jsp"));
sTemplate.setAttribute("subfieldjs", link(modulePath + "resources/js/subfields.js"));
out.write(sTemplate.toString());
// check the template group syntax and show eventual errors
out.write(buildTemplateGroupCheckHtml());
}
if (!isInitSuccess()) {
// form was not configured correctly, show error message
StringTemplate sTemplate = getOutputTemplate("initerror");
sTemplate.setAttribute("headline", getMessages().key("form.init.error.headline"));
sTemplate.setAttribute("text", getMessages().key("form.init.error.description"));
out.write(sTemplate.toString());
} else if (showRelease()) {
// form is still to be released, show release text
out.write(getFormConfiguration().getReleaseText());
} else if (showExpired()) {
// form is expired, show expiration text
out.write(getFormConfiguration().getExpirationText());
} else if (showMaximumSubmissions()) {
// form cannot take any more submissions
if (getFormConfiguration().getMaximumSubmissionsText() != null) {
out.write(getFormConfiguration().getMaximumSubmissionsText());
}
} else if (!showForm() && isValidFormaction()) {
// form has been submitted with correct values, decide further actions
if (showCheck()) {
// show optional check page
out.write(buildCheckHtml());
} else if (showDownloadData()) {
// show the data download page
// include(modulePath + "elements/datadownload.jsp");
Map modParameterMap = getParameterMap();
modParameterMap.put(PARAM_URI, new String[] {getFormConfiguration().getConfigUri().toString()});
include(modulePath + "elements/datadownload.jsp", null, false, modParameterMap);
} else {
// try to send a notification email with the submitted form field values
if (sendData()) {
// successfully sent email, decide further actions
if (getFormConfiguration().hasTargetUri()) {
// another target URI is configured, redirect to it
getResponse().sendRedirect(link(getFormConfiguration().getTargetUri()));
} else {
// show confirmation end page
out.write(buildConfirmHtml());
}
// prepare the web form action class if configured
prepareAfterWebformAction();
} else {
// failure sending email, show error message
StringTemplate sTemplate = getOutputTemplate("emailerror");
sTemplate.setAttribute("headline", getMessages().key("form.error.mail.headline"));
sTemplate.setAttribute("text", getMessages().key("form.error.mail.text"));
sTemplate.setAttribute("error", getErrors().get("sendmail"));
out.write(sTemplate.toString());
}
}
} else {
// create the main input form
out.write(buildFormHtml());
}
}
/**
* Returns the configured form field values as hidden input fields.<p>
*
* @return the configured form field values as hidden input fields
*/
public String createHiddenFields() {
if (CmsStringUtil.isEmpty(m_hiddenFields)) {
List<I_CmsField> fields = getFormConfiguration().getAllFields(true, false, false);
StringBuffer result = new StringBuffer(fields.size() * 8);
// iterate the form fields
for (int i = 0, n = fields.size(); i < n; i++) {
I_CmsField currentField = fields.get(i);
if (currentField == null) {
continue;
} else if (CmsCheckboxField.class.isAssignableFrom(currentField.getClass())) {
// special case: checkbox, can have more than one value
Iterator<CmsFieldItem> k = currentField.getItems().iterator();
while (k.hasNext()) {
CmsFieldItem item = k.next();
if (item.isSelected()) {
result.append("<input type=\"hidden\" name=\"");
result.append(currentField.getName());
result.append("\" value=\"");
result.append(CmsEncoder.escapeXml(item.getValue()));
result.append("\" />\n");
}
}
} else if (CmsTableField.class.isAssignableFrom(currentField.getClass())) {
// special case: table, can have more than one value
Iterator<CmsFieldItem> k = currentField.getItems().iterator();
while (k.hasNext()) {
CmsFieldItem item = k.next();
result.append("<input type=\"hidden\" name=\"");
result.append(currentField.getName() + item.getDbLabel());
result.append("\" value=\"");
result.append(CmsEncoder.escapeXml(item.getValue()));
result.append("\" />\n");
}
} else if (CmsParameterField.class.equals(currentField.getClass())) {
// special case: Parameter field must store Field value and request parameters
result.append("<input type=\"hidden\" name=\"");
result.append(currentField.getName());
result.append("\" value=\"");
result.append(CmsEncoder.escapeXml(currentField.getValue()));
result.append("\" />\n");
result.append(CmsParameterField.createHiddenFields(getParameterMap(), currentField.getParameters()));
} else if (CmsStringUtil.isNotEmpty(currentField.getValue())) {
// all other fields are converted to a simple hidden field
result.append("<input type=\"hidden\" name=\"");
result.append(currentField.getName());
result.append("\" value=\"");
result.append(CmsEncoder.escapeXml(currentField.getValue()));
result.append("\" />\n");
}
}
// store the generated input fields for further usage to avoid unnecessary rebuilding
m_hiddenFields = result.toString();
}
// return generated result list
return m_hiddenFields;
}
/**
* Creates the output String of the submitted fields for email creation.<p>
*
* @param isHtmlMail if true, the output is formatted as HTML, otherwise as plain text
* @param isConfirmationMail if true, the text for the confirmation mail is created, otherwise the text for mail receiver
*
* @return the output String of the submitted fields for email creation
*/
public String createMailTextFromFields(boolean isHtmlMail, boolean isConfirmationMail) {
List<I_CmsField> fieldValues = getFormConfiguration().getAllFields(true, false, true);
StringBuffer fieldsResult = new StringBuffer(fieldValues.size() * 16);
List<I_CmsField> htmlFields = new ArrayList<I_CmsField>(fieldValues.size());
String mailCss = null;
// determine CSS to use for HTML email
if (isHtmlMail) {
// create HTML email using the template output
if (CmsStringUtil.isNotEmpty(getFormConfiguration().getMailCSS())) {
// use individually configured CSS
mailCss = getFormConfiguration().getMailCSS();
}
}
// generate output for submitted form fields
Iterator<I_CmsField> i = fieldValues.iterator();
while (i.hasNext()) {
I_CmsField current = i.next();
if (current instanceof CmsPagingField) {
continue;
}
if (!useInFormDataMacro(current)) {
continue;
}
if ((current instanceof CmsEmptyField) && !current.isMandatory()) {
continue;
}
String value = current.toString();
if (((current instanceof CmsDynamicField) && !((current instanceof CmsDisplayField) || (current instanceof CmsHiddenDisplayField)))) {
if (!current.isMandatory()) {
// show dynamic fields only if they are marked as mandatory
continue;
}
// compute the value for the dynamic field
value = getFormConfiguration().getFieldStringValueByName(current.getName());
} else if (current instanceof CmsHiddenDisplayField) {
// do not show hidden display fields and empty fields
continue;
} else if (current instanceof CmsFileUploadField) {
value = current.getValue();
if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
// try to read upload item from session attribute
FileItem fileItem = getUploadFile(current);
if (fileItem != null) {
value = fileItem.getName();
}
}
value = CmsFormHandler.getTruncatedFileItemName(value);
}
if (isHtmlMail) {
// format field label as HTML
String label = current.getLabel();
// special case for table and empty fields
if (current instanceof CmsTableField) {
label = ((CmsTableField)current).buildLabel(this);
} else if (current instanceof CmsEmptyField) {
label = "";
}
// format field value as HTML
String fieldValue = convertToHtmlValue(value);
// special case for table and empty fields
if (current instanceof CmsTableField) {
fieldValue = ((CmsTableField)current).buildRows(this);
} else if (current instanceof CmsEmptyField) {
fieldValue = value;
}
// if label and value is not set, skip it
if (CmsStringUtil.isEmpty(label) && CmsStringUtil.isEmpty(fieldValue)) {
continue;
}
I_CmsField mailField = new CmsTextField();
mailField.setLabel(label);
mailField.setValue(fieldValue);
htmlFields.add(mailField);
} else {
// format output as plain text
String label;
try {
label = CmsHtmlToTextConverter.htmlToText(
current.getLabel(),
getCmsObject().getRequestContext().getEncoding(),
true).trim();
} catch (Exception e) {
// error parsing the String, provide it as is
label = current.getLabel();
}
// if label and value is not set, skip it
if (CmsStringUtil.isEmpty(label) && CmsStringUtil.isEmpty(value)) {
continue;
}
fieldsResult.append(label);
// special case by table fields
if (current instanceof CmsTableField) {
fieldsResult.append(((CmsTableField)current).buildText(isConfirmationMail));
continue;
}
fieldsResult.append("\t");
fieldsResult.append(value);
fieldsResult.append("\n");
}
}
// generate the main mail text
String mailText;
if (isHtmlMail) {
// generate HTML email
if (isConfirmationMail) {
// append the confirmation email text
mailText = getFormConfiguration().getConfirmationMailText();
} else {
// append the email text
mailText = getFormConfiguration().getMailText();
}
// create field output
StringTemplate sTemplate = getOutputTemplate("htmlemailfields");
sTemplate.setAttribute("mailcss", mailCss);
sTemplate.setAttribute("fields", htmlFields);
fieldsResult.append(sTemplate.toString());
} else {
// generate simple text email
if (isConfirmationMail) {
// append the confirmation email text
mailText = getFormConfiguration().getConfirmationMailTextPlain();
} else {
// append the email text
mailText = getFormConfiguration().getMailTextPlain();
}
}
// resolve the common macros
mailText = m_macroResolver.resolveMacros(mailText);
// check presence of formdata macro in mail text using new macro resolver (important, do not use the same here!)
CmsMacroResolver macroResolver = CmsMacroResolver.newInstance();
macroResolver.setKeepEmptyMacros(true);
macroResolver.addMacro(MACRO_FORMDATA, "");
if (mailText.length() > macroResolver.resolveMacros(mailText).length()) {
// form data macro found, resolve it
macroResolver.addMacro(MACRO_FORMDATA, fieldsResult.toString());
mailText = macroResolver.resolveMacros(mailText);
} else {
// no form data macro found, add the fields below the mail text
if (!isHtmlMail) {
mailText += "\n\n";
}
mailText += fieldsResult;
}
if (isHtmlMail) {
StringTemplate sTemplate = getOutputTemplate("htmlemail");
String errorHeadline = null;
if (!isConfirmationMail && getFormConfiguration().hasConfigurationErrors()) {
// write form configuration errors to html mail
errorHeadline = getMessages().key("form.configuration.error.headline");
}
// set necessary attributes
sTemplate.setAttribute("mailcss", mailCss);
sTemplate.setAttribute("mailtext", mailText);
sTemplate.setAttribute("errorheadline", errorHeadline);
sTemplate.setAttribute("errors", getFormConfiguration().getConfigurationErrors());
return sTemplate.toString();
} else {
StringBuffer result = new StringBuffer(mailText);
if (!isConfirmationMail && getFormConfiguration().hasConfigurationErrors()) {
// write form configuration errors to text mail
result.append("\n");
result.append(getMessages().key("form.configuration.error.headline"));
result.append("\n");
for (int k = 0; k < getFormConfiguration().getConfigurationErrors().size(); k++) {
result.append(getFormConfiguration().getConfigurationErrors().get(k));
result.append("\n");
}
}
return result.toString();
}
}
/**
* Returns if the data download should be initiated.<p>
*
* @return true if the data download should be initiated, otherwise false
*/
public boolean downloadData() {
boolean result = false;
String paramAction = getParameter(CmsFormHandler.PARAM_FORMACTION);
if (CmsFormHandler.ACTION_DOWNLOAD_DATA_2.equals(paramAction)) {
result = true;
}
return result;
}
/**
* Returns the entry id of the submitted form in the database.<p>
*
* Returns a value > 0, if the form was submitted to database successfully,
* 0, if the form is not submitted to database yet,
*
* @return the entry id of the form
*/
public int getEntryId() {
return m_entryId;
}
/**
* Returns the errors found when validating the form.<p>
*
* @return the errors found when validating the form
*/
public Map<String, String> getErrors() {
return m_errors;
}
/**
* Returns the check page text, after resolving macros.<p>
*
* @return the check page text
*/
public String getFormCheckText() {
CmsMacroResolver macroResolver = CmsMacroResolver.newInstance();
macroResolver.setKeepEmptyMacros(true);
List<I_CmsField> fields = getFormConfiguration().getFields();
I_CmsField field;
Iterator<I_CmsField> itFields = fields.iterator();
// add field values as macros
while (itFields.hasNext()) {
field = itFields.next();
macroResolver.addMacro(field.getLabel(), field.getValue());
if (!field.getLabel().equals(field.getDbLabel())) {
macroResolver.addMacro(field.getDbLabel(), field.getValue());
}
if (field instanceof CmsTableField) {
Iterator<CmsFieldItem> it = field.getItems().iterator();
while (it.hasNext()) {
CmsFieldItem item = it.next();
macroResolver.addMacro(item.getLabel(), item.getValue());
if (!item.getLabel().equals(item.getDbLabel())) {
macroResolver.addMacro(item.getDbLabel(), item.getValue());
}
}
}
}
return macroResolver.resolveMacros(getFormConfiguration().getFormCheckText());
}
/**
* Returns the form configuration.<p>
*
* @return the form configuration
*/
public CmsForm getFormConfiguration() {
return m_formConfiguration;
}
/**
* Returns the confirmation text, after resolving macros.<p>
*
* @return the confirmation text
*/
public String getFormConfirmationText() {
return m_macroResolver.resolveMacros(getFormConfiguration().getFormConfirmationText());
}
/**
* Returns the infos found when validating the form.<p>
*
* @return the infos found when validating the form
*/
public Map<String, String> getInfos() {
return m_infos;
}
/**
* Returns the localized messages.<p>
*
* @return the localized messages
*/
public CmsMessages getMessages() {
return m_messages;
}
/**
* Returns the template for the web form HTML output.<p>
*
* @param templateName the name of the template to return
*
* @return the template for the web form HTML output
*/
public StringTemplate getOutputTemplate(String templateName) {
StringTemplate result = getOutputTemplateGroup().getInstanceOf(templateName);
if (!getRequestContext().getCurrentProject().isOnlineProject() && (result == null)) {
// no template with the specified name found, return initialized error template
try {
CmsFile stFile = getCmsObject().readFile(CmsForm.VFS_PATH_ERROR_TEMPLATEFILE);
String stContent = new String(stFile.getContents(), getCmsObject().getRequestContext().getEncoding());
result = new StringTemplate(stContent, DefaultTemplateLexer.class);
// set the error attributes of the template
result.setAttribute("errorheadline", "Error getting output template");
result.setAttribute("errortext", "The desired template \"" + templateName + "\" was not found.");
result.setAttribute("errortemplatenames", getOutputTemplateGroup().getTemplateNames());
} catch (Exception e) {
// something went wrong, log error
LOG.error("Error while getting error output template from file \""
+ CmsForm.VFS_PATH_ERROR_TEMPLATEFILE
+ "\".");
}
}
return result;
}
/**
* Returns the output template group that generates the web form HTML output.<p>
*
* @return the output template group that generates the web form HTML output
*/
public StringTemplateGroup getOutputTemplateGroup() {
if (m_outputTemplates == null) {
String vfsPath = null;
if (getFormConfiguration() != null) {
// check if an individual output template path is set
vfsPath = getFormConfiguration().getTemplateFile();
if (!getCmsObject().existsResource(vfsPath)) {
// configured output template path does not exist, log error and use default output templates
LOG.error("Configured web form HTML output template file \"" + vfsPath + "\" does not exist.");
vfsPath = null;
}
}
if (vfsPath == null) {
// no valid template path configured, use default output templates
vfsPath = OpenCms.getModuleManager().getModule(CmsForm.MODULE_NAME).getParameter(
CmsForm.MODULE_PARAM_TEMPLATE_FILE,
CmsForm.VFS_PATH_DEFAULT_TEMPLATEFILE);
}
try {
// first try to get the initialized template group from VFS cache
String rootPath = getRequestContext().addSiteRoot(vfsPath);
StringTemplateGroup stGroup = (StringTemplateGroup)CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().getCachedObject(
getCmsObject(),
rootPath);
if (stGroup == null) {
// template group is not in cache, read the file and generate template group
CmsFile stFile = getCmsObject().readFile(vfsPath);
String stContent = new String(
stFile.getContents(),
getCmsObject().getRequestContext().getEncoding());
StringTemplateErrorListener errors = new CmsStringTemplateErrorListener();
stGroup = new StringTemplateGroup(new StringReader(stContent), DefaultTemplateLexer.class, errors);
// store the template group in cache
CmsVfsMemoryObjectCache.getVfsMemoryObjectCache().putCachedObject(getCmsObject(), rootPath, stGroup);
}
m_outputTemplates = stGroup;
} catch (Exception e) {
// something went wrong, log error
LOG.error("Error while creating web form HTML output templates from file \"" + vfsPath + "\".");
}
}
return m_outputTemplates;
}
/**
* Returns the map of request parameters.<p>
*
* @return the map of request parameters
*/
public Map<String, String[]> getParameterMap() {
return m_parameterMap;
}
/**
* Returns the HTML to include the CSS style sheet for the form.<p>
*
* The CSS to use can be specified individually for each form and as module parameter {@link CmsForm#MODULE_PARAM_CSS}.
* If no value is defined, the default CSS file shipped with the module is used.<p>
*
* <b>Important</b>: to generate valid XHTML code, specify <code>false</code> as module parameter value and include the CSS
* in your template head manually.<p>
*
* @return the HTML to include the CSS style sheet for the form
*/
public String getStyleSheet() {
String cssParam = OpenCms.getModuleManager().getModule(CmsForm.MODULE_NAME).getParameter(
CmsForm.MODULE_PARAM_CSS);
if (CmsStringUtil.FALSE.equalsIgnoreCase(cssParam)) {
// parameter set to false, this overrides all other individual CSS settings
return "";
}
StringBuffer result = new StringBuffer(64);
// get the optional individual CSS file for the form
String configuredCss = null;
if (getFormConfiguration() != null) {
configuredCss = getFormConfiguration().getCssFile();
}
if (CmsStringUtil.isEmptyOrWhitespaceOnly(configuredCss)) {
// no individual CSS configured, use CSS configured as module parameter
configuredCss = cssParam;
if (CmsStringUtil.isEmptyOrWhitespaceOnly(configuredCss)) {
// no module parameter set, fall back to default CSS
configuredCss = CmsWorkplace.VFS_PATH_MODULES + CmsForm.MODULE_NAME + "/resources/css/webform.css";
}
}
result.append("<link rel=\"stylesheet\" type=\"text/css\" href=\"").append(link(configuredCss)).append("\" />");
return result.toString();
}
/**
* Return <code>null</code> or the file item for the given field in
* case it is of type <code>{@link CmsFileUploadField}</code>.<p>
*
* @param isFileUploadField the field to the the file item of
*
* @return <code>null</code> or the file item for the given field in
* case it is of type <code>{@link CmsFileUploadField}</code>
*/
public FileItem getUploadFile(I_CmsField isFileUploadField) {
FileItem result = null;
FileItem current;
String fieldName = isFileUploadField.getName();
String uploadFileFieldName;
Map<String, FileItem> fileUploads = (Map<String, FileItem>)getRequest().getSession().getAttribute(
ATTRIBUTE_FILEITEMS);
if (fileUploads != null) {
Iterator<FileItem> i = fileUploads.values().iterator();
while (i.hasNext()) {
current = i.next();
uploadFileFieldName = current.getFieldName();
if (fieldName.equals(uploadFileFieldName)) {
result = current;
}
}
}
return result;
}
/**
* Returns if the submitted values contain validation errors.<p>
*
* @return <code>true</code> if the submitted values contain validation errors, otherwise <code>false</code>
*/
public boolean hasValidationErrors() {
return (!isInitial() && (getErrors().size() > 0));
}
/**
* Initializes the form handler and creates the necessary configuration objects.<p>
*
* @see org.opencms.jsp.CmsJspBean#init(javax.servlet.jsp.PageContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public void init(PageContext context, HttpServletRequest req, HttpServletResponse res) {
try {
init(context, req, res, null);
} catch (CmsException e) {
// log initialization error
LOG.error(e);
}
}
/**
* Initializes the form handler and creates the necessary configuration objects.<p>
*
* @param context the JSP page context object
* @param req the JSP request
* @param res the JSP response
* @param formConfigUri URI of the form configuration file, if not provided, current URI is used for configuration
*
* @throws CmsException if initializing the form message objects fails
*/
public void init(PageContext context, HttpServletRequest req, HttpServletResponse res, String formConfigUri)
throws CmsException {
super.init(context, req, res);
try {
// initialize the form configuration
configureForm(req, formConfigUri);
m_initSuccess = true;
} catch (Exception e) {
// error in form initialization, initialize at least the localized messages
initMessages(formConfigUri);
}
}
/**
* Returns if the form is displayed for the first time.<p>
*
* @return <code>true</code> if the form is displayed for the first time, otherwise <code>false</code>
*/
public boolean isInitial() {
return m_initial;
}
/**
* Returns if the form handler configuration has been initialized successfully.<p>
*
* @return <code>true</code> if the form handler configuration has been initialized successfully, otherwise <code>false</code>
*/
public boolean isInitSuccess() {
return m_initSuccess;
}
/**
* Returns if the formaction is of predefined value.<p>
*
* @return <code>true</code> if the formaction is valid, otherwise <code>false</code>
*/
public boolean isValidFormaction() {
String formAction = getParameter(PARAM_FORMACTION);
boolean result = false;
if (ACTION_CONFIRMED.equalsIgnoreCase(formAction)) {
result = true;
} else if (ACTION_CORRECT_INPUT.equalsIgnoreCase(formAction)) {
result = true;
} else if (ACTION_DOWNLOAD_DATA_1.equalsIgnoreCase(formAction)) {
result = true;
} else if (ACTION_DOWNLOAD_DATA_2.equalsIgnoreCase(formAction)) {
result = true;
} else if (ACTION_SUBMIT.equalsIgnoreCase(formAction)) {
result = true;
}
return result;
}
/**
* Prepares the after web form action.<p>
*/
public void prepareAfterWebformAction() {
String actionClass = getFormConfiguration().getActionClass();
if (CmsStringUtil.isNotEmpty(actionClass)) {
try {
I_CmsWebformActionHandler handler = getObject(actionClass);
handler.afterWebformAction(getCmsObject(), this);
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Invalid webform action handler class: " + actionClass, e);
}
}
}
}
/**
* Sends the confirmation mail with the form data to the specified email address.<p>
*
* @throws Exception if sending the confirmation mail fails
*/
public void sendConfirmationMail() throws Exception {
String mailTo = getFormConfiguration().getConfirmationMailEmail();
if (CmsStringUtil.isNotEmpty(mailTo)) {
// create the new confirmation mail message depending on the configured email type
if (getFormConfiguration().getMailType().equals(CmsForm.MAILTYPE_HTML)) {
// create a HTML email
CmsHtmlMail theMail = new CmsHtmlMail();
theMail.setCharset(getCmsObject().getRequestContext().getEncoding());
if (CmsStringUtil.isNotEmpty(getFormConfiguration().getConfirmationMailFrom())) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getFormConfiguration().getConfirmationMailFromName())) {
theMail.setFrom(
m_macroResolver.resolveMacros(getFormConfiguration().getConfirmationMailFrom()),
m_macroResolver.resolveMacros(getFormConfiguration().getConfirmationMailFromName()));
} else {
theMail.setFrom(m_macroResolver.resolveMacros(getFormConfiguration().getConfirmationMailFrom()));
}
} else if (CmsStringUtil.isNotEmpty(getFormConfiguration().getMailFrom())) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getFormConfiguration().getMailFromName())) {
theMail.setFrom(
m_macroResolver.resolveMacros(getFormConfiguration().getMailFrom()),
m_macroResolver.resolveMacros(getFormConfiguration().getMailFromName()));
} else {
theMail.setFrom(m_macroResolver.resolveMacros(getFormConfiguration().getMailFrom()));
}
}
theMail.setTo(createInternetAddresses(mailTo));
theMail.setSubject(m_macroResolver.resolveMacros(getFormConfiguration().getMailSubjectPrefix()
+ getFormConfiguration().getConfirmationMailSubject()));
theMail.setHtmlMsg(createMailTextFromFields(true, true));
// send the mail
theMail.send();
} else {
// create a plain text email
CmsSimpleMail theMail = new CmsSimpleMail();
theMail.setCharset(getCmsObject().getRequestContext().getEncoding());
if (CmsStringUtil.isNotEmpty(getFormConfiguration().getMailFrom())) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getFormConfiguration().getMailFromName())) {
theMail.setFrom(
m_macroResolver.resolveMacros(getFormConfiguration().getMailFrom()),
m_macroResolver.resolveMacros(getFormConfiguration().getMailFromName()));
} else {
theMail.setFrom(m_macroResolver.resolveMacros(getFormConfiguration().getMailFrom()));
}
}
theMail.setTo(createInternetAddresses(mailTo));
theMail.setSubject(m_macroResolver.resolveMacros(getFormConfiguration().getMailSubjectPrefix()
+ getFormConfiguration().getConfirmationMailSubject()));
theMail.setMsg(createMailTextFromFields(false, true));
// send the mail
theMail.send();
}
}
}
/**
* Sends the collected data due to the configuration of the form
* (email, database or both).<p>
*
* @return <code>true</code> if successful
*/
public boolean sendData() {
boolean result = true;
try {
CmsForm data = getFormConfiguration();
data.removeCaptchaField();
// fill the macro resolver for resolving in subject and content:
List<I_CmsField> fields = data.getAllFields(false, true, true);
Iterator<I_CmsField> itFields = fields.iterator();
// add field values as macros
while (itFields.hasNext()) {
I_CmsField field = itFields.next();
if (field instanceof CmsPagingField) {
continue;
}
String fValue = field.getValue();
if ((field instanceof CmsDynamicField)
&& !((field instanceof CmsDisplayField) || (field instanceof CmsHiddenDisplayField))) {
fValue = data.getFieldStringValueByName(field.getName());
}
if (field instanceof CmsFileUploadField) {
if (CmsStringUtil.isEmptyOrWhitespaceOnly(fValue)) {
// try to read upload item from session attribute
FileItem fileItem = getUploadFile(field);
if (fileItem != null) {
fValue = fileItem.getName();
}
}
fValue = CmsFormHandler.getTruncatedFileItemName(fValue);
}
m_macroResolver.addMacro(field.getLabel(), fValue);
if (!field.getLabel().equals(field.getDbLabel())) {
m_macroResolver.addMacro(field.getDbLabel(), fValue);
}
if (field instanceof CmsTableField) {
Iterator<CmsFieldItem> it = field.getItems().iterator();
while (it.hasNext()) {
CmsFieldItem item = it.next();
m_macroResolver.addMacro(item.getLabel(), item.getValue());
if (!item.getLabel().equals(item.getDbLabel())) {
m_macroResolver.addMacro(item.getDbLabel(), item.getValue());
}
}
}
}
// add current date as macro
m_macroResolver.addMacro(
MACRO_DATE,
CmsDateUtil.getDateTime(new Date(), DateFormat.LONG, getRequestContext().getLocale()));
// send optional confirmation mail
if (data.isConfirmationMailEnabled()) {
if (!data.isConfirmationMailOptional()
|| Boolean.valueOf(getParameter(CmsForm.PARAM_SENDCONFIRMATION)).booleanValue()) {
sendConfirmationMail();
}
}
if (data.isTransportDatabase()) {
// save submitted form to database and store the uploaded files
m_entryId = sendDatabase();
if (m_entryId > 0) {
// successful submit
result &= true;
} else {
// error during submit
result &= false;
}
}
if (data.isTransportEmail()) {
result &= sendMail();
}
} catch (Exception e) {
// an error occurred during mail creation
if (LOG.isErrorEnabled()) {
LOG.error("An unexpected error occured.", e);
}
getErrors().put("sendmail", e.getMessage());
result = false;
}
return result;
}
/**
* Returns if the optional check page should be displayed.<p>
*
* @return <code>true</code> if the optional check page should be displayed, otherwise <code>false</code>
*/
public boolean showCheck() {
boolean result = false;
if (getFormConfiguration().getShowCheck() && ACTION_SUBMIT.equals(getParameter(PARAM_FORMACTION))) {
result = true;
} else if (getFormConfiguration().captchaFieldIsOnCheckPage()
&& ACTION_CONFIRMED.equals(getParameter(PARAM_FORMACTION))
&& !validate()) {
result = true;
}
return result;
}
/**
* Returns if the data download page should be displayed.<p>
*
* @return <code>true</code> if the data download page should be displayed, otherwise <code>false</code>
*/
public boolean showDownloadData() {
boolean result = false;
String paramAction = getParameter(CmsFormHandler.PARAM_FORMACTION);
if (CmsFormHandler.ACTION_DOWNLOAD_DATA_1.equals(paramAction)) {
result = true;
} else if (CmsFormHandler.ACTION_DOWNLOAD_DATA_2.equals(paramAction)) {
result = true;
}
return result;
}
/**
* Returns if the expiration text should be shown instead of the form.<p>
*
* @return <code>true</code> if the expiration text should be shown, otherwise <code>false</code>
*/
public boolean showExpired() {
return (getFormConfiguration().getExpirationDate() > 0)
&& (System.currentTimeMillis() > getFormConfiguration().getExpirationDate());
}
/**
* Returns if the release text should be shown instead of the form.<p>
*
* @return <code>true</code> if the release text should be shown, otherwise <code>false</code>
*/
public boolean showRelease() {
return (getFormConfiguration().getReleaseDate() > 0)
&& (System.currentTimeMillis() < getFormConfiguration().getReleaseDate());
}
/**
* Returns if the maximum submissions text should be shown instead of the form.<p>
*
* @return <code>true</code> if the maximum submissions text should be shown, otherwise <code>false</code>
*/
public boolean showMaximumSubmissions() {
CmsFormDataAccess formDataInstance = CmsFormDataAccess.getInstance();
int count = 0;
CmsFormDatabaseFilter filter = CmsFormDatabaseFilter.DEFAULT.filterEntryId(m_entryId);
String formID = getFormConfiguration().getFormId();
filter = filter.filterFormId(formID);
try {
count = formDataInstance.countForms(filter);
} catch (SQLException ex) {
// problem reading from database
}
return ((getFormConfiguration().getMaximumSubmissions() > 0) && (getFormConfiguration().getMaximumSubmissions() <= count));
}
/**
* Returns if the input form should be displayed.<p>
*
* @return <code>true</code> if the input form should be displayed, otherwise <code>false</code>
*/
public boolean showForm() {
boolean result = false;
String formAction = getParameter(PARAM_FORMACTION);
if (isInitial()) {
// initial call
result = true;
} else if (ACTION_CORRECT_INPUT.equalsIgnoreCase(formAction)) {
// user decided to modify his inputs
result = true;
} else if (ACTION_SUBMIT.equalsIgnoreCase(formAction) && !validate()) {
// input field validation failed
result = true;
if (getFormConfiguration().hasCaptchaField() && getFormConfiguration().captchaFieldIsOnCheckPage()) {
// if there is a captcha field and a check page configured, we do have to remove the already
// initialized captcha field from the form again. the captcha field gets initialized together with
// the form, in this moment it is not clear yet whether we have validation errors or and need to
// to go back to the input form...
getFormConfiguration().removeCaptchaField();
}
} else if (ACTION_CONFIRMED.equalsIgnoreCase(formAction)
&& getFormConfiguration().captchaFieldIsOnCheckPage()
&& !validate()) {
// captcha field validation on check page failed: redisplay the check page, not the input page!
result = false;
} else if (ACTION_DOWNLOAD_DATA_1.equalsIgnoreCase(formAction)) {
result = false;
} else if (ACTION_DOWNLOAD_DATA_2.equalsIgnoreCase(formAction)) {
result = false;
} else if (CmsStringUtil.isNotEmpty(getParameter("back"))) {
result = true;
} else if ((CmsStringUtil.isNotEmpty(getParameter("page"))) && CmsStringUtil.isEmpty(getParameter("finalpage"))) {
result = true;
}
return result;
}
/**
* Returns if the template head or other HTML should be included (the page does not serve as a download page).<p>
*
* @return <code>true</code> if the template head or other HTML should be included (the page does not serve as a download page)
*/
public boolean showTemplate() {
boolean result = true;
if (CmsFormHandler.ACTION_DOWNLOAD_DATA_2.equals(getParameter(CmsFormHandler.PARAM_FORMACTION))) {
result = false;
}
return result;
}
/**
* Validation method that checks the given input fields.<p>
*
* All errors are stored in the member m_errors Map, with the input field name as key
* and the error message String as value.<p>
*
* @return <code>true</code> if all necessary fields can be validated, otherwise <code>false</code>
*/
public boolean validate() {
if (m_isValidatedCorrect != null) {
return m_isValidatedCorrect.booleanValue();
}
boolean allOk = true;
// if the previous button was used, then no validation is necessary here
if (CmsStringUtil.isNotEmpty(getParameter("back"))) {
return true;
}
// iterate the form fields
List<I_CmsField> fields = getFormConfiguration().getFields();
int pagingPos = fields.size();
if (CmsStringUtil.isNotEmpty(getParameter("page"))) {
int value = new Integer(getParameter("page")).intValue();
pagingPos = CmsPagingField.getLastFieldPosFromPage(this, value) + 1;
}
// validate each form field
for (int i = 0, n = pagingPos; i < n; i++) {
I_CmsField currentField = fields.get(i);
if (CmsCaptchaField.class.isAssignableFrom(currentField.getClass())) {
// the captcha field doesn't get validated here...
continue;
}
// call the field validation
if (!validateField(currentField)) {
allOk = false;
}
if (currentField.hasCurrentSubFields()) {
// also validate the current sub fields
Iterator<I_CmsField> k = currentField.getCurrentSubFields().iterator();
while (k.hasNext()) {
// call the sub field validation
if (!validateField(k.next())) {
allOk = false;
}
}
}
}
CmsCaptchaField captchaField = getFormConfiguration().getCaptchaField();
if ((captchaField != null) && (pagingPos == fields.size())) {
// captcha field is enabled and we are on the last page or check page
boolean captchaFieldIsOnInputPage = getFormConfiguration().captchaFieldIsOnInputPage()
&& getFormConfiguration().isInputFormSubmitted();
boolean captchaFieldIsOnCheckPage = getFormConfiguration().captchaFieldIsOnCheckPage()
&& getFormConfiguration().isCheckPageSubmitted();
if (captchaFieldIsOnInputPage || captchaFieldIsOnCheckPage) {
if (!captchaField.validateCaptchaPhrase(this, captchaField.getValue())) {
getErrors().put(captchaField.getName(), ERROR_VALIDATION);
allOk = false;
}
}
}
m_isValidatedCorrect = Boolean.valueOf(allOk);
return allOk;
}
/**
* Returns the HTML for the optional form check page, generated by using the given string template file.<p>
*
* @return the HTML for the optional form check page, generated by using the given string template file
*/
protected String buildCheckHtml() {
// create the check page
StringTemplate sTemplate = getOutputTemplate("checkpage");
// set the necessary attributes to use in the string template
sTemplate.setAttribute(
"formuri",
OpenCms.getLinkManager().substituteLink(getCmsObject(), getCmsObject().getRequestContext().getUri()));
sTemplate.setAttribute("formconfig", getFormConfiguration());
sTemplate.setAttribute("checktext", getFormCheckText());
CmsCaptchaField captchaField = getFormConfiguration().getCaptchaField();
sTemplate.setAttribute("captchafield", captchaField);
if (captchaField != null) {
// do this only if a captcha field is configured
String errorMessage = getErrors().get(captchaField.getName());
if (errorMessage != null) {
// create the error message for the field
if (CmsFormHandler.ERROR_MANDATORY.equals(errorMessage)) {
errorMessage = getMessages().key("form.error.mandatory");
} else {
errorMessage = getMessages().key("form.error.validation");
}
}
sTemplate.setAttribute("captchaerror", errorMessage);
sTemplate.setAttribute(
"captchaimagelink",
OpenCms.getLinkManager().substituteLink(
getCmsObject(),
"/system/modules/com.alkacon.opencms.formgenerator/pages/captcha.jsp?"
+ captchaField.getCaptchaSettings().toRequestParams(getCmsObject())));
}
List<I_CmsField> fields = getFormConfiguration().getAllFields(true, false, false);
List<I_CmsField> checkFields = new ArrayList<I_CmsField>(fields.size());
for (int i = 0, n = fields.size(); i < n; i++) {
I_CmsField current = fields.get(i);
if ((!CmsDynamicField.class.isAssignableFrom(current.getClass())
&& !CmsHiddenField.class.isAssignableFrom(current.getClass())
&& !CmsCaptchaField.class.isAssignableFrom(current.getClass()) && !CmsHiddenDisplayField.class.isAssignableFrom(current.getClass()))
|| (CmsDisplayField.class.isAssignableFrom(current.getClass()))) {
if ((current instanceof CmsEmptyField) || (current instanceof CmsPagingField)) {
continue;
}
String label = current.getLabel();
if (current instanceof CmsTableField) {
label = ((CmsTableField)current).buildLabel(this);
}
String value = current.toString();
if (current instanceof CmsTableField) {
value = ((CmsTableField)current).buildRows(this);
} else if (current instanceof CmsPasswordField) {
value = value.replaceAll(".", "*");
} else if (current instanceof CmsFileUploadField) {
if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
// try to read upload item from session attribute
FileItem fileItem = getUploadFile(current);
if (fileItem != null) {
value = fileItem.getName();
}
}
value = CmsFormHandler.getTruncatedFileItemName(value);
value = convertToHtmlValue(value);
} else {
value = convertToHtmlValue(value);
}
I_CmsField checkField = new CmsTextField();
checkField.setLabel(label);
checkField.setValue(value);
checkFields.add(checkField);
}
}
sTemplate.setAttribute("checkfields", checkFields);
sTemplate.setAttribute("hiddenfields", createHiddenFields());
sTemplate.setAttribute("checkbutton", getMessages().key("form.button.checked"));
sTemplate.setAttribute("correctbutton", getMessages().key("form.button.correct"));
return sTemplate.toString();
}
/**
* Returns the HTML for the form confirmation page, generated by using the given string template file.<p>
*
* @return the HTML for the form confirmation page, generated by using the given string template file
*/
protected String buildConfirmHtml() {
// create the confirmation page
StringTemplate sTemplate = getOutputTemplate("confirmationpage");
// set the necessary attributes to use in the string template
sTemplate.setAttribute("formconfig", getFormConfiguration());
sTemplate.setAttribute("confirmtext", getFormConfirmationText());
// generate the list of fields to display
List<I_CmsField> fields = getFormConfiguration().getAllFields(true, false, true);
List<I_CmsField> confirmFields = new ArrayList<I_CmsField>(fields.size());
for (int i = 0, n = fields.size(); i < n; i++) {
I_CmsField current = fields.get(i);
if (CmsHiddenField.class.isAssignableFrom(current.getClass())
|| CmsPrivacyField.class.isAssignableFrom(current.getClass())
|| CmsCaptchaField.class.isAssignableFrom(current.getClass())
|| CmsPagingField.class.isAssignableFrom(current.getClass())) {
continue;
}
String label = current.getLabel();
if (current instanceof CmsTableField) {
label = ((CmsTableField)current).buildLabel(this);
} else if (current instanceof CmsEmptyField) {
label = "";
}
String value = current.toString();
if ((current instanceof CmsDisplayField)) {
value = convertToHtmlValue(value);
} else if ((current instanceof CmsHiddenDisplayField)) {
continue;
} else if ((current instanceof CmsDynamicField)) {
if (!current.isMandatory()) {
// show dynamic fields only if they are marked as mandatory
continue;
}
// compute the value for the dynamic field
value = getFormConfiguration().getFieldStringValueByName(current.getName());
value = convertToHtmlValue(value);
} else if (current instanceof CmsTableField) {
value = ((CmsTableField)current).buildRows(this);
} else if (current instanceof CmsPasswordField) {
value = value.replaceAll(".", "*");
} else if (current instanceof CmsFileUploadField) {
if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
// try to read upload item from session attribute
FileItem fileItem = getUploadFile(current);
if (fileItem != null) {
value = fileItem.getName();
}
}
value = CmsFormHandler.getTruncatedFileItemName(value);
value = convertToHtmlValue(value);
} else if (current instanceof CmsEmptyField) {
// do nothing
} else {
value = convertToHtmlValue(value);
}
// if label and value is not set, skip it
if (CmsStringUtil.isEmpty(label) && CmsStringUtil.isEmpty(value)) {
continue;
}
I_CmsField confirmField = new CmsTextField();
confirmField.setLabel(label);
confirmField.setValue(value);
confirmFields.add(confirmField);
}
sTemplate.setAttribute("confirmfields", confirmFields);
return sTemplate.toString();
}
/**
* Returns the HTML for the input form, generated by using the given string template file.<p>
*
* @return the HTML for the input form, generated by using the given string template file
*/
protected String buildFormHtml() {
// determine if form type has to be set to "multipart/form-data" in case of upload fields
String encType = null;
for (Iterator<I_CmsField> i = getFormConfiguration().getFields().iterator(); i.hasNext();) {
I_CmsField field = i.next();
if (field.getType().equals(CmsFileUploadField.getStaticType())) {
encType = " enctype=\"multipart/form-data\"";
break;
}
}
// determine error message
String errorMessage = null;
if (hasValidationErrors()) {
errorMessage = getMessages().key("form.error.message");
}
// determine mandatory message
String mandatoryMessage = null;
if (getFormConfiguration().isShowMandatory() && getFormConfiguration().hasMandatoryFields()) {
mandatoryMessage = getMessages().key("form.message.mandatory");
}
// calculate fields to show (e.g. if paging is activated)
boolean paging = false;
int pos = 0;
int fieldNr = 0;
int place = 0;
int currPage = 1;
int pagingPos = 0;
if (getParameterMap().containsKey(PARAM_BACK) && getParameterMap().containsKey(PARAM_PAGE)) {
String[] pagingString = getParameterMap().get(PARAM_PAGE);
currPage = new Integer(pagingString[0]).intValue();
currPage = CmsPagingField.getPreviousPage(currPage);
} else if (getParameterMap().containsKey("page") && !hasValidationErrors()) {
String[] pagingString = getParameterMap().get(PARAM_PAGE);
currPage = new Integer(pagingString[0]).intValue();
currPage = CmsPagingField.getNextPage(currPage);
} else if (getParameterMap().containsKey(PARAM_PAGE) && hasValidationErrors()) {
String[] pagingString = getParameterMap().get(PARAM_PAGE);
currPage = new Integer(pagingString[0]).intValue();
}
pagingPos = CmsPagingField.getFirstFieldPosFromPage(this, currPage);
fieldNr = pagingPos;
// generate HTML for the input fields
StringBuffer fieldHtml = new StringBuffer(getFormConfiguration().getFields().size() * 256);
StringBuffer subFieldJSBuf = new StringBuffer(512);
for (int i = pagingPos, n = getFormConfiguration().getFields().size(); i < n; i++) {
fieldNr += 1;
// loop through all form input fields
I_CmsField field = getFormConfiguration().getFields().get(i);
if (i == (n - 1)) {
// the last one has to close the row
place = 1;
}
field.setPlaceholder(place);
field.setPosition(pos);
field.setFieldNr(fieldNr);
String infoMessage = getInfos().get(field.getName());
// validate the file upload field here already because of the lost values in these fields
if (field instanceof CmsFileUploadField) {
infoMessage = field.validateForInfo(this);
}
fieldHtml.append(field.buildHtml(
this,
getMessages(),
getErrors().get(field.getName()),
getFormConfiguration().isShowMandatory(),
infoMessage));
subFieldJSBuf.append(field.getSubFieldScript());
pos = field.getPosition();
place = field.getPlaceholder();
// if there is a paging field do not show the following fields
if (field instanceof CmsPagingField) {
paging = true;
break;
}
}
// determine if subfield JavaScript has to be added
String subFieldJS = null;
if (subFieldJSBuf.length() > 0) {
subFieldJS = subFieldJSBuf.toString();
}
// determine if the submit and other buttons are shown
String submitButton = null;
String resetButton = null;
String hiddenFields = null;
String prevButton = null;
if (!paging) {
// show submit button on last form page
submitButton = getMessages().key("form.button.submit");
if (getParameterMap().containsKey(PARAM_PAGE)) {
// add hidden fields and previous button on last form page
StringBuffer hFieldsBuf = CmsPagingField.appendHiddenFields(
this,
getMessages(),
getFormConfiguration().getFields().size());
hFieldsBuf.append("<input type=\"hidden\" name=\"page\" value=\"").append(currPage).append("\" />");
hFieldsBuf.append("<input type=\"hidden\" name=\"finalpage\" value=\"true\" />");
hiddenFields = hFieldsBuf.toString();
prevButton = getMessages().key("form.button.prev");
} else {
// there is no paging, but do the file upload values in hidden values
// otherwise the contents are forgotten in case of other wrong filled fields
StringBuffer hFieldsBuf = CmsPagingField.appendHiddenUploadFields(this, getMessages());
hiddenFields = hFieldsBuf.toString();
}
if (getFormConfiguration().isShowReset()) {
// show reset button if configured
resetButton = getMessages().key("form.button.reset");
}
}
// determine if the download button has to be shown
String downloadButton = null;
if (!getRequestContext().getCurrentProject().isOnlineProject() && getFormConfiguration().isTransportDatabase()) {
downloadButton = getMessages().key("form.button.downloaddata");
}
// create the main form and pass the previously generated field HTML as attribute
StringTemplate sTemplate = getOutputTemplate("form");
// set the necessary attributes to use in the string template
sTemplate.setAttribute(
"formuri",
OpenCms.getLinkManager().substituteLink(getCmsObject(), getCmsObject().getRequestContext().getUri()));
sTemplate.setAttribute("enctype", encType);
sTemplate.setAttribute("errormessage", errorMessage);
sTemplate.setAttribute("mandatorymessage", mandatoryMessage);
sTemplate.setAttribute("formconfig", getFormConfiguration());
sTemplate.setAttribute("fields", fieldHtml.toString());
sTemplate.setAttribute("subfieldjs", subFieldJS);
sTemplate.setAttribute("downloadbutton", downloadButton);
sTemplate.setAttribute("submitbutton", submitButton);
sTemplate.setAttribute("resetbutton", resetButton);
sTemplate.setAttribute("hiddenfields", hiddenFields);
sTemplate.setAttribute("prevbutton", prevButton);
return sTemplate.toString();
}
/**
* Returns the HTML for errors in the used string template group file.<p>
*
* <b>Note</b>: Only generates output in an offline project
*
* @return the HTML for errors in the used string template group file
*/
protected String buildTemplateGroupCheckHtml() {
String result = "";
if (!getRequestContext().getCurrentProject().isOnlineProject()) {
// check template group errors
StringTemplateErrorListener el = getOutputTemplateGroup().getErrorListener();
if ((el != null) && CmsStringUtil.isNotEmptyOrWhitespaceOnly(el.toString())) {
// errors(s) found, show error template
try {
CmsFile stFile = getCmsObject().readFile(CmsForm.VFS_PATH_ERROR_TEMPLATEFILE);
String stContent = new String(
stFile.getContents(),
getCmsObject().getRequestContext().getEncoding());
StringTemplate st = new StringTemplate(stContent, DefaultTemplateLexer.class);
// set the error attributes of the template
st.setAttribute("errorheadline", "Error parsing output template group");
st.setAttribute("errortext", "Error output:<br/><pre>" + el.toString() + "</pre>");
st.setAttribute("errortemplatenames", getOutputTemplateGroup().getTemplateNames());
result = st.toString();
} catch (Exception e) {
// something went wrong, log error
LOG.error("Error while getting error output template from file \""
+ CmsForm.VFS_PATH_ERROR_TEMPLATEFILE
+ "\".");
}
}
}
return result;
}
/**
* Parses the form configuration and creates the necessary configuration objects.<p>
*
* @param req the JSP request
* @param formConfigUri URI of the form configuration file, if not provided, current URI is used for configuration
*
* @throws Exception if creating the form configuration objects fails
*/
protected void configureForm(HttpServletRequest req, String formConfigUri) throws Exception {
// read the form configuration file from VFS
if (CmsStringUtil.isEmpty(formConfigUri)) {
formConfigUri = getRequestContext().getUri();
}
m_multipartFileItems = CmsRequestUtil.readMultipartFileItems(req);
m_macroResolver = CmsMacroResolver.newInstance();
m_macroResolver.setKeepEmptyMacros(true);
m_macroResolver.setCmsObject(getCmsObject());
m_macroResolver.addMacro(
MACRO_URL,
OpenCms.getSiteManager().getCurrentSite(getCmsObject()).getServerPrefix(
getCmsObject(),
getRequestContext().getUri())
+ link(getRequestContext().getUri()));
m_macroResolver.addMacro(MACRO_LOCALE, getRequestContext().getLocale().toString());
if (m_multipartFileItems != null) {
m_parameterMap = CmsRequestUtil.readParameterMapFromMultiPart(
getRequestContext().getEncoding(),
m_multipartFileItems);
} else {
m_parameterMap = new HashMap<String, String[]>();
m_parameterMap.putAll(getRequest().getParameterMap());
}
if (m_multipartFileItems != null) {
Map<String, FileItem> fileUploads = (Map<String, FileItem>)req.getSession().getAttribute(
ATTRIBUTE_FILEITEMS);
if (fileUploads == null) {
fileUploads = new HashMap<String, FileItem>();
}
// check, if there are any attachments
Iterator<FileItem> i = m_multipartFileItems.iterator();
while (i.hasNext()) {
FileItem fileItem = i.next();
if (CmsStringUtil.isNotEmpty(fileItem.getName())) {
// append file upload to the map of file items
fileUploads.put(fileItem.getFieldName(), fileItem);
m_parameterMap.put(fileItem.getFieldName(), new String[] {fileItem.getName()});
}
}
req.getSession().setAttribute(ATTRIBUTE_FILEITEMS, fileUploads);
} else {
req.getSession().removeAttribute(ATTRIBUTE_FILEITEMS);
}
String formAction = getParameter(PARAM_FORMACTION);
// security check: some form actions are not allowed for everyone:
if (this.getCmsObject().getRequestContext().getCurrentProject().isOnlineProject()) {
if (CmsFormHandler.ACTION_DOWNLOAD_DATA_1.equals(formAction)) {
LOG.error("Received an illegal request for download data of form "
+ formConfigUri
+ " (online request)");
formAction = ACTION_SUBMIT;
}
}
m_isValidatedCorrect = null;
setInitial(CmsStringUtil.isEmpty(formAction));
// get the localized messages
initMessages(formConfigUri);
// get the form configuration
setFormConfiguration(new CmsForm(this, getMessages(), isInitial(), formConfigUri, formAction));
}
/**
* Creates a list of Internet addresses (email) from a semicolon separated String.<p>
*
* @param mailAddresses a semicolon separated String with email addresses
* @return list of Internet addresses (email)
* @throws AddressException if an email address is not correct
*/
protected List<InternetAddress> createInternetAddresses(String mailAddresses) throws AddressException {
if (CmsStringUtil.isNotEmpty(mailAddresses)) {
// at least one email address is present, generate list
StringTokenizer T = new StringTokenizer(mailAddresses, ";");
List<InternetAddress> addresses = new ArrayList<InternetAddress>(T.countTokens());
while (T.hasMoreTokens()) {
InternetAddress address = new InternetAddress(T.nextToken());
addresses.add(address);
}
return addresses;
} else {
// no address given, return empty list
return Collections.emptyList();
}
}
/**
* Returns the request parameter with the specified name.<p>
*
* @param parameter the parameter to return
*
* @return the parameter value
*/
protected String getParameter(String parameter) {
try {
return (m_parameterMap.get(parameter))[0];
} catch (NullPointerException e) {
return "";
}
}
/**
* Initializes the localized messages for the web form.<p>
*
* @param formConfigUri URI of the form configuration file, if not provided, current URI is used for configuration
*
* @throws CmsException if accessing the VFS fails
*/
protected void initMessages(String formConfigUri) throws CmsException {
// get the localized messages
CmsModule module = OpenCms.getModuleManager().getModule(CmsForm.MODULE_NAME);
String para = module.getParameter("message", "/com/alkacon/opencms/formgenerator/workplace");
// get the site message
String siteroot = getCmsObject().getRequestContext().getSiteRoot();
if (siteroot.startsWith(CmsResource.VFS_FOLDER_SITES)) {
siteroot = siteroot.substring(CmsResource.VFS_FOLDER_SITES.length() + 1);
}
if (!CmsStringUtil.isEmptyOrWhitespaceOnly(siteroot)) {
String fileSite = module.getParameter("message_" + siteroot);
if (!CmsStringUtil.isEmptyOrWhitespaceOnly(fileSite)) {
para = fileSite;
}
}
// use the optional property file if configured
String propertyFile = null;
if (CmsStringUtil.isEmpty(formConfigUri)) {
formConfigUri = getRequestContext().getUri();
}
CmsProperty cmsProperty = getCmsObject().readPropertyObject(formConfigUri, "webform.propertyfile", false);
if (cmsProperty != null) {
propertyFile = cmsProperty.getValue();
}
if (CmsStringUtil.isNotEmpty(propertyFile)) {
addMessages(new CmsMessages(propertyFile, getRequestContext().getLocale()));
} else {
addMessages(new CmsMessages(para, getRequestContext().getLocale()));
}
}
/**
* Stores the given form data in the database.<p>
*
* @return the entry id of the submitted form in the database or '-1' if something goes wrong
*
* @throws Exception if something goes wrong
*/
protected int sendDatabase() throws Exception {
return CmsFormDataAccess.getInstance().writeFormData(this);
}
/**
* Sends the mail with the form data to the specified recipients.<p>
*
* If configured, sends also a confirmation mail to the form submitter.<p>
*
* @return true if the mail has been successfully sent, otherwise false
*/
protected boolean sendMail() {
try {
// create the new mail message depending on the configured email type
if (getFormConfiguration().getMailType().equals(CmsForm.MAILTYPE_HTML)) {
// create a HTML email
CmsHtmlMail theMail = new CmsHtmlMail();
theMail.setCharset(getCmsObject().getRequestContext().getEncoding());
if (CmsStringUtil.isNotEmpty(getFormConfiguration().getMailFrom())) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getFormConfiguration().getMailFromName())) {
theMail.setFrom(
m_macroResolver.resolveMacros(getFormConfiguration().getMailFrom()),
m_macroResolver.resolveMacros(getFormConfiguration().getMailFromName()));
} else {
theMail.setFrom(m_macroResolver.resolveMacros(getFormConfiguration().getMailFrom()));
}
}
theMail.setTo(createInternetAddresses(getFormConfiguration().getMailTo()));
List<InternetAddress> ccRec = createInternetAddresses(m_macroResolver.resolveMacros(getFormConfiguration().getMailCC()));
if (ccRec.size() > 0) {
theMail.setCc(ccRec);
}
List<InternetAddress> bccRec = createInternetAddresses(m_macroResolver.resolveMacros(getFormConfiguration().getMailBCC()));
if (bccRec.size() > 0) {
theMail.setBcc(bccRec);
}
theMail.setSubject(m_macroResolver.resolveMacros(getFormConfiguration().getMailSubjectPrefix()
+ getFormConfiguration().getMailSubject()));
theMail.setHtmlMsg(createMailTextFromFields(true, false));
// attach file uploads
Map<String, FileItem> fileUploads = (Map<String, FileItem>)getRequest().getSession().getAttribute(
ATTRIBUTE_FILEITEMS);
if (fileUploads != null) {
Iterator<FileItem> i = fileUploads.values().iterator();
while (i.hasNext()) {
FileItem attachment = i.next();
if (attachment != null) {
String filename = attachment.getName().substring(
attachment.getName().lastIndexOf(File.separator) + 1);
theMail.attach(
new CmsByteArrayDataSource(
filename,
attachment.get(),
OpenCms.getResourceManager().getMimeType(filename, null, "application/octet-stream")),
filename,
filename);
}
}
}
// send the mail
theMail.send();
} else {
// create a plain text email
CmsSimpleMail theMail = new CmsSimpleMail();
theMail.setCharset(getCmsObject().getRequestContext().getEncoding());
if (CmsStringUtil.isNotEmpty(getFormConfiguration().getMailFrom())) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(getFormConfiguration().getMailFromName())) {
theMail.setFrom(
m_macroResolver.resolveMacros(getFormConfiguration().getMailFrom()),
m_macroResolver.resolveMacros(getFormConfiguration().getMailFromName()));
} else {
theMail.setFrom(m_macroResolver.resolveMacros(getFormConfiguration().getMailFrom()));
}
}
theMail.setTo(createInternetAddresses(getFormConfiguration().getMailTo()));
List<InternetAddress> ccRec = createInternetAddresses(m_macroResolver.resolveMacros(getFormConfiguration().getMailCC()));
if (ccRec.size() > 0) {
theMail.setCc(ccRec);
}
List<InternetAddress> bccRec = createInternetAddresses(m_macroResolver.resolveMacros(getFormConfiguration().getMailBCC()));
if (bccRec.size() > 0) {
theMail.setBcc(bccRec);
}
theMail.setSubject(m_macroResolver.resolveMacros(getFormConfiguration().getMailSubjectPrefix()
+ getFormConfiguration().getMailSubject()));
theMail.setMsg(createMailTextFromFields(false, false));
// send the mail
theMail.send();
}
} catch (Exception e) {
// an error occured during mail creation
if (LOG.isErrorEnabled()) {
LOG.error(e.getLocalizedMessage(), e);
}
getErrors().put("sendmail", e.getMessage());
return false;
}
return true;
}
/**
* Sets the form configuration.<p>
*
* @param configuration the form configuration
*/
protected void setFormConfiguration(CmsForm configuration) {
m_formConfiguration = configuration;
}
/**
* Sets if the form is displayed for the first time.<p>
*
* @param initial true if the form is displayed for the first time, otherwise false
*/
protected void setInitial(boolean initial) {
m_initial = initial;
}
/**
* Sets the localized messages.<p>
*
* @param messages the localized messages
*/
protected void setMessages(CmsMessages messages) {
addMessages(messages);
}
/**
* Checks if the given field should be used in form data macros.<p>
*
* @param field the field to check
*
* @return if the given field should be used in form data macros
*/
protected boolean useInFormDataMacro(I_CmsField field) {
// don't show the letter of agreement (CmsPrivacyField) and captcha field value
return !((field instanceof CmsPrivacyField) || (field instanceof CmsCaptchaField));
}
/**
* Creates an object from data type I_CmsWebformActionHandler.<p>
*
* @param className name from class to create an object from
*
* @return object from data type I_CmsWebformActionHandler
*/
private I_CmsWebformActionHandler getObject(String className) throws Exception {
I_CmsWebformActionHandler object = null;
Class<I_CmsWebformActionHandler> c = (Class<I_CmsWebformActionHandler>)Class.forName(className);
object = c.newInstance();
return object;
}
/**
* Validates a single form field.<p>
*
* @param field the field to validate
*
* @return <code>true</code> if the field is validated, otherwise <code>false</code>
*/
private boolean validateField(I_CmsField field) {
if (field == null) {
return true;
}
if (CmsCaptchaField.class.isAssignableFrom(field.getClass())) {
// the captcha field doesn't get validated here...
return true;
}
// check if a file upload field is empty, but it was filled out already
String validationInfo = "";
validationInfo = field.validateForInfo(this);
if (CmsStringUtil.isNotEmpty(validationInfo)) {
getInfos().put(field.getName(), validationInfo);
}
// check for validation errors
String validationError = field.validate(this);
if (CmsStringUtil.isNotEmpty(validationError)) {
getErrors().put(field.getName(), validationError);
return false;
}
return true;
}
}