org.ambraproject.wombat.controller.FeedbackController.java Source code

Java tutorial

Introduction

Here is the source code for org.ambraproject.wombat.controller.FeedbackController.java

Source

/*
 * Copyright (c) 2017 Public Library of Science
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

package org.ambraproject.wombat.controller;

import org.ambraproject.wombat.config.site.Site;
import org.ambraproject.wombat.config.site.SiteParam;
import org.ambraproject.wombat.model.EmailMessage;
import org.ambraproject.wombat.service.FreemarkerMailService;
import org.ambraproject.wombat.service.HoneypotService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.validator.routines.EmailValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfig;

import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Controller
public class FeedbackController extends WombatController {

    @Autowired
    private FreeMarkerConfig freeMarkerConfig;
    @Autowired
    private FreemarkerMailService freemarkerMailService;
    @Autowired
    private HoneypotService honeypotService;
    @Autowired
    private JavaMailSender javaMailSender; // TODO

    private static Map<String, Object> getFeedbackConfig(Site site) {
        return (Map<String, Object>) site.getTheme().getConfigMap("email").get("feedback");
    }

    private static void validateFeedbackConfig(Site site) {
        if (getFeedbackConfig(site).get("destination") == null) {
            throw new NotFoundException("Feedback is not configured with a destination address");
        }
    }

    @RequestMapping(name = "feedback", value = "/feedback", method = RequestMethod.GET)
    public String serveFeedbackPage(Model model, @SiteParam Site site) throws IOException {
        validateFeedbackConfig(site);
        return site + "/ftl/feedback/feedback";
    }

    @RequestMapping(name = "feedbackPost", value = "/feedback", method = RequestMethod.POST)
    public String receiveFeedback(HttpServletRequest request, HttpServletResponse response, Model model,
            @SiteParam Site site, @RequestParam("fromEmailAddress") String fromEmailAddress,
            @RequestParam("note") String note, @RequestParam("subject") String subject,
            @RequestParam("name") String name, @RequestParam("userId") String userId,
            @RequestParam(value = "consent", required = false) String consent,
            @RequestParam(value = "authorPhone", required = false) String authorPhone,
            @RequestParam(value = "authorAffiliation", required = false) String authorAffiliation)
            throws IOException, MessagingException {
        validateFeedbackConfig(site);

        // Fill input parameters into model. (These can be used in two ways: in the generated email if all input is valid,
        // or in the form in case we need to display validation errors.)
        model.addAttribute("fromEmailAddress", fromEmailAddress);
        model.addAttribute("note", note);
        model.addAttribute("name", name);
        model.addAttribute("subject", subject);

        Set<String> errors = validateInput(fromEmailAddress, note, subject, name, consent);
        if (applyValidation(response, model, errors)) {
            return serveFeedbackPage(model, site);
        }

        if (subject.isEmpty()) {
            model.addAttribute("subject", getFeedbackConfig(site).get("defaultSubject"));
        }

        model.addAttribute("id", userId);
        model.addAttribute("userInfo", formatUserInfo(request));

        if (honeypotService.checkHoneypot(request, authorPhone, authorAffiliation)) {
            return site + "/ftl/feedback/success";
        }

        Multipart content = freemarkerMailService.createContent(site, "feedback", model);

        String destinationAddress = (String) getFeedbackConfig(site).get("destination");
        EmailMessage message = EmailMessage.builder()
                .addToEmailAddress(EmailMessage.createAddress(null, destinationAddress))
                .setSenderAddress(EmailMessage.createAddress(name, fromEmailAddress)).setSubject(subject)
                .setContent(content).setEncoding(freeMarkerConfig.getConfiguration().getDefaultEncoding()).build();

        message.send(javaMailSender);
        return site + "/ftl/feedback/success";
    }

    /**
     * Validate form parameters.
     *
     * @return a set of error flags to be added to the FTL model (empty if all input is valid)
     */
    private static Set<String> validateInput(String fromEmailAddress, String note, String subject, String name,
            String consent) {
        Set<String> errors = new HashSet<>();
        if (StringUtils.isBlank(subject)) {
            errors.add("subjectError");
        }
        if (StringUtils.isBlank(name)) {
            errors.add("nameError");
        }
        if (StringUtils.isBlank(fromEmailAddress)) {
            errors.add("emailAddressMissingError");
        } else if (!EmailValidator.getInstance().isValid(fromEmailAddress)) {
            errors.add("emailAddressInvalidError");
        }
        if (StringUtils.isBlank(note)) {
            errors.add("noteError");
        }
        if (consent == null || !"true".equals(consent)) {
            errors.add("consentError");
        }
        return errors;
    }

    private static String formatUserInfo(HttpServletRequest request) {
        return getUserSessionAttributes(request).entrySet().stream()
                .map((Map.Entry<String, String> entry) -> (entry.getKey() + " ---> " + entry.getValue()))
                .collect(Collectors.joining("<br/>\n"));
    }

    private static Map<String, String> getUserSessionAttributes(HttpServletRequest request) {
        Map<String, String> headers = new LinkedHashMap<>();

        for (String headerName : Collections.list(request.getHeaderNames())) {
            List<String> headerValues = Collections.list(request.getHeaders(headerName));
            headers.put(headerName, headerValues.stream().collect(joinWithComma()));
        }

        headers.put("server-name", request.getServerName() + ":" + request.getServerPort());
        headers.put("remote-addr", request.getRemoteAddr());
        headers.put("local-addr", request.getLocalAddr() + ":" + request.getLocalPort());

        /*
         * Keeping this in case more values get passed from the client other than just the visible form
         * fields
         */
        for (String paramName : Collections.list(request.getParameterNames())) {
            String[] paramValues = request.getParameterValues(paramName);
            headers.put(paramName, Stream.of(paramValues).collect(joinWithComma()));
        }

        return headers;
    }

    private static Collector<CharSequence, ?, String> joinWithComma() {
        return Collectors.joining(",");
    }

}