org.opencastproject.adminui.endpoint.EmailEndpoint.java Source code

Java tutorial

Introduction

Here is the source code for org.opencastproject.adminui.endpoint.EmailEndpoint.java

Source

/**
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License
 * at:
 *
 *   http://opensource.org/licenses/ecl2.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package org.opencastproject.adminui.endpoint;

import static org.opencastproject.index.service.util.RestUtils.okJsonList;
import static org.opencastproject.security.api.SecurityConstants.GLOBAL_ADMIN_ROLE;
import static org.opencastproject.util.RestUtil.splitCommaSeparatedParam;
import static org.opencastproject.util.RestUtil.R.badRequest;
import static org.opencastproject.util.data.Collections.nil;
import static org.opencastproject.util.data.Option.option;

import org.opencastproject.adminui.util.ParticipationUtils;
import org.opencastproject.comments.Comment;
import org.opencastproject.kernel.mail.EmailAddress;
import org.opencastproject.messages.EmailConfiguration;
import org.opencastproject.messages.Mail;
import org.opencastproject.messages.MailService;
import org.opencastproject.messages.MessageSignature;
import org.opencastproject.messages.MessageTemplate;
import org.opencastproject.messages.TemplateMessageQuery;
import org.opencastproject.messages.TemplateType;
import org.opencastproject.messages.TemplateType.Type;
import org.opencastproject.messages.persistence.MailServiceException;
import org.opencastproject.pm.api.CaptureAgent;
import org.opencastproject.pm.api.Course;
import org.opencastproject.pm.api.EmailSender;
import org.opencastproject.pm.api.Message;
import org.opencastproject.pm.api.Person;
import org.opencastproject.pm.api.PersonType;
import org.opencastproject.pm.api.Recording;
import org.opencastproject.pm.api.Room;
import org.opencastproject.pm.api.persistence.ParticipationManagementDatabase;
import org.opencastproject.pm.api.persistence.ParticipationManagementDatabaseException;
import org.opencastproject.rest.BulkOperationResult;
import org.opencastproject.scheduler.api.SchedulerService;
import org.opencastproject.scheduler.api.SchedulerService.ReviewStatus;
import org.opencastproject.security.api.SecurityService;
import org.opencastproject.security.api.UnauthorizedException;
import org.opencastproject.security.api.User;
import org.opencastproject.util.Jsons;
import org.opencastproject.util.Jsons.Obj;
import org.opencastproject.util.Jsons.Val;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.SmartIterator;
import org.opencastproject.util.data.Function;
import org.opencastproject.util.data.Monadics;
import org.opencastproject.util.data.Option;
import org.opencastproject.util.doc.rest.RestParameter;
import org.opencastproject.util.doc.rest.RestQuery;
import org.opencastproject.util.doc.rest.RestResponse;
import org.opencastproject.util.doc.rest.RestService;

import com.entwinemedia.fn.data.json.JValue;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

@Path("/")
@RestService(name = "emailservice", title = "Email Service", notes = "", abstractText = "Provides email operations")
public class EmailEndpoint {
    /** The course name to use for the demo template. */
    private static final String DEFAULT_DEMO_COURSE_NAME = "Demo Course Name";
    /** The course description to use for the demo template. */
    private static final String DEFAULT_DEMO_COURSE_DESCRIPTION = "Demo Course Description";
    /** The default number of demo recordings to show in a demo template. */
    private static final int DEMO_RECORDING_NUMBER = 5;

    /** The key for the name of the template variable. */
    private static final String NAME_KEY = "NAME";
    /** The key for the template variable's description in English. */
    private static final String DESCRIPTION_EN_KEY = "DESCRIPTION.EN";
    /** The key for the template variable. */
    private static final String VARIABLE_KEY = "VARIABLE";
    /** Keys for the template variables. */
    /** The template variable to put the module / course description into an email. */
    private static final String MODULE_DESCRIPTION_KEY = "MODULE.DESCRIPTION";
    /** The template variable to put the module / course name into an email. */
    private static final String MODULE_NAME_KEY = "MODULE.NAME";
    /** The template variable to put the link to opt out of recording into an email. */
    private static final String OPT_OUT_LINK_KEY = "OPT.OUT.LINK";
    /** The template variable to put the staff's name into an email. */
    private static final String STAFF_NAME_KEY = "STAFF.NAME";

    /** Logging utility */
    private static final Logger logger = LoggerFactory.getLogger(EmailEndpoint.class);

    /** The participation management email sender */
    private EmailSender emailSender;

    /** The mail service */
    private MailService mailService;

    /** The security service */
    private SecurityService securityService;

    /** The participation management database */
    private ParticipationManagementDatabase participationDatabase;

    /** The scheduler service */
    private SchedulerService schedulerService;

    /** A parser for handling JSON documents inside the body of a request. **/
    private JSONParser parser = new JSONParser();

    /** OSGi callback for the mail service. */
    public void setMailService(MailService mailService) {
        this.mailService = mailService;
    }

    /** OSGi callback for the security service. */
    public void setSecurityService(SecurityService securityService) {
        this.securityService = securityService;
    }

    /** OSGi callback for the participation database. */
    public void setParticipationDatabase(ParticipationManagementDatabase participationDatabase) {
        this.participationDatabase = participationDatabase;
    }

    /** OSGi callback for the participation email sender. */
    public void setEmailSender(EmailSender emailSender) {
        this.emailSender = emailSender;
    }

    /** OSGi callback for the scheduler service. */
    public void setSchedulerService(SchedulerService schedulerService) {
        this.schedulerService = schedulerService;
    }

    protected void activate(ComponentContext cc) {
        logger.info("Activated email service endpoint");
    }

    @SuppressWarnings("unchecked")
    @GET
    @Path("variables.json")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "getEmailTemplateVariables", description = "Returns a list of email template variables as JSON", returnDescription = "The email template variables as a JSON list.", restParameters = {}, reponses = {
            @RestResponse(description = "Returns the email template variables list as JSON", responseCode = HttpServletResponse.SC_OK) })
    public Response getEmailTemplateVariables() throws UnauthorizedException {
        JSONArray array = new JSONArray();
        JSONObject optout = new JSONObject();
        optout.put(NAME_KEY, OPT_OUT_LINK_KEY);
        optout.put(VARIABLE_KEY, "${optOutLink}");
        optout.put(DESCRIPTION_EN_KEY,
                "Inserts a link for instructors to opt out of having their lectures recorded.");
        array.add(optout);

        JSONObject staff = new JSONObject();
        staff.put(NAME_KEY, STAFF_NAME_KEY);
        staff.put(VARIABLE_KEY, "${staff}");
        staff.put(DESCRIPTION_EN_KEY, "Inserts the staff member's name.");
        array.add(staff);

        JSONObject moduleName = new JSONObject();
        moduleName.put(NAME_KEY, MODULE_NAME_KEY);
        moduleName.put(VARIABLE_KEY, "<#list modules as module>${module.name}</#list>");
        moduleName.put(DESCRIPTION_EN_KEY,
                "Inserts the list of module names the staff member is involved in into the email.");
        array.add(moduleName);

        JSONObject moduleDescription = new JSONObject();
        moduleDescription.put(NAME_KEY, MODULE_DESCRIPTION_KEY);
        moduleDescription.put(VARIABLE_KEY, "<#list modules as module>${module.description}</#list>");
        moduleDescription.put(DESCRIPTION_EN_KEY, "Inserts the list of module descriptions into the email.");
        array.add(moduleDescription);

        return Response.ok(array.toJSONString()).build();
    }

    @GET
    @Path("templates.json")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "getemailtemplates", description = "Returns a list of email templates as JSON", returnDescription = "The email template list as JSON", restParameters = {
            @RestParameter(name = "onlymine", description = "Whether to return only my email templates", isRequired = false, defaultValue = "true", type = RestParameter.Type.BOOLEAN) }, reponses = {
                    @RestResponse(description = "Returns the email template list as JSON", responseCode = HttpServletResponse.SC_OK),
                    @RestResponse(description = "Not authorized to get all email templates!", responseCode = HttpServletResponse.SC_UNAUTHORIZED) })
    public Response getEmailTemplates(@QueryParam("limit") int limit, @QueryParam("offset") int offset,
            @QueryParam("onlymine") @DefaultValue("true") boolean onlymine) throws UnauthorizedException {
        TemplateMessageQuery messageQuery = new TemplateMessageQuery();

        if (onlymine) {
            messageQuery.withCreator(securityService.getUser().getUsername());
        } else {
            // Check for the global and local admin role
            if (!(securityService.getUser().hasRole(GLOBAL_ADMIN_ROLE)
                    || securityService.getUser().hasRole(securityService.getOrganization().getAdminRole())))
                throw new UnauthorizedException("Not authorized to get all email tempaltes!");
        }

        try {
            List<MessageTemplate> messageTemplates = mailService.findMessageTemplates(messageQuery);
            int total = messageTemplates.size();

            // Apply Limit and offset
            messageTemplates = new SmartIterator<MessageTemplate>(limit, offset)
                    .applyLimitAndOffset(messageTemplates);

            List<JValue> templateJSON = new ArrayList<JValue>();
            for (MessageTemplate template : messageTemplates) {
                templateJSON.add(template.toJValue());
            }
            return okJsonList(templateJSON, offset, limit, total);
        } catch (Exception e) {
            logger.error("Could not retrieve the email templates: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @GET
    @Path("template/{templateId}")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "getemailtemplate", description = "Returns the email template by the given id as JSON", returnDescription = "The email template as JSON", pathParameters = {
            @RestParameter(name = "templateId", description = "The template id", isRequired = true, type = RestParameter.Type.INTEGER) }, reponses = {
                    @RestResponse(description = "Returns the email template as JSON", responseCode = HttpServletResponse.SC_OK),
                    @RestResponse(description = "The template has not been found", responseCode = HttpServletResponse.SC_NOT_FOUND) })
    public Response getEmailTemplate(@PathParam("templateId") long templateId) throws NotFoundException {
        try {
            MessageTemplate messageTemplate = mailService.getMessageTemplate(templateId);
            return Response.ok(messageTemplate.toJson().toJson()).build();
        } catch (NotFoundException e) {
            throw e;
        } catch (Exception e) {
            logger.error("Could not retrieve the email template {}: {}", templateId,
                    ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @POST
    @Path("template")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "createemailtemplate", description = "Returns the created email template as JSON", returnDescription = "The created email template as JSON", restParameters = {
            @RestParameter(name = "name", description = "The template name", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "type", description = "The template type", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "subject", description = "The template subject", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "body", description = "The template body", isRequired = true, type = RestParameter.Type.TEXT) }, reponses = {
                    @RestResponse(description = "Returns the created email template as JSON", responseCode = HttpServletResponse.SC_OK),
                    @RestResponse(description = "Template type wrong", responseCode = HttpServletResponse.SC_BAD_REQUEST) })
    public Response createEmailTemplate(@FormParam("name") String name, @FormParam("type") String typeString,
            @FormParam("subject") String subject, @FormParam("body") String body) throws NotFoundException {
        Type type = TemplateType.INVITATION.getType();
        try {
            type = TemplateType.Type.valueOf(typeString);
        } catch (Exception e) {
            logger.warn("Unable to parse template type {}", typeString);
            return Response.status(Response.Status.BAD_REQUEST).build();
        }

        User currentUser = securityService.getUser();
        MessageTemplate template = new MessageTemplate(name, currentUser, subject, body, type.getType(), new Date(),
                nil(Comment.class));
        try {
            MessageTemplate messageTemplate = mailService.updateMessageTemplate(template);
            return Response.ok(messageTemplate.toJson().toJson()).build();
        } catch (Exception e) {
            logger.error("Could not create the email template: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * Creates a given number of recordings to display in a demo email template.
     *
     * @param numberOfRecordings
     *          The number of recordings to create.
     * @param currentPerson
     *          The current person to add as a staff person to these recordings.
     * @return A Collection of Recording objects to use in a demo template.
     */
    private Collection<Recording> getExampleRecordings(int numberOf, Person currentPerson) {
        List<PersonType> personTypes = new ArrayList<PersonType>();
        Collection<Recording> exampleRecordings = new ArrayList<Recording>();
        for (int i = 0; i < numberOf; i++) {
            ArrayList<Person> staff = new ArrayList<Person>();
            Course demoCourse = new Course("Demo-Course-" + i, DEFAULT_DEMO_COURSE_NAME + " " + i,
                    DEFAULT_DEMO_COURSE_DESCRIPTION + " " + i);
            // Necessary to get all of the demo courses rendered as it is looking for unique ids.
            demoCourse.setId(new Long(i));
            Room room = new Room("Example Room Name " + i);
            staff.add(new Person(null, currentPerson.getName(), currentPerson.getEmail(), personTypes));
            List<Person> participants = new ArrayList<Person>();
            CaptureAgent captureAgent = new CaptureAgent(room, "Demo Capture Agent " + i);
            Recording recording = Recording.recording(null, "Demo Recording " + i, staff, demoCourse, room,
                    new Date(), new Date(), new Date(new Date().getTime() + 10000), participants, captureAgent);
            exampleRecordings.add(recording);
        }
        return exampleRecordings;
    }

    /**
     * Create a JSON Obj with all of the variables substituted with example data such as course names, descriptions etc.
     *
     * @param currentUser
     *          The current user to pull their data such as name and email.
     * @param subject
     *          The subject for the email.
     * @param body
     *          The content for the email template.
     * @return A JSON object with the subject and body with all of the variables substituted.
     */
    protected Obj getExampleBody(User currentUser, String subject, String body) {
        if (mailService == null) {
            return null;
        }
        List<PersonType> personTypes = new ArrayList<PersonType>();
        String currentName = currentUser.getName();
        if (currentName == null) {
            currentName = "Staff Name";
        }
        String currentEmail = currentUser.getEmail();
        if (currentEmail == null) {
            currentEmail = "staffemail@fake.com";
        }
        Person currentPerson = new Person(null, currentName, currentEmail, personTypes);
        Collection<Recording> exampleRecordings = getExampleRecordings(DEMO_RECORDING_NUMBER, currentPerson);
        MessageTemplate msgTemplate = new MessageTemplate("Demo Template", currentUser, subject, body);
        EmailAddress sender = new EmailAddress("sender@fake.com", "Example Sender");
        EmailAddress replyTo = new EmailAddress("reply@fake.com", "Example Reply");
        List<Comment> comments = new ArrayList<Comment>();
        MessageSignature signature = new MessageSignature(null, "Example Signature", currentUser, sender,
                Option.some(replyTo), "Example Signature Text", new Date(), comments);
        Message message = new Message(currentPerson, msgTemplate, signature);
        String exampleBody = emailSender.renderInvitationBody(message, exampleRecordings, currentPerson);
        return Jsons.obj(Jsons.p("body", exampleBody), Jsons.p("subject", subject));
    }

    @POST
    @Path("demotemplate")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "getDemoEmailTemplate", description = "Returns an email template with variables filled in with demo data as JSON", pathParameters = {}, restParameters = {
            @RestParameter(name = "templateContent", description = "A JSON object containing the body and subject of an email template.", isRequired = true, type = RestParameter.Type.STRING) }, reponses = {
                    @RestResponse(description = "Returns the email template with demo data filled in, presented in JSON format.", responseCode = HttpServletResponse.SC_OK),
                    @RestResponse(description = "Badly formed json for the body or template. ", responseCode = HttpServletResponse.SC_BAD_REQUEST) }, returnDescription = "The example email template as JSON")
    public Response getDemoEmailTemplate(String templateContent) {
        if (StringUtils.isBlank(templateContent)) {
            return Response.status(Response.Status.BAD_REQUEST).build();
        }
        JSONObject decodedTemplate = null;
        try {
            decodedTemplate = (JSONObject) parser.parse(templateContent);
        } catch (ParseException e) {
            return Response.status(Response.Status.BAD_REQUEST).build();
        }
        String body = (String) decodedTemplate.get("body");
        String subject = (String) decodedTemplate.get("subject");

        Obj demoTemplate = getExampleBody(securityService.getUser(), subject, body);
        if (demoTemplate == null) {
            return Response.serverError().build();
        }
        return Response.ok(demoTemplate.toJson()).build();
    }

    @PUT
    @Path("template/{templateId}")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "updateemailtemplate", description = "Returns the updated email template by the given id as JSON", returnDescription = "The updated email template as JSON", pathParameters = {
            @RestParameter(name = "templateId", description = "The template id", isRequired = true, type = RestParameter.Type.INTEGER) }, restParameters = {
                    @RestParameter(name = "name", description = "The template name", isRequired = true, type = RestParameter.Type.STRING),
                    @RestParameter(name = "type", description = "The template type", isRequired = true, type = RestParameter.Type.STRING),
                    @RestParameter(name = "subject", description = "The template subject", isRequired = true, type = RestParameter.Type.STRING),
                    @RestParameter(name = "body", description = "The template body", isRequired = true, type = RestParameter.Type.STRING) }, reponses = {
                            @RestResponse(description = "Returns the email template as JSON", responseCode = HttpServletResponse.SC_OK),
                            @RestResponse(description = "Template type wrong", responseCode = HttpServletResponse.SC_BAD_REQUEST),
                            @RestResponse(description = "The template has not been found", responseCode = HttpServletResponse.SC_NOT_FOUND) })
    public Response updateEmailTemplate(@PathParam("templateId") long templateId, @FormParam("name") String name,
            @FormParam("type") String typeString, @FormParam("subject") String subject,
            @FormParam("body") String body) throws NotFoundException {
        Type type = TemplateType.INVITATION.getType();
        try {
            type = TemplateType.Type.valueOf(typeString);
        } catch (Exception e) {
            logger.warn("Unable to parse template type {}", typeString);
            return Response.status(Response.Status.BAD_REQUEST).build();
        }

        try {
            MessageTemplate template = mailService.getMessageTemplate(templateId);
            template.setName(name);
            template.setType(type.getType());
            template.setSubject(subject);
            template.setBody(body);

            MessageTemplate messageTemplate = mailService.updateMessageTemplate(template);
            return Response.ok(messageTemplate.toJson().toJson()).build();
        } catch (NotFoundException e) {
            throw e;
        } catch (Exception e) {
            logger.error("Could not update the email template {}: {}", templateId, ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @DELETE
    @Path("template/{templateId}")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "deleteemailtemplate", description = "Deletes the email template by the given id", returnDescription = "No content", pathParameters = {
            @RestParameter(name = "templateId", description = "The template id", isRequired = true, type = RestParameter.Type.INTEGER) }, reponses = {
                    @RestResponse(description = "Email template has been deleted", responseCode = HttpServletResponse.SC_NO_CONTENT),
                    @RestResponse(description = "The template has not been found", responseCode = HttpServletResponse.SC_NOT_FOUND) })
    public Response deleteEmailTemplate(@PathParam("templateId") long templateId) throws NotFoundException {
        try {
            mailService.deleteMessageTemplate(templateId);
            return Response.noContent().build();
        } catch (NotFoundException e) {
            throw e;
        } catch (Exception e) {
            logger.error("Could not delete the email template {}: {}", templateId, ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @POST
    @Path("deleteTemplates")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "deleteemailtemplates", description = "Deletes a json list of email templates by their given ids e.g. [8, 9, 10, 11]", returnDescription = "Ok with lists of successfully deleted templates, ones there were not found and ones that there were errors deleting.", reponses = {
            @RestResponse(description = "Email templates have been deleted or will be in the not found or error category.", responseCode = HttpServletResponse.SC_OK),
            @RestResponse(description = "The template has not been found", responseCode = HttpServletResponse.SC_NOT_FOUND) })
    public Response deleteEmailTemplate(String templateIdsContent) throws NotFoundException {
        if (StringUtils.isBlank(templateIdsContent)) {
            return Response.status(Response.Status.BAD_REQUEST).build();
        }

        JSONArray templateIdJsonArray;
        try {
            templateIdJsonArray = (JSONArray) parser.parse(templateIdsContent);
        } catch (ParseException e) {
            logger.error("Unable to parse '{}' because: {}", templateIdsContent, ExceptionUtils.getStackTrace(e));
            return Response.status(Response.Status.BAD_REQUEST).build();
        } catch (ClassCastException e) {
            logger.error("Unable to cast '{}' because: {}", templateIdsContent, ExceptionUtils.getStackTrace(e));
            return Response.status(Response.Status.BAD_REQUEST).build();
        }
        BulkOperationResult result = new BulkOperationResult();
        ArrayList<Long> templateIds = new ArrayList<Long>();
        for (Object templateIdObject : templateIdJsonArray) {
            try {
                Long currentId = Long.parseLong(templateIdObject.toString());
                mailService.getMessageTemplate(currentId);
                templateIds.add(currentId);
            } catch (NumberFormatException e) {
                logger.error("Unable to parse template id '{}' because: {}", templateIdObject,
                        ExceptionUtils.getStackTrace(e));
                result.addServerError(templateIdObject.toString());
            } catch (MailServiceException e) {
                logger.error("Unable to find template with id '{}' because: {}", templateIdObject,
                        ExceptionUtils.getStackTrace(e));
                result.addServerError(templateIdObject.toString());
            } catch (NotFoundException e) {
                logger.warn("Unable to find template with id '{}'", templateIdObject);
                result.addNotFound(templateIdObject.toString());
            }
        }

        for (Long templateId : templateIds) {
            try {
                mailService.deleteMessageTemplate(templateId);
                result.addOk(templateId);
            } catch (NotFoundException e) {
                logger.warn("Unable to delete template {}", templateId);
                result.addNotFound(templateId);
            } catch (Exception e) {
                logger.error("Could not delete the email template '{}': {}", templateId,
                        ExceptionUtils.getStackTrace(e));
                result.addServerError(templateId);
            }
        }
        return Response.ok(result.toJson()).build();
    }

    @GET
    @Path("signatures.json")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "getemailsignatures", description = "Returns a list of email signatures as JSON", returnDescription = "The email signature list as JSON", reponses = {
            @RestResponse(description = "Returns the email signature list as JSON", responseCode = HttpServletResponse.SC_OK) })
    public Response getEmailSignatures() {
        try {
            List<MessageSignature> messageSignatures = mailService.getMessageSignatures();

            List<Val> signatures = new ArrayList<Val>();
            for (MessageSignature signature : messageSignatures) {
                signatures.add(signature.toJson());
            }

            return Response.ok(Jsons.arr(signatures).toJson()).build();
        } catch (Exception e) {
            logger.error("Could not retrieve the email signatures: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @GET
    @Path("signature/{signatureId}")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "getemailsignature", description = "Returns the email signature by the given id as JSON", returnDescription = "The email signature as JSON", pathParameters = {
            @RestParameter(name = "signatureId", description = "The signature id", isRequired = true, type = RestParameter.Type.INTEGER) }, reponses = {
                    @RestResponse(description = "Returns the email signature as JSON", responseCode = HttpServletResponse.SC_OK),
                    @RestResponse(description = "The signature has not been found", responseCode = HttpServletResponse.SC_NOT_FOUND) })
    public Response getEmailSignature(@PathParam("signatureId") long signatureId) throws NotFoundException {
        try {
            MessageSignature messageSignature = mailService.getMessageSignature(signatureId);
            return Response.ok(messageSignature.toJson().toJson()).build();
        } catch (NotFoundException e) {
            throw e;
        } catch (Exception e) {
            logger.error("Could not retrieve the email signature {}: {}", signatureId,
                    ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @POST
    @Path("signature")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "createemailsignature", description = "Returns the created email signature as JSON", returnDescription = "The created email signature as JSON", restParameters = {
            @RestParameter(name = "name", description = "The signature name", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "from_name", description = "The sender's name", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "from_address", description = "The sender's address", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "reply_name", description = "The reply name", isRequired = false, type = RestParameter.Type.STRING),
            @RestParameter(name = "reply_address", description = "The reply address", isRequired = false, type = RestParameter.Type.STRING),
            @RestParameter(name = "text", description = "The signature text", isRequired = true, type = RestParameter.Type.STRING) }, reponses = {
                    @RestResponse(description = "Bad request if the user already has a signature.", responseCode = HttpServletResponse.SC_BAD_REQUEST),
                    @RestResponse(description = "Returns the created email template as JSON", responseCode = HttpServletResponse.SC_OK) })
    public Response createEmailSignature(@FormParam("name") String name, @FormParam("from_name") String emailName,
            @FormParam("from_address") String address, @FormParam("reply_name") String replyName,
            @FormParam("reply_address") String replyAddress, @FormParam("text") String text)
            throws NotFoundException {
        User currentUser = securityService.getUser();
        try {
            if (mailService.getSignatureTotalByUserName() > 0) {
                return badRequest("Unable to create another signature as a user can't have more than 1 signature");
            }
        } catch (Exception e) {
            logger.error("Couldn't get the number of signatures for a user: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }

        Option<EmailAddress> reply = Option.<EmailAddress>none();
        if (StringUtils.isNotBlank(replyAddress) && StringUtils.isNotBlank(replyName))
            reply = Option.some(EmailAddress.emailAddress(replyAddress, replyName));

        EmailAddress emailAddress = EmailAddress.emailAddress(address, emailName);
        MessageSignature messageSignature = new MessageSignature(null, name, currentUser, emailAddress, reply, text,
                new Date(), nil(Comment.class));
        try {
            MessageSignature updatedMessageSignature = mailService.updateMessageSignature(messageSignature);
            return Response.ok(updatedMessageSignature.toJson().toJson()).build();
        } catch (Exception e) {
            logger.error("Could not create the email signature: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @PUT
    @Path("signature/{signatureId}")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "updateemailsignature", description = "Returns the updated email signature by the given id as JSON", returnDescription = "The updated email signature as JSON", pathParameters = {
            @RestParameter(name = "signatureId", description = "The signature id", isRequired = true, type = RestParameter.Type.INTEGER) }, restParameters = {
                    @RestParameter(name = "name", description = "The signature name", isRequired = true, type = RestParameter.Type.STRING),
                    @RestParameter(name = "from_name", description = "The signature type", isRequired = true, type = RestParameter.Type.STRING),
                    @RestParameter(name = "from_address", description = "The sender's address", isRequired = true, type = RestParameter.Type.STRING),
                    @RestParameter(name = "reply_name", description = "The reply name", isRequired = false, type = RestParameter.Type.STRING),
                    @RestParameter(name = "reply_address", description = "The reply address", isRequired = false, type = RestParameter.Type.STRING),
                    @RestParameter(name = "text", description = "The signature text", isRequired = true, type = RestParameter.Type.STRING) }, reponses = {
                            @RestResponse(description = "Returns the email signature as JSON", responseCode = HttpServletResponse.SC_OK),
                            @RestResponse(description = "The signature has not been found", responseCode = HttpServletResponse.SC_NOT_FOUND) })
    public Response updateEmailSignature(@PathParam("signatureId") long signatureId, @FormParam("name") String name,
            @FormParam("from_name") String emailName, @FormParam("from_address") String address,
            @FormParam("reply_name") String replyName, @FormParam("reply_address") String replyAddress,
            @FormParam("text") String text) throws NotFoundException {

        Option<EmailAddress> reply = Option.<EmailAddress>none();
        if (StringUtils.isNotBlank(replyAddress) && StringUtils.isNotBlank(replyName))
            reply = Option.some(EmailAddress.emailAddress(replyAddress, replyName));

        try {
            MessageSignature messageSignature = mailService.getMessageSignature(signatureId);
            messageSignature.setName(name);
            messageSignature.setSender(EmailAddress.emailAddress(address, emailName));
            messageSignature.setReplyTo(reply);
            messageSignature.setSignature(text);

            MessageSignature updatedMessageSignature = mailService.updateMessageSignature(messageSignature);
            return Response.ok(updatedMessageSignature.toJson().toJson()).build();
        } catch (NotFoundException e) {
            throw e;
        } catch (Exception e) {
            logger.error("Could not update the email signature {}: {}", signatureId,
                    ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @DELETE
    @Path("signature/{signatureId}")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "deleteemailtemplate", description = "Deletes the email signature by the given id", returnDescription = "No content", pathParameters = {
            @RestParameter(name = "signatureId", description = "The signature id", isRequired = true, type = RestParameter.Type.INTEGER) }, reponses = {
                    @RestResponse(description = "Email signature has been deleted", responseCode = HttpServletResponse.SC_NO_CONTENT),
                    @RestResponse(description = "The email signature has not been found", responseCode = HttpServletResponse.SC_NOT_FOUND) })
    public Response deleteEmailSignature(@PathParam("signatureId") long signatureId) throws NotFoundException {
        try {
            mailService.deleteMessageSignature(signatureId);
            return Response.noContent().build();
        } catch (NotFoundException e) {
            throw e;
        } catch (Exception e) {
            logger.error("Could not delete the email signature {}: {}", signatureId,
                    ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @GET
    @Path("configuration")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "getemailconfiguration", description = "Returns the email configuration as JSON", returnDescription = "The email configuration as JSON", reponses = {
            @RestResponse(description = "Returns the email configuration as JSON", responseCode = HttpServletResponse.SC_OK) })
    public Response getEmailConfiguration() throws NotFoundException {
        try {
            EmailConfiguration emailConfiguration = mailService.getEmailConfiguration();
            return Response.ok(emailConfiguration.toJson().toJson()).build();
        } catch (Exception e) {
            logger.error("Could not retrieve the email configuration: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @PUT
    @Path("configuration")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "updateemailconfiguration", description = "Updates the email configuration", returnDescription = "The email configuration as JSON", restParameters = {
            @RestParameter(name = "transport", description = "The mail transport", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "server", description = "The server hostname", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "port", description = "The server port address", isRequired = true, type = RestParameter.Type.INTEGER),
            @RestParameter(name = "username", description = "The username", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "password", description = "The password", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "ssl", description = "Whether SSL is used", isRequired = true, type = RestParameter.Type.BOOLEAN) }, reponses = {
                    @RestResponse(description = "Returns the updated blacklist as JSON", responseCode = HttpServletResponse.SC_OK) })
    public Response updateEmailConfiguration(@FormParam("transport") String transport,
            @FormParam("server") String server, @FormParam("port") int port, @FormParam("username") String username,
            @FormParam("password") String password, @FormParam("ssl") boolean ssl) {
        try {
            EmailConfiguration emailConfiguration = new EmailConfiguration(transport, server, port, username,
                    password, ssl);
            mailService.updateEmailConfiguration(emailConfiguration);
            return Response.ok(emailConfiguration.toJson().toJson()).build();
        } catch (Exception e) {
            logger.error("Could not update the email configuration: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @POST
    @Path("preview/{templateId}")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "previewmail", description = "Render a mail with the current email configuration for reviewing it", returnDescription = "The renderend message as JSON", pathParameters = {
            @RestParameter(name = "templateId", description = "The template id", isRequired = true, type = RestParameter.Type.INTEGER) }, restParameters = {
                    @RestParameter(name = "eventIds", description = "A comma separated list of event ids", isRequired = true, type = RestParameter.Type.TEXT),
                    @RestParameter(name = "personIds", description = "A comma separated list of person ids", isRequired = true, type = RestParameter.Type.TEXT),
                    @RestParameter(name = "signature", description = "Whether to include the signature", isRequired = false, type = RestParameter.Type.BOOLEAN),
                    @RestParameter(name = "body", description = "The optional message body", isRequired = false, type = RestParameter.Type.TEXT) }, reponses = {
                            @RestResponse(description = "No recording or person ids have been set!", responseCode = HttpServletResponse.SC_BAD_REQUEST),
                            @RestResponse(description = "No message signature is configured or no PM user to perform this action!", responseCode = HttpServletResponse.SC_PRECONDITION_FAILED),
                            @RestResponse(description = "Email has been sent", responseCode = HttpServletResponse.SC_NO_CONTENT) })
    public Response previewMail(@PathParam("templateId") long template,
            @FormParam("eventIds") String eventIdsString, @FormParam("personIds") String personIdsString,
            @FormParam("signature") @DefaultValue("false") boolean signature, @FormParam("subject") String subject,
            @FormParam("body") String body) {
        if (participationDatabase == null)
            return Response.status(Status.SERVICE_UNAVAILABLE).build();

        final Monadics.ListMonadic<String> eventIds = splitCommaSeparatedParam(option(eventIdsString));
        final Monadics.ListMonadic<Long> personIds = splitCommaSeparatedParam(option(personIdsString)).map(toLong);
        if (eventIds.value().isEmpty() || personIds.value().isEmpty())
            return badRequest();

        Person creator;
        try {
            creator = participationDatabase.getPerson(securityService.getUser().getEmail());
        } catch (Exception e) {
            logger.error("No valid participation managment user found for preview this mail! {}",
                    ExceptionUtils.getStackTrace(e));
            return Response.status(Status.PRECONDITION_FAILED).build();
        }

        try {
            List<Recording> recordings = ParticipationUtils.getRecordingsByEventId(schedulerService,
                    participationDatabase, eventIds.value());
            List<Person> recipients = getPersonById(personIds.value());

            MessageTemplate messageTemplate = mailService.getMessageTemplate(template);
            if (StringUtils.isNotBlank(body))
                messageTemplate.setBody(body);

            MessageSignature messageSignature = null;

            if (signature) {
                try {
                    messageSignature = mailService.getCurrentUsersSignature();
                } catch (NotFoundException e) {
                    return Response.status(Status.PRECONDITION_FAILED).build();
                }
                if (messageSignature == null)
                    return Response.status(Status.PRECONDITION_FAILED).build();
            } else {
                // Create an empty message signature.
                messageSignature = new MessageSignature(0L, creator.getName(), securityService.getUser(),
                        new EmailAddress(creator.getEmail(), creator.getName()), Option.<EmailAddress>none(), "",
                        new Date(), (new ArrayList<Comment>()));
            }
            messageTemplate
                    .setBody(messageTemplate.getBody().concat("\r\n").concat(messageSignature.getSignature()));
            Message message = new Message(creator, messageTemplate, messageSignature, signature);

            List<Val> renderArr = new ArrayList<Jsons.Val>();
            for (Person recipient : recipients) {
                renderArr.add(Jsons.v(emailSender.renderInvitationBody(message, recordings, recipient)));
            }
            return Response.ok(Jsons.arr(renderArr).toJson()).build();
        } catch (Exception e) {
            logger.error("Could not create a preview email: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @POST
    @Path("send/{templateId}")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "sendmail", description = "Render an email with the current email configuration for reviewing it", returnDescription = "The renderend message as JSON", pathParameters = {
            @RestParameter(name = "templateId", description = "The template id", isRequired = true, type = RestParameter.Type.INTEGER) }, restParameters = {
                    @RestParameter(name = "eventIds", description = "A comma separated list of event ids", isRequired = true, type = RestParameter.Type.TEXT),
                    @RestParameter(name = "personIds", description = "A comma separated list of person ids", isRequired = true, type = RestParameter.Type.TEXT),
                    @RestParameter(name = "signature", description = "Whether to include the signature", isRequired = false, type = RestParameter.Type.BOOLEAN),
                    @RestParameter(name = "subject", description = "The optional message subject", isRequired = false, type = RestParameter.Type.STRING),
                    @RestParameter(name = "body", description = "The optional message body", isRequired = false, type = RestParameter.Type.TEXT),
                    @RestParameter(name = "store", description = "Whether to store the message to the audit trail", isRequired = false, type = RestParameter.Type.BOOLEAN) }, reponses = {
                            @RestResponse(description = "No recording or person ids have been set!", responseCode = HttpServletResponse.SC_BAD_REQUEST),
                            @RestResponse(description = "No message signature is configured or no PM user to perform this action!", responseCode = HttpServletResponse.SC_PRECONDITION_FAILED),
                            @RestResponse(description = "Email has been sent", responseCode = HttpServletResponse.SC_NO_CONTENT) })
    public Response sendMail(@PathParam("templateId") long template, @FormParam("eventIds") String eventIdsString,
            @FormParam("personIds") String personIdsString,
            @FormParam("signature") @DefaultValue("false") boolean signature, @FormParam("subject") String subject,
            @FormParam("body") String body, @FormParam("store") @DefaultValue("false") boolean store) {
        if (participationDatabase == null)
            return Response.status(Status.SERVICE_UNAVAILABLE).build();

        final Monadics.ListMonadic<String> eventIds = splitCommaSeparatedParam(option(eventIdsString));
        final Monadics.ListMonadic<Long> personIds = splitCommaSeparatedParam(option(personIdsString)).map(toLong);
        if (eventIds.value().isEmpty() || personIds.value().isEmpty())
            return badRequest();

        Person creator;
        try {
            creator = participationDatabase.getPerson(securityService.getUser().getEmail());
        } catch (Exception e) {
            logger.error("No valid participation management user found for sending this mail!");
            return Response.status(Status.PRECONDITION_FAILED).build();
        }

        try {
            List<Recording> recordings = ParticipationUtils.getRecordingsByEventId(schedulerService,
                    participationDatabase, eventIds.value());
            List<Person> recipients = getPersonById(personIds.value());

            MessageTemplate messageTemplate = mailService.getMessageTemplate(template);
            if (StringUtils.isNotBlank(subject))
                messageTemplate.setSubject(subject);
            if (StringUtils.isNotBlank(body))
                messageTemplate.setBody(body);

            MessageSignature messageSignature = mailService.getCurrentUsersSignature();
            if (messageSignature == null)
                return Response.status(Status.PRECONDITION_FAILED).build();

            Message message = new Message(creator, messageTemplate, messageSignature, signature);
            emailSender.sendMessagesForRecordings(recordings, recipients, message, store);

            for (String eventId : eventIds.value()) {
                try {
                    schedulerService.updateReviewStatus(eventId, ReviewStatus.UNCONFIRMED);
                } catch (Exception e) {
                    logger.error("Unable to update review status of event {}", eventId);
                    continue;
                }
            }
            return Response.noContent().build();
        } catch (Exception e) {
            logger.error("Could not update the email configuration: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    @POST
    @Path("sendtestmail")
    @Produces(MediaType.APPLICATION_JSON)
    @RestQuery(name = "sendtestmail", description = "Sends an test mail with the current email configuration", returnDescription = "The sent message as JSON", restParameters = {
            @RestParameter(name = "to", description = "A semi-colon separated list of recipient addresses", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "subject", description = "The message subject", isRequired = true, type = RestParameter.Type.STRING),
            @RestParameter(name = "body", description = "The message body", isRequired = true, type = RestParameter.Type.STRING) }, reponses = {
                    @RestResponse(description = "Email has been sent", responseCode = HttpServletResponse.SC_NO_CONTENT) })
    public Response sendTestMail(@FormParam("to") String to, @FormParam("subject") String subject,
            @FormParam("body") String bodyString) {
        try {
            User user = securityService.getUser();

            MessageTemplate template = new MessageTemplate("Test Mail", user, subject, bodyString,
                    TemplateType.INVITATION, new Date(), nil(Comment.class));
            MessageSignature messageSignature = MessageSignature.messageSignature("Test Signature", user,
                    EmailAddress.emailAddress(user.getEmail(), user.getName()), "This is a test mail signature");

            List<EmailAddress> addresses = new ArrayList<EmailAddress>();
            for (String address : StringUtils.split(to, ";")) {
                addresses.add(EmailAddress.emailAddress(address, null));
            }

            String body = template.getBody().concat("\r\n").concat(messageSignature.getSignature());
            final Mail mail = new Mail(messageSignature.getSender(), messageSignature.getReplyTo(), addresses,
                    template.getSubject(), body);
            mailService.send(mail);
            return Response.noContent().build();
        } catch (Exception e) {
            logger.error("Could not update the email configuration: {}", ExceptionUtils.getStackTrace(e));
            throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
        }
    }

    private List<Person> getPersonById(List<Long> ids) {
        List<Person> persons = new ArrayList<Person>();
        for (long personId : ids) {
            try {
                persons.add(participationDatabase.getPerson(personId));
            } catch (ParticipationManagementDatabaseException e) {
                logger.error("Unable to get person with id {}: {}", personId, ExceptionUtils.getStackTrace(e));
                throw new WebApplicationException(e);
            } catch (NotFoundException e) {
                logger.info("Didn't find any person with id {}", personId);
                continue;
            }
        }
        return persons;
    }

    private Function<String, Long> toLong = new Function<String, Long>() {
        @Override
        public Long apply(String a) {
            return Long.parseLong(a);
        }
    };

}