org.sakaiproject.feedback.tool.entityproviders.FeedbackEntityProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.feedback.tool.entityproviders.FeedbackEntityProvider.java

Source

/********************************************************************************** 
 * $URL$ 
 * $Id$ 
 *********************************************************************************** 
 * 
 * Copyright (c) 2011 The Sakai Foundation 
 * 
 * Licensed 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://www.osedu.org/licenses/ECL-2.0 
 * 
 * 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.sakaiproject.feedback.tool.entityproviders;

import java.sql.SQLException;
import java.text.DateFormat;
import java.util.*;

import org.apache.log4j.Logger;
import org.apache.commons.fileupload.FileItem;

import org.sakaiproject.entitybroker.EntityView;
import org.sakaiproject.entitybroker.entityprovider.annotations.EntityCustomAction;
import org.sakaiproject.entitybroker.entityprovider.capabilities.ActionsExecutable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.AutoRegisterEntityProvider;
import org.sakaiproject.entitybroker.entityprovider.capabilities.Describeable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.Outputable;
import org.sakaiproject.entitybroker.entityprovider.capabilities.RequestAware;
import org.sakaiproject.entitybroker.entityprovider.extension.Formats;
import org.sakaiproject.entitybroker.entityprovider.extension.RequestGetter;
import org.sakaiproject.entitybroker.util.AbstractEntityProvider;
import org.sakaiproject.feedback.db.Database;
import org.sakaiproject.feedback.exception.AttachmentsTooBigException;
import org.sakaiproject.feedback.tool.FeedbackTool;
import org.sakaiproject.feedback.util.Constants;
import org.sakaiproject.feedback.util.SakaiProxy;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.user.api.User;
import org.sakaiproject.util.RequestFilter;

import net.tanesha.recaptcha.ReCaptcha;
import net.tanesha.recaptcha.ReCaptchaFactory;
import net.tanesha.recaptcha.ReCaptchaResponse;

import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

public class FeedbackEntityProvider extends AbstractEntityProvider
        implements AutoRegisterEntityProvider, Outputable, Describeable, ActionsExecutable, RequestAware {

    public final static String ENTITY_PREFIX = "feedback";

    // Error codes start
    private final static String ATTACHMENTS_TOO_BIG = "ATTACHMENTS_TOO_BIG";
    private final static String BAD_DESCRIPTION = "BAD_DESCRIPTION";
    private final static String BAD_RECIPIENT = "BAD_RECIPIENT";
    private final static String BADLY_FORMED_RECIPIENT = "BADLY_FORMED_RECIPIENT";
    private final static String BAD_REQUEST = "BAD_REQUEST";
    private final static String BAD_TITLE = "BAD_TITLE";
    private final static String ERROR = "ERROR";
    private final static String NO_SENDER_ADDRESS = "NO_SENDER_ADDRESS";
    private final static String RECAPTCHA_FAILURE = "RECAPTCHA_FAILURE";
    private final static String SUCCESS = "SUCCESS";
    // Error codes end

    private final Logger logger = Logger.getLogger(getClass());

    private SakaiProxy sakaiProxy = null;

    public void setSakaiProxy(SakaiProxy sakaiProxy) {
        this.sakaiProxy = sakaiProxy;
    }

    private Database db = null;

    public void setDb(Database db) {
        this.db = db;
    }

    private RequestGetter requestGetter = null;

    private long maxAttachmentsBytes = 0L;

    public void init() {

        maxAttachmentsBytes = sakaiProxy.getAttachmentLimit() * 1024 * 1024;

        if (logger.isDebugEnabled()) {
            logger.debug("maxAttachmentsBytes = " + maxAttachmentsBytes);
        }
    }

    public Object getSampleEntity() {
        return null;
    }

    public String getEntityPrefix() {
        return ENTITY_PREFIX;
    }

    public String[] getHandledOutputFormats() {
        return new String[] { Formats.JSON, Formats.HTML };
    }

    public void setRequestGetter(RequestGetter rg) {
        this.requestGetter = rg;
    }

    @EntityCustomAction(action = "reportcontent", viewKey = EntityView.VIEW_EDIT)
    public String handleContentReport(EntityView view, Map<String, Object> params) {
        return handleReport(view, params, Constants.CONTENT);
    }

    @EntityCustomAction(action = "reporttechnical", viewKey = EntityView.VIEW_EDIT)
    public String handleTechnicalReport(EntityView view, Map<String, Object> params) {
        return handleReport(view, params, Constants.TECHNICAL);
    }

    @EntityCustomAction(action = "reporthelpdesk", viewKey = EntityView.VIEW_EDIT)
    public String handleHelpReport(EntityView view, Map<String, Object> params) {
        return handleReport(view, params, Constants.HELPDESK);
    }

    private String handleReport(final EntityView view, final Map<String, Object> params, final String type) {

        final String userId = developerHelperService.getCurrentUserId();

        if (view.getPathSegments().length != 3) {
            return BAD_REQUEST;
        }

        // Because the Feedback Tool EntityProvider parses URLs using forward slashes
        // (see /direct/feedback/describe) we replace forward slashes with a constant
        // and substitute them back here
        // TODO Doesn't this have a possible NPE?
        final String siteId = view.getPathSegment(1).replaceAll(FeedbackTool.FORWARD_SLASH, "/");

        if (logger.isDebugEnabled())
            logger.debug("Site ID: " + siteId);

        final String title = (String) params.get("title");
        final String description = (String) params.get("description");
        final boolean siteExists = new Boolean((String) params.get("siteExists"));

        final String browserNameAndVersion = requestGetter.getRequest().getHeader("User-Agent");
        final String osNameAndVersion = (String) params.get("oscpu");
        final String windowHeight = (String) params.get("windowHeight");
        final String windowWidth = (String) params.get("windowWidth");
        final String browserSize = windowWidth + " x " + windowHeight + " pixels";
        final String screenHeight = (String) params.get("screenHeight");
        final String screenWidth = (String) params.get("screenWidth");
        final String screenSize = screenWidth + " x " + screenHeight + " pixels";
        final String plugins = (String) params.get("plugins");
        final String ip = requestGetter.getRequest().getRemoteAddr();
        int fmt = DateFormat.MEDIUM;
        Locale locale = sakaiProxy.getLocale();
        DateFormat format = DateFormat.getDateTimeInstance(fmt, fmt, locale);
        final String currentTime = format.format(new Date());

        if (title == null || title.isEmpty()) {
            logger.debug("Subject incorrect. Returning " + BAD_TITLE + " ...");
            return BAD_TITLE;
        }

        if (description == null || description.isEmpty()) {
            logger.debug("No summary. Returning " + BAD_DESCRIPTION + " ...");
            return BAD_DESCRIPTION;
        }

        if (logger.isDebugEnabled())
            logger.debug("title: " + title + ". description: " + description);

        String toAddress = null;

        boolean addNoContactMessage = false;

        // The senderAddress can be either picked up from the current user's
        // account, or manually entered by the user submitting the report.
        String senderAddress = null;

        if (userId != null) {
            senderAddress = sakaiProxy.getUser(userId).getEmail();

            String alternativeRecipientId = (String) params.get("alternativerecipient");

            if (alternativeRecipientId != null && alternativeRecipientId.length() > 0) {
                User alternativeRecipientUser = sakaiProxy.getUser(alternativeRecipientId);

                if (alternativeRecipientUser != null) {
                    toAddress = alternativeRecipientUser.getEmail();
                    addNoContactMessage = true;
                } else {
                    try {
                        //validate site contact email address
                        InternetAddress emailAddr = new InternetAddress(alternativeRecipientId);
                        emailAddr.validate();
                        toAddress = alternativeRecipientId;
                    } catch (AddressException ex) {
                        logger.error(
                                "Incorrectly formed site contact email address. Returning BADLY_FORMED_RECIPIENT...");
                        return BADLY_FORMED_RECIPIENT;
                    }
                }
            } else {
                toAddress = getToAddress(type, siteId);
            }
        } else {
            // Recaptcha
            if (sakaiProxy.getConfigBoolean("user.recaptcha.enabled", false)) {
                String publicKey = sakaiProxy.getConfigString("user.recaptcha.public-key", "");
                String privateKey = sakaiProxy.getConfigString("user.recaptcha.private-key", "");
                ReCaptcha captcha = ReCaptchaFactory.newReCaptcha(publicKey, privateKey, false);
                String challengeField = (String) params.get("recaptcha_challenge_field");
                String responseField = (String) params.get("recaptcha_response_field");
                if (challengeField == null)
                    challengeField = "";
                if (responseField == null)
                    responseField = "";
                String remoteAddress = requestGetter.getRequest().getRemoteAddr();
                ReCaptchaResponse response = captcha.checkAnswer(remoteAddress, challengeField, responseField);
                if (!response.isValid()) {
                    logger.warn("Recaptcha failed with this message: " + response.getErrorMessage());
                    return RECAPTCHA_FAILURE;
                }
            }

            senderAddress = (String) params.get("senderaddress");
            if (senderAddress == null || senderAddress.length() == 0) {
                logger.error("No sender email address for non logged in user. Returning BAD REQUEST ...");
                return BAD_REQUEST;
            }

            toAddress = getToAddress(type, siteId);
        }

        if (toAddress == null || toAddress.isEmpty()) {
            logger.error("No recipient. Returning BAD REQUEST ...");
            return BAD_REQUEST;
        }

        if (senderAddress != null && senderAddress.length() > 0) {
            List<FileItem> attachments = null;

            try {
                attachments = getAttachments(params);
                sakaiProxy.sendEmail(userId, senderAddress, toAddress, addNoContactMessage, siteId, type, title,
                        description, attachments, siteExists, browserNameAndVersion, osNameAndVersion, browserSize,
                        screenSize, plugins, ip, currentTime);
                db.logReport(userId, senderAddress, siteId, type, title, description);
                return SUCCESS;
            } catch (AttachmentsTooBigException atbe) {
                logger.error("The total size of the attachments exceeded the permitted limit of "
                        + maxAttachmentsBytes + ". '" + ATTACHMENTS_TOO_BIG + "' will be returned to the client.");
                return ATTACHMENTS_TOO_BIG;
            } catch (SQLException sqlException) {
                logger.error("Caught exception while generating report. '" + Database.DB_ERROR
                        + "' will be returned to the client.", sqlException);
                return Database.DB_ERROR;
            } catch (Exception e) {
                logger.error("Caught exception while sending email or generating report. '" + ERROR
                        + "' will be returned to the client.", e);
                return ERROR;
            }
        } else {
            logger.error("Failed to determine a sender address No email or report will be generated. '"
                    + NO_SENDER_ADDRESS + "' will be returned to the client.");
            return NO_SENDER_ADDRESS;
        }
    }

    private String getToAddress(String type, String siteId) {
        String toAddress;
        if (Constants.CONTENT.equals(type)) {
            toAddress = sakaiProxy.getSiteProperty(siteId, Site.PROP_SITE_CONTACT_EMAIL);
            if (toAddress == null) {
                toAddress = sakaiProxy.getConfigString(Constants.PROP_TECHNICAL_ADDRESS, null);
            }
        } else {
            toAddress = sakaiProxy.getConfigString(Constants.PROP_TECHNICAL_ADDRESS, null);
        }
        return toAddress;
    }

    private List<FileItem> getAttachments(final Map<String, Object> params) throws Exception {

        final List<FileItem> fileItems = new ArrayList<FileItem>();

        final String uploadsDone = (String) params.get(RequestFilter.ATTR_UPLOADS_DONE);

        if (uploadsDone != null && uploadsDone.equals(RequestFilter.ATTR_UPLOADS_DONE)) {
            logger.debug("UPLOAD STATUS: " + params.get("upload.status"));

            FileItem attachment1 = (FileItem) params.get("attachment_0");
            if (attachment1 != null && attachment1.getSize() > 0) {
                fileItems.add(attachment1);
            }
            FileItem attachment2 = (FileItem) params.get("attachment_1");
            if (attachment2 != null && attachment2.getSize() > 0) {
                fileItems.add(attachment2);
            }
            FileItem attachment3 = (FileItem) params.get("attachment_2");
            if (attachment3 != null && attachment3.getSize() > 0) {
                fileItems.add(attachment3);
            }
            FileItem attachment4 = (FileItem) params.get("attachment_3");
            if (attachment4 != null && attachment4.getSize() > 0) {
                fileItems.add(attachment4);
            }
            FileItem attachment5 = (FileItem) params.get("attachment_4");
            if (attachment5 != null && attachment5.getSize() > 0) {
                fileItems.add(attachment5);
            }
        }

        long totalSize = 0L;

        for (FileItem fileItem : fileItems) {
            totalSize += fileItem.getSize();
        }

        if (totalSize > maxAttachmentsBytes) {
            throw new AttachmentsTooBigException();
        }

        return fileItems;
    }
}