/* This file is part of Cyclos (www.cyclos.org). A project of the Social Trade Organisation (www.socialtrade.org). Cyclos 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 2 of the License, or (at your option) any later version. Cyclos 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 Cyclos; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package nl.strohalm.cyclos.controls.members; import java.io.IOException; import java.util.Iterator; import java.util.List; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import nl.strohalm.cyclos.annotations.Inject; import nl.strohalm.cyclos.controls.BasePublicFormAction; import nl.strohalm.cyclos.controls.elements.CreateElementAction; import nl.strohalm.cyclos.entities.access.MemberUser; import nl.strohalm.cyclos.entities.access.User; import nl.strohalm.cyclos.entities.customization.fields.MemberCustomField; import nl.strohalm.cyclos.entities.customization.fields.MemberCustomField.Access; import nl.strohalm.cyclos.entities.customization.fields.MemberCustomFieldValue; import nl.strohalm.cyclos.entities.groups.MemberGroup; import nl.strohalm.cyclos.entities.members.Member; import nl.strohalm.cyclos.entities.members.RegisteredMember; import nl.strohalm.cyclos.entities.settings.AccessSettings; import nl.strohalm.cyclos.entities.settings.LocalSettings; import nl.strohalm.cyclos.entities.settings.events.LocalSettingsChangeListener; import nl.strohalm.cyclos.entities.settings.events.LocalSettingsEvent; import nl.strohalm.cyclos.exceptions.MailSendingException; import nl.strohalm.cyclos.services.customization.MemberCustomFieldService; import nl.strohalm.cyclos.services.elements.WhenSaving; import nl.strohalm.cyclos.services.elements.exceptions.UsernameAlreadyInUseException; import nl.strohalm.cyclos.servlets.CaptchaServlet; import nl.strohalm.cyclos.utils.ActionHelper; import nl.strohalm.cyclos.utils.CustomFieldHelper; import nl.strohalm.cyclos.utils.binding.DataBinder; import nl.strohalm.cyclos.utils.validation.PasswordsDontMatchError; import nl.strohalm.cyclos.utils.validation.ValidationError; import nl.strohalm.cyclos.utils.validation.ValidationException; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; /** * Action for a public member registration * @author luis */ public class PublicCreateMemberAction extends BasePublicFormAction implements LocalSettingsChangeListener { private MemberCustomFieldService memberCustomFieldService; private DataBinder<Member> dataBinder; private ReadWriteLock lock = new ReentrantReadWriteLock(); private CustomFieldHelper customFieldHelper; public DataBinder<Member> getDataBinder() { try { lock.readLock().lock(); if (dataBinder == null) { final LocalSettings localSettings = settingsService.getLocalSettings(); final AccessSettings accessSettings = settingsService.getAccessSettings(); dataBinder = CreateElementAction.getDataBinder(localSettings, accessSettings, Member.class, MemberUser.class, MemberGroup.class, MemberCustomField.class, MemberCustomFieldValue.class); } return dataBinder; } finally { lock.readLock().unlock(); } } public MemberCustomFieldService getMemberCustomFieldService() { return memberCustomFieldService; } @Override public void onLocalSettingsUpdate(final LocalSettingsEvent event) { try { lock.writeLock().lock(); dataBinder = null; } finally { lock.writeLock().unlock(); } } @Inject public void setCustomFieldHelper(final CustomFieldHelper customFieldHelper) { this.customFieldHelper = customFieldHelper; } @Inject public void setMemberCustomFieldService(final MemberCustomFieldService memberCustomFieldService) { this.memberCustomFieldService = memberCustomFieldService; } @Override protected ActionForward handleDisplay(final ActionMapping mapping, final ActionForm actionForm, final HttpServletRequest request, final HttpServletResponse response) throws Exception { // When there's a logged user, redirect to home if (request.getSession().getAttribute("loggedUser") != null) { try { response.sendRedirect("/" + request.getContextPath()); } catch (final IOException e) { // Ignore } return null; } return super.handleDisplay(mapping, actionForm, request, response); } @Override protected ActionForward handleSubmit(final ActionMapping mapping, final ActionForm actionForm, final HttpServletRequest request, final HttpServletResponse response) { final CreateMemberForm form = (CreateMemberForm) actionForm; final HttpSession session = request.getSession(); // Check the captcha challenge if (!CaptchaServlet.checkChallenge(request, form.getCaptcha())) { session.setAttribute("forceBack", true); throw new ValidationException("createMember.captcha.invalid"); } // Save the member final Member member = getDataBinder().readFromString(form.getMember()); RegisteredMember registeredMember; try { registeredMember = (RegisteredMember) elementService.register(member, false, request.getRemoteAddr()); } catch (final UsernameAlreadyInUseException e) { final ActionForward actionForward = ActionHelper.sendError(mapping, request, response, "createMember.public.alreadyExists"); session.setAttribute("forceBack", "forceBack"); return actionForward; } catch (final MailSendingException e) { return ActionHelper.sendError(mapping, request, response, "createMember.public.errorSendingMail"); } // We will send the flow to the error page not to showing an error, but the created message String message; if (registeredMember instanceof Member) { if (((Member) registeredMember).isActive()) { final User user = ((Member) registeredMember).getUser(); if (user.getPassword() != null && user.getPasswordDate() == null) { // Member is active and password is sent by mail message = "createMember.public.awaitingPassword"; } else { // Member is ready message = "createMember.public.validated"; } } else { // Member in inactive group. Message is awaiting activation by admin message = "createMember.public.awaitingActivation"; } } else { // Typed e-mail has to be validated message = "createMember.public.awaitingMailValidation"; } session.removeAttribute("forceBack"); ActionHelper.sendError(mapping, request, response, message, registeredMember.getUsername()); return mapping.findForward("confirmation"); } @Override protected void prepareForm(final ActionMapping mapping, final ActionForm actionForm, final HttpServletRequest request, final HttpServletResponse response) { final CreateMemberForm form = (CreateMemberForm) actionForm; MemberGroup group; try { group = groupService.load(form.getGroupId()); if (!group.isInitialGroup()) { throw new Exception(); } } catch (final Exception e) { throw new ValidationException(); } // Get the custom fields final List<MemberCustomField> customFields = customFieldHelper.onlyForGroup(memberCustomFieldService.list(), group); for (final Iterator<MemberCustomField> it = customFields.iterator(); it.hasNext();) { final MemberCustomField field = it.next(); boolean use = true; // Only use fields that are visible and editable by members final Access visibility = field.getVisibilityAccess(); if (visibility != null && !visibility.granted(group, true, false, true, false)) { use = false; } else { // Check if the field can be updated final Access update = field.getUpdateAccess(); if (update != null && !update.granted(group, true, false, true, false)) { use = false; } } // Remove the field from list if not used if (!use) { it.remove(); } } request.setAttribute("formAction", mapping.getPath()); request.setAttribute("customFields", customFields); request.setAttribute("isPublic", true); request.setAttribute("allowSetPassword", true); request.setAttribute("group", group); } @Override protected void validateForm(final ActionMapping mapping, final ActionForm actionForm, final HttpServletRequest request, final HttpServletResponse response) throws ValidationException { final CreateMemberForm form = (CreateMemberForm) actionForm; // Form validation final Member member = getDataBinder().readFromString(form.getMember()); ValidationException exc; try { elementService.validate(member, WhenSaving.PUBLIC, form.isManualPassword()); exc = new ValidationException(); } catch (final ValidationException e) { exc = e; } final String captcha = form.getCaptcha(); if (StringUtils.isEmpty(captcha) || !CaptchaServlet.checkChallenge(request, captcha)) { exc.addPropertyError("captcha", new ValidationError("createMember.captcha.invalid")); } String password; try { password = StringUtils.trimToNull(member.getUser().getPassword()); } catch (final Exception e) { password = null; } final String confirmPassword = StringUtils.trimToNull(form.getConfirmPassword()); if (password != null && (confirmPassword == null || !ObjectUtils.equals(confirmPassword, member.getUser().getPassword()))) { exc.addGeneralError(new PasswordsDontMatchError()); } exc.throwIfHasErrors(); } }