/*
* File : $Source: /alkacon/cvs/alkacon/com.alkacon.opencms.registration/src/com/alkacon/opencms/registration/CmsRegistrationFormHandler.java,v $
* Date : $Date: 2011/03/10 11:59:04 $
* Version: $Revision: 1.4 $
*
* This file is part of the Alkacon OpenCms Add-On Module Package
*
* Copyright (c) 2007 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.registration;
import com.alkacon.opencms.formgenerator.CmsDynamicField;
import com.alkacon.opencms.formgenerator.CmsEmailField;
import com.alkacon.opencms.formgenerator.CmsForm;
import com.alkacon.opencms.formgenerator.CmsFormHandler;
import com.alkacon.opencms.formgenerator.I_CmsField;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsUser;
import org.opencms.i18n.CmsEncoder;
import org.opencms.i18n.CmsMessages;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.module.CmsModule;
import org.opencms.util.CmsDateUtil;
import org.opencms.util.CmsMacroResolver;
import org.opencms.util.CmsRequestUtil;
import org.opencms.util.CmsStringUtil;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.logging.Log;
import org.antlr.stringtemplate.StringTemplate;
/**
* 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 Michael Moossen
*
* @version $Revision: 1.4 $
*
* @since 7.0.4
*/
public class CmsRegistrationFormHandler extends CmsFormHandler {
/** Macro name for the activation uri macro that can be used in mail text fields. */
public static final String MACRO_ACTURI = "acturi";
/** Parameter name for the activation code. */
public static final String PARAM_ACTCODE = "ac";
/** Field name for email address. */
private static final String FIELD_EMAIL = "email";
/** Field name for login. */
private static final String FIELD_LOGIN = "login";
/** Field name for password. */
private static final String FIELD_PASSWORD = "password";
/** The log object for this class. */
private static final Log LOG = CmsLog.getLog(CmsRegistrationFormHandler.class);
/** The module name. */
private static final String MODULE = "com.alkacon.opencms.registration";
/** Reflection method prefix constant. */
private static final String REFLECTION_GETTER_PREFIX = "get";
/** Reflection method prefix constant. */
private static final String REFLECTION_SETTER_PREFIX = "set";
/** Flag to determine if the registration page or the profile page is shown. */
private boolean m_profilePage;
/**
* 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 CmsRegistrationFormHandler() {
super();
}
/**
* Returns the user name from the activation code.<p>
*
* @param code the activation code
*
* @return the user name
*/
public static String getUserName(String code) {
String reverse = "";
for (int i = 0; i < code.length(); i++) {
reverse = (code.charAt(i) + reverse);
}
return new String(Base64.decodeBase64(reverse.getBytes()));
}
/**
* As test case.<p>
*
* @param args not used
*/
public static void main(String[] args) {
CmsUser user = new CmsUser(null, "/mylongouname/m.moossen@alkacon.com", "", "", "", "", 0, 0, 0, null);
String code = getActivationCode(user);
System.out.println(code);
System.out.println(getUserName(code));
CmsMacroResolver macroResolver = CmsMacroResolver.newInstance();
macroResolver.setKeepEmptyMacros(true);
// create macros for getters
Method[] methods = CmsUser.class.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.getReturnType() != String.class) {
continue;
}
if (method.getParameterTypes().length > 0) {
continue;
}
if (!method.getName().startsWith("get")
|| (method.getName().length() < 4)
|| method.getName().equals("getPassword")) {
continue;
}
String label = ("" + method.getName().charAt(3)).toLowerCase();
if (method.getName().length() > 4) {
label += method.getName().substring(4);
}
try {
Object value = method.invoke(user, new Object[] {});
if (value == null) {
value = "";
}
macroResolver.addMacro(label, value.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
// add addinfo values as macros
Iterator itFields = user.getAdditionalInfo().entrySet().iterator();
while (itFields.hasNext()) {
Map.Entry entry = (Map.Entry)itFields.next();
if ((entry.getValue() instanceof String) && (entry.getKey() instanceof String)) {
macroResolver.addMacro(entry.getKey().toString(), entry.getValue().toString());
}
}
// add login
macroResolver.addMacro(FIELD_LOGIN, user.getSimpleName());
}
/**
* Returns the activation code for the given user.<p>
*
* @param user the user to generate an activation code for
*
* @return the activation code
*/
private static String getActivationCode(CmsUser user) {
String code = new String(Base64.encodeBase64(user.getName().getBytes()));
String reverse = "";
for (int i = 0; i < code.length(); i++) {
reverse = (code.charAt(i) + reverse);
}
return reverse;
}
/**
* Activates the user identified by the current activation code code.<p>
*
* @throws CmsException if something goes wrong
*/
public void activateUser() throws CmsException {
CmsUser user = getUser();
user.setEnabled(true);
CmsRegistrationModuleAction.getAdminCms().writeUser(user);
}
/**
* Special form output for the registration.<p>
*
* @see com.alkacon.opencms.formgenerator.CmsFormHandler#createForm()
*/
@Override
public void createForm() throws IOException, JspException {
// the output writer
Writer out = getJspContext().getOut();
String actcode = getRequest().getParameter(PARAM_ACTCODE);
if (isRegistrationPage() && CmsStringUtil.isNotEmptyOrWhitespaceOnly(actcode)) {
// show user activation page
getRequest().setAttribute("formhandler", this);
include("/system/modules/com.alkacon.opencms.registration/elements/activation.jsp");
} else {
boolean showForm = showForm();
if (!showForm) {
// form has been submitted with correct values, decide further actions
if (showCheck()) {
// show optional check page
out.write(buildCheckHtml());
} else {
// try to send a notification email with the submitted form field values
if (sendData()) {
// successfully sent mail, show confirmation end page
out.write(buildConfirmHtml());
} else {
// failure sending mail, show error output
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 {
// set some variables depending on page and show the form
if (isProfilePage()) {
// the profile page has a modified form text to show
getFormConfiguration().setFormText(getEditFormText());
if (isInitial()) {
// fill in the field values on initial profile page
fillFields();
}
}
// create the form
out.write(buildFormHtml());
}
}
}
/**
* Checks if the current activation code identifies an existing user.<p>
*
* @return if the user is exists
*/
public boolean existUser() {
return (getUser() != null);
}
/**
* Fills the fields with the data of the current user.<p>
*/
public void fillFields() {
if (getRequestContext().currentUser().isGuestUser()) {
// ignore if guest user
return;
}
Iterator<I_CmsField> fields = getRegFormConfiguration().getFields().iterator();
while (fields.hasNext()) {
I_CmsField field = fields.next();
if (CmsStringUtil.isEmptyOrWhitespaceOnly(field.getDbLabel())) {
continue;
}
String value = getUserValue(getRequestContext().currentUser(), field.getDbLabel());
field.setValue(value);
}
}
/**
* Returns the text to be show in the profile page.<p>
*
* @return the text to be show in the profile page
*/
public String getEditFormText() {
CmsMacroResolver macroResolver = getUserMacroResolver();
return macroResolver.resolveMacros(getRegFormConfiguration().getFormText());
}
/**
* Returns the text to be show when the user activates his account.<p>
*
* @return the text to be show when the user activates his account
*/
public String getFormActivatedText() {
CmsMacroResolver macroResolver = getUserMacroResolver();
return macroResolver.resolveMacros(getRegFormConfiguration().getFormActivationText());
}
/**
* Returns the form configuration.<p>
*
* @return the form configuration
*/
@Override
public CmsForm getFormConfiguration() {
if (isProfilePage()) {
return getRegFormConfiguration();
} else {
return m_formConfiguration;
}
}
/**
* Returns the form configuration.<p>
*
* @return the form configuration
*/
public CmsRegistrationForm getRegFormConfiguration() {
return (CmsRegistrationForm)super.getFormConfiguration();
}
/**
* Initializes the form handler 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
*/
@Override
protected void configureForm(HttpServletRequest req, String formConfigUri) throws Exception {
m_multipartFileItems = CmsRequestUtil.readMultipartFileItems(req);
m_macroResolver = CmsMacroResolver.newInstance();
m_macroResolver.setKeepEmptyMacros(true);
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)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);
m_isValidatedCorrect = null;
setInitial(CmsStringUtil.isEmpty(formAction));
// get the localized messages
CmsModule module = OpenCms.getModuleManager().getModule(MODULE);
String para = module.getParameter("message", "/com/alkacon/opencms/registration/workplace");
setMessages(new CmsMessages(para, getRequestContext().getLocale()));
// get the form configuration
setFormConfiguration(new CmsRegistrationForm(this, getMessages(), isInitial(), formConfigUri, formAction));
}
/**
* Returns if the profile page is shown.<p>
*
* @return <code>true</code> if the profile page is shown, otherwise <code>false</code>
*/
public boolean isProfilePage() {
return m_profilePage;
}
/**
* Returns if the registration page is shown.<p>
*
* @return <code>true</code> if the registration page is shown, otherwise <code>false</code>
*/
public boolean isRegistrationPage() {
return !m_profilePage;
}
/**
* Checks if the current activation code identifies an existing user and if it is already activated.<p>
*
* @return if the user is activated
*/
public boolean isUserActivated() {
CmsUser user = getUser();
if (user == null) {
return false;
}
return user.isEnabled();
}
/**
* Sends the collected data due to the configuration of the form
* (email, database or both).<p>
*
* @return true if successful
*/
@Override
public boolean sendData() {
boolean result = true;
try {
CmsRegistrationForm data = getRegFormConfiguration();
data.removeCaptchaField();
// fill the macro resolver for resolving in subject and content:
List<I_CmsField> fields = data.getAllFields();
Iterator<I_CmsField> itFields = fields.iterator();
// add field values as macros
while (itFields.hasNext()) {
I_CmsField field = itFields.next();
String fValue = field.getValue();
if (field instanceof CmsDynamicField) {
fValue = data.getFieldStringValueByName(field.getName());
}
m_macroResolver.addMacro(field.getLabel(), fValue);
if (field instanceof CmsEmailField) {
if (data.isEmailAsLogin()) {
m_macroResolver.addMacro(FIELD_LOGIN, fValue);
}
}
if (!field.getLabel().equals(field.getDbLabel())) {
m_macroResolver.addMacro(field.getDbLabel(), fValue);
}
}
// add current date as macro
m_macroResolver.addMacro(MACRO_DATE, CmsDateUtil.getDateTime(
new Date(),
DateFormat.LONG,
getRequestContext().getLocale()));
if (getRequestContext().currentUser().isGuestUser()) {
// create the user here
createUser();
} else {
editUser();
}
// send optional confirmation mail
if (data.isConfirmationMailEnabled()) {
if (!data.isConfirmationMailOptional()
|| Boolean.valueOf(getParameter(CmsForm.PARAM_SENDCONFIRMATION)).booleanValue()) {
sendConfirmationMail();
}
}
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;
}
/**
* Sets if the profile page is shown.<p>
*
* @param profilePage flag to determine if the registration page or the profile page is shown
*/
public void setProfilePage(boolean profilePage) {
m_profilePage = profilePage;
}
/**
* @see com.alkacon.opencms.formgenerator.CmsFormHandler#useInFormDataMacro(com.alkacon.opencms.formgenerator.I_CmsField)
*/
@Override
protected boolean useInFormDataMacro(I_CmsField field) {
boolean ret = super.useInFormDataMacro(field);
ret &= !(field instanceof CmsPasswordField);
return ret;
}
/**
* Creates the user.<p>
*
* @throws CmsException if something goes wrong
*/
private void createUser() throws CmsException {
// first create the user with the basics
CmsRegistrationForm form = getRegFormConfiguration();
CmsObject cms = CmsRegistrationModuleAction.getAdminCms();
String email = form.getFieldByDbLabel(FIELD_EMAIL).getValue();
String login = form.getOrgUnit();
if (form.isEmailAsLogin()) {
login += email;
} else {
login += form.getFieldByDbLabel(FIELD_LOGIN).getValue();
}
String password = form.getFieldByDbLabel(FIELD_PASSWORD).getValue();
// default description
String description = Messages.get().getBundle(getCmsObject().getRequestContext().getLocale()).key(
Messages.GUI_USER_DESCRIPTION_0);
CmsUser user = cms.createUser(login, password, description, null);
user.setEmail(email);
user.setEnabled(false);
cms.writeUser(user);
// add activation uri macro
String link = OpenCms.getSiteManager().getCurrentSite(getCmsObject()).getServerPrefix(
cms,
getRequestContext().getUri())
+ link(CmsRequestUtil.appendParameter(
getRequestContext().getUri(),
PARAM_ACTCODE,
CmsEncoder.encode(getActivationCode(user))));
m_macroResolver.addMacro(MACRO_ACTURI, "<a href=\"" + link + "\">" + link + "</a>");
// now add additional information
// iterate all fields except email, login and password
List<String> excludes = Arrays.asList(new String[] {FIELD_EMAIL, FIELD_PASSWORD, FIELD_LOGIN});
Iterator<I_CmsField> it = form.getAllFields().iterator();
while (it.hasNext()) {
I_CmsField field = it.next();
if (excludes.contains(field.getDbLabel())) {
continue;
}
setUserValue(user, field);
}
cms.writeUser(user);
// now assign the user to the given group only if needed
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(form.getGroup())) {
cms.addUserToGroup(user.getName(), form.getGroup());
}
}
/**
* Sets all fields to the current user.<p>
*
* @throws CmsException if something goes wrong
*/
private void editUser() throws CmsException {
CmsObject cms = CmsRegistrationModuleAction.getAdminCms();
CmsUser user = getRequestContext().currentUser();
CmsRegistrationForm form = getRegFormConfiguration();
Iterator<I_CmsField> it = form.getAllFields().iterator();
while (it.hasNext()) {
I_CmsField field = it.next();
setUserValue(user, field);
if (field.getDbLabel().equals(FIELD_PASSWORD)) {
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(field.getValue())) {
cms.setPassword(user.getName(), field.getValue());
}
}
}
cms.writeUser(user);
}
/**
* Checks if the current activation code identifies an existing user.<p>
*
* @return if the user is exists
*/
private CmsUser getUser() {
String code = getJspContext().getRequest().getParameter(PARAM_ACTCODE);
if (code == null) {
return getRequestContext().currentUser();
}
String userName = getUserName(code);
try {
return getCmsObject().readUser(userName);
} catch (Exception e) {
return null;
}
}
/**
* Returns the macro resolver for the activation and profile pages.<p>
*
* @return the macro resolver for the activation and profile pages
*/
private CmsMacroResolver getUserMacroResolver() {
CmsUser user = getUser();
CmsMacroResolver macroResolver = CmsMacroResolver.newInstance();
macroResolver.setKeepEmptyMacros(true);
// create macros for getters
Method[] methods = CmsUser.class.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method.getReturnType() != String.class) {
continue;
}
if (method.getParameterTypes().length > 0) {
continue;
}
if (!method.getName().startsWith("get")
|| (method.getName().length() < 4)
|| method.getName().equals("getPassword")) {
continue;
}
String label = ("" + method.getName().charAt(3)).toLowerCase();
if (method.getName().length() > 4) {
label += method.getName().substring(4);
}
Object value = null;
try {
value = method.invoke(user, new Object[] {});
} catch (Exception e) {
// ignore
}
if (value == null) {
value = "";
}
macroResolver.addMacro(label, value.toString());
}
// add addinfo values as macros
Iterator itFields = user.getAdditionalInfo().entrySet().iterator();
while (itFields.hasNext()) {
Map.Entry entry = (Map.Entry)itFields.next();
if ((entry.getValue() instanceof String) && (entry.getKey() instanceof String)) {
macroResolver.addMacro(entry.getKey().toString(), entry.getValue().toString());
}
}
// add login
macroResolver.addMacro(FIELD_LOGIN, user.getSimpleName());
return macroResolver;
}
/**
* Returns the value of the given field for the given user.<p>
*
* @param user the user
* @param label the field
*
* @return the value of the given field for the given user
*/
private String getUserValue(CmsUser user, String label) {
String methodName = REFLECTION_GETTER_PREFIX;
methodName += ("" + label.charAt(0)).toUpperCase();
if (label.length() > 1) {
methodName += label.substring(1);
}
Object value = null;
try {
// try to access the method
Method method = CmsUser.class.getMethod(methodName, new Class[] {});
value = method.invoke(user, new Object[] {});
} catch (Exception e) {
// get additional info
value = user.getAdditionalInfo(label);
}
return value == null ? "" : value.toString();
}
/**
* Sets the field value for the given user.<p>
*
* @param user the user to set the field for
* @param field the field to set
*/
private void setUserValue(CmsUser user, I_CmsField field) {
String methodName = REFLECTION_SETTER_PREFIX;
methodName += ("" + field.getDbLabel().charAt(0)).toUpperCase();
if (field.getDbLabel().length() > 1) {
methodName += field.getDbLabel().substring(1);
}
try {
// try to access the method
Method method = CmsUser.class.getMethod(methodName, new Class[] {String.class});
method.invoke(user, new String[] {field.getValue()});
} catch (Exception e) {
// set additional info
user.setAdditionalInfo(field.getDbLabel(), field.getValue());
}
}
}