org.tdl.vireo.controller.SubmissionController.java Source code

Java tutorial

Introduction

Here is the source code for org.tdl.vireo.controller.SubmissionController.java

Source

package org.tdl.vireo.controller;

import static edu.tamu.weaver.response.ApiStatus.ERROR;
import static edu.tamu.weaver.response.ApiStatus.INVALID;
import static edu.tamu.weaver.response.ApiStatus.SUCCESS;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletResponse;

import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.mail.MailException;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
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.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.tdl.vireo.exception.DepositException;
import org.tdl.vireo.exception.OrganizationDoesNotAcceptSubmissionsExcception;
import org.tdl.vireo.model.CustomActionValue;
import org.tdl.vireo.model.DepositLocation;
import org.tdl.vireo.model.EmailTemplate;
import org.tdl.vireo.model.EmailWorkflowRule;
import org.tdl.vireo.model.FieldValue;
import org.tdl.vireo.model.InputType;
import org.tdl.vireo.model.NamedSearchFilterGroup;
import org.tdl.vireo.model.Role;
import org.tdl.vireo.model.Submission;
import org.tdl.vireo.model.SubmissionFieldProfile;
import org.tdl.vireo.model.SubmissionListColumn;
import org.tdl.vireo.model.SubmissionStatus;
import org.tdl.vireo.model.User;
import org.tdl.vireo.model.depositor.Depositor;
import org.tdl.vireo.model.export.ExportPackage;
import org.tdl.vireo.model.packager.AbstractPackager;
import org.tdl.vireo.model.repo.ActionLogRepo;
import org.tdl.vireo.model.repo.ConfigurationRepo;
import org.tdl.vireo.model.repo.CustomActionValueRepo;
import org.tdl.vireo.model.repo.DepositLocationRepo;
import org.tdl.vireo.model.repo.EmailTemplateRepo;
import org.tdl.vireo.model.repo.FieldValueRepo;
import org.tdl.vireo.model.repo.InputTypeRepo;
import org.tdl.vireo.model.repo.NamedSearchFilterGroupRepo;
import org.tdl.vireo.model.repo.OrganizationRepo;
import org.tdl.vireo.model.repo.SubmissionFieldProfileRepo;
import org.tdl.vireo.model.repo.SubmissionRepo;
import org.tdl.vireo.model.repo.SubmissionStatusRepo;
import org.tdl.vireo.model.repo.UserRepo;
import org.tdl.vireo.model.validation.FieldValueValidator;
import org.tdl.vireo.service.AssetService;
import org.tdl.vireo.service.DepositorService;
import org.tdl.vireo.utility.OrcidUtility;
import org.tdl.vireo.utility.PackagerUtility;
import org.tdl.vireo.utility.TemplateUtility;

import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import edu.tamu.weaver.auth.annotation.WeaverCredentials;
import edu.tamu.weaver.auth.annotation.WeaverUser;
import edu.tamu.weaver.auth.model.Credentials;
import edu.tamu.weaver.data.model.ApiPage;
import edu.tamu.weaver.email.service.EmailSender;
import edu.tamu.weaver.response.ApiResponse;
import edu.tamu.weaver.response.ApiView;
import edu.tamu.weaver.validation.results.ValidationResults;

@RestController
@RequestMapping("/submission")
public class SubmissionController {

    private Logger LOG = LoggerFactory.getLogger(this.getClass());

    private static final String STARTING_SUBMISSION_STATUS_NAME = "In Progress";

    private static final String NEEDS_CORRECTION_SUBMISSION_STATUS_NAME = "Needs Correction";

    private static final String CORRECTIONS_RECEIVED_SUBMISSION_STATUS_NAME = "Corrections Received";

    @Autowired
    private UserRepo userRepo;

    @Autowired
    private SubmissionRepo submissionRepo;

    @Autowired
    private FieldValueRepo fieldValueRepo;

    @Autowired
    private SubmissionFieldProfileRepo submissionFieldProfileRepo;

    @Autowired
    private NamedSearchFilterGroupRepo namedSearchFilterGroupRepo;

    @Autowired
    private OrganizationRepo organizationRepo;

    @Autowired
    private InputTypeRepo inputTypeRepo;

    @Autowired
    private SubmissionStatusRepo submissionStatusRepo;

    @Autowired
    private EmailSender emailSender;

    @Autowired
    private EmailTemplateRepo emailTemplateRepo;

    @Autowired
    private TemplateUtility templateUtility;

    @Autowired
    private AssetService assetService;

    @Autowired
    private ConfigurationRepo configurationRepo;

    @Autowired
    private ActionLogRepo actionLogRepo;

    @Autowired
    private DepositLocationRepo depositLocationRepo;

    @Autowired
    private DepositorService depositorService;

    @Autowired
    private PackagerUtility packagerUtility;

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private CustomActionValueRepo customActionValueRepo;

    @Value("${app.document.folder:private}")
    private String documentFolder;

    @Value("${app.documentType.rename:}")
    private String documentTypesToRename;

    @RequestMapping("/all")
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse getAll() {
        return new ApiResponse(SUCCESS, submissionRepo.findAll());
    }

    @RequestMapping("/all-by-user")
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse getAllByUser(@WeaverUser User user) {
        return new ApiResponse(SUCCESS, submissionRepo.findAllBySubmitter(user));
    }

    @RequestMapping("/get-one/{submissionId}")
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse getOne(@WeaverUser User user, @PathVariable Long submissionId) {
        Submission submission = null;
        if (user.getRole().ordinal() <= Role.ROLE_MANAGER.ordinal()) {
            submission = submissionRepo.read(submissionId);
        } else {
            submission = submissionRepo.findOneBySubmitterAndId(user, submissionId);
        }
        if (submission == null) {
            return new ApiResponse(ERROR, "Submission not found");
        }
        return new ApiResponse(SUCCESS, submission);
    }

    @RequestMapping("/advisor-review/{submissionHash}")
    public ApiResponse getOne(@PathVariable String submissionHash) {
        Submission submission = submissionRepo.findOneByAdvisorAccessHash(submissionHash);
        return new ApiResponse(SUCCESS, submission);
    }

    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse createSubmission(@WeaverUser User user, @WeaverCredentials Credentials credentials,
            @RequestBody Map<String, String> data) throws OrganizationDoesNotAcceptSubmissionsExcception {
        Submission submission = submissionRepo.create(user,
                organizationRepo.read(Long.valueOf(data.get("organizationId"))),
                submissionStatusRepo.findByName(STARTING_SUBMISSION_STATUS_NAME), credentials);
        actionLogRepo.createPublicLog(submission, user, "Submission created.");
        return new ApiResponse(SUCCESS, submission);
    }

    @RequestMapping("/delete/{submissionId}")
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse deleteSubmission(@WeaverUser User user, @PathVariable Long submissionId) {
        Submission submissionToDelete = submissionRepo.read(submissionId);

        ApiResponse response = new ApiResponse(SUCCESS);
        if (submissionToDelete.getSubmitter().getEmail().equals(user.getEmail())
                || user.getRole().ordinal() <= Role.ROLE_MANAGER.ordinal()) {
            submissionRepo.delete(submissionToDelete);
        } else {
            response = new ApiResponse(ERROR, "Insufficient permisions to delete this submission.");
        }

        return response;
    }

    @RequestMapping(value = "/{submissionId}/add-comment", method = RequestMethod.POST)
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse addComment(@WeaverUser User user, @PathVariable Long submissionId,
            @RequestBody Map<String, Object> data) {

        Submission submission = submissionRepo.read(submissionId);

        String commentVisibility = data.get("commentVisiblity") != null ? (String) data.get("commentVisiblity")
                : "public";

        if (commentVisibility.equals("public")) {
            sendEmail(user, submission, data);
        } else {
            String subject = (String) data.get("subject");
            String templatedMessage = templateUtility.compileString((String) data.get("message"), submission);
            actionLogRepo.createPrivateLog(submission, user, subject + ": " + templatedMessage);
        }

        return new ApiResponse(SUCCESS);
    }

    @RequestMapping(value = "/{submissionId}/send-email", method = RequestMethod.POST)
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse sendEmail(@WeaverUser User user, @PathVariable Long submissionId,
            @RequestBody Map<String, Object> data) {
        sendEmail(user, submissionRepo.read(submissionId), data);
        return new ApiResponse(SUCCESS);
    }

    private void sendEmail(User user, Submission submission, Map<String, Object> data) {

        String subject = (String) data.get("subject");

        String templatedMessage = templateUtility.compileString((String) data.get("message"), submission);

        String recipientEmails = new String();

        boolean sendRecipientEmail = (boolean) data.get("sendEmailToRecipient");

        if (sendRecipientEmail) {
            boolean sendCCRecipientEmail = (boolean) data.get("sendEmailToCCRecipient");

            SimpleMailMessage smm = new SimpleMailMessage();

            String recipientEmail = (String) data.get("recipientEmail");

            recipientEmails = "Email sent to: [ " + recipientEmail + " ]; ";

            smm.setTo(recipientEmail.split(";"));

            if (sendCCRecipientEmail) {
                String ccRecipientEmail = (String) data.get("ccRecipientEmail");
                smm.setCc(ccRecipientEmail.split(";"));
                recipientEmails = "Email sent to: [ " + recipientEmail + " ] " + " and cc to: [ " + ccRecipientEmail
                        + " ]; ";
            }

            String preferredEmail = user.getSetting("preferedEmail");

            if (user.getSetting("ccEmail") != null && user.getSetting("ccEmail").equals("true")) {
                smm.setBcc(preferredEmail == null ? user.getEmail() : preferredEmail);
            }

            smm.setSubject(subject);
            smm.setText(templatedMessage);

            emailSender.send(smm);

        }

        actionLogRepo.createPublicLog(submission, user, recipientEmails + subject + ": " + templatedMessage);
    }

    @RequestMapping(value = "/batch-comment")
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse batchComment(@WeaverUser User user, @RequestBody Map<String, Object> data) {
        submissionRepo.batchDynamicSubmissionQuery(user.getActiveFilter(), user.getSubmissionViewColumns())
                .forEach(sub -> {
                    Map<String, Object> subMessage = new HashMap<String, Object>(data);
                    if ((boolean) data.get("sendEmailToRecipient")
                            || (boolean) data.get("sendEmailToCCRecipient")) {
                        subMessage.put("recipientEmail",
                                findEmailValue(sub, subMessage.get("recipientEmail").toString()));
                        subMessage.put("ccRecipientEmail",
                                findEmailValue(sub, subMessage.get("ccRecipientEmail").toString()));
                    }

                    addComment(user, sub.getId(), subMessage);
                });
        return new ApiResponse(SUCCESS);

    }

    private String findEmailValue(Submission submission, String recipient) {
        if (recipient.equals("student")) {
            // data.put("recipientEmail", )
            return submission.getSubmitter().getSetting("preferedEmail");

        } else if (recipient.equals("advisor")) {
            return submission.getFieldValuesByInputType(inputTypeRepo.findByName("INPUT_CONTACT")).get(0)
                    .getContacts().get(0);
        } else {
            return "";
        }
    }

    @RequestMapping(value = "/{submissionId}/update-field-value/{fieldProfileId}", method = RequestMethod.POST)
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse updateFieldValue(@WeaverUser User user, @PathVariable Long submissionId,
            @PathVariable String fieldProfileId, @RequestBody FieldValue fieldValue) {
        ApiResponse apiResponse = null;
        SubmissionFieldProfile submissionFieldProfile = submissionFieldProfileRepo
                .getOne(Long.parseLong(fieldProfileId));
        ValidationResults validationResults = getValidationResults(submissionFieldProfile.getId().toString(),
                fieldValue);
        if (validationResults.isValid()) {
            Map<String, String> orcidErrors = new HashMap<String, String>();
            if (isOrcidVerificationActive(submissionFieldProfile, fieldValue)) {
                orcidErrors = OrcidUtility.verifyOrcid(user, fieldValue);
            }
            if (orcidErrors.isEmpty()) {
                Submission submission = submissionRepo.read(submissionId);
                if (fieldValue.getId() == null) {

                    fieldValue = fieldValueRepo.save(fieldValue);
                    submission.addFieldValue(fieldValue);
                    submission = submissionRepo.save(submission);

                    if (submissionFieldProfile.getLogged()) {
                        actionLogRepo.createPublicLog(submission, user,
                                submissionFieldProfile.getGloss() + " was set to " + fieldValue.getValue());
                    }

                } else {

                    FieldValue oldFieldValue = fieldValueRepo.findOne(fieldValue.getId());
                    String oldValue = oldFieldValue.getValue();
                    fieldValue = fieldValueRepo.save(fieldValue);

                    if (submissionFieldProfile.getLogged()) {
                        actionLogRepo.createPublicLog(submission, user,
                                submissionFieldProfile.getGloss() + " was changed from " + convertBoolean(oldValue)
                                        + " to " + convertBoolean(fieldValue.getValue()));
                    }

                }
                apiResponse = new ApiResponse(SUCCESS, fieldValue);

                simpMessagingTemplate.convertAndSend("/channel/submission/" + submission.getId() + "/field-values",
                        apiResponse);

            } else {
                Map<String, Map<String, String>> orcidErrorsMap = new HashMap<String, Map<String, String>>();
                orcidErrorsMap.put("value", orcidErrors);
                apiResponse = new ApiResponse(INVALID, orcidErrorsMap);
            }
        } else {
            apiResponse = new ApiResponse(INVALID, validationResults.getMessages());
        }

        return apiResponse;
    }

    private String convertBoolean(final String value) {
        String result = value;
        if (result.equals("true")) {
            result = "Yes";
        } else if (result.equals("false")) {
            result = "No";
        }
        return result;
    }

    @RequestMapping(value = "/{submissionId}/validate-field-value/{fieldProfileId}", method = RequestMethod.POST)
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse validateFieldValue(@WeaverUser User user, @PathVariable Long submissionId,
            @PathVariable String fieldProfileId, @RequestBody FieldValue fieldValue) {
        ApiResponse apiResponse = null;
        SubmissionFieldProfile submissionFieldProfile = submissionFieldProfileRepo
                .getOne(Long.parseLong(fieldProfileId));
        ValidationResults validationResults = getValidationResults(submissionFieldProfile.getId().toString(),
                fieldValue);
        if (validationResults.isValid()) {
            apiResponse = new ApiResponse(SUCCESS, validationResults.getMessages());
        } else {
            apiResponse = new ApiResponse(INVALID, validationResults.getMessages());
        }
        return apiResponse;
    }

    private ValidationResults getValidationResults(String fieldProfileId, FieldValue fieldValue) {
        fieldValue.setModelValidator(
                new FieldValueValidator(submissionFieldProfileRepo.getOne(Long.parseLong(fieldProfileId))));
        return fieldValue.validate(fieldValue);
    }

    private boolean isOrcidVerificationActive(SubmissionFieldProfile submissionFieldProfile,
            FieldValue fieldValue) {
        return submissionFieldProfile.getInputType().getName().equals("INPUT_ORCID") && configurationRepo
                .getByNameAndType("orcid_authentication", "orcid").getValue().toLowerCase().equals("true");
    }

    @RequestMapping(value = "/{submissionId}/update-custom-action-value", method = RequestMethod.POST)
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse updateCustomActionValue(@WeaverUser User user,
            @PathVariable("submissionId") Long submissionId, @RequestBody CustomActionValue customActionValue) {
        Submission submission = submissionRepo.getOne(submissionId);
        ApiResponse response = new ApiResponse(SUCCESS, customActionValueRepo.update(customActionValue));
        actionLogRepo.createPublicLog(submission, user,
                "Custom action " + customActionValue.getDefinition().getLabel() + " "
                        + (customActionValue.getValue() ? "set" : "unset"));
        simpMessagingTemplate.convertAndSend("/channel/submission/" + submission.getId() + "/custom-action-values",
                response);
        return response;
    }

    @RequestMapping("/{submissionId}/change-status/{submissionStatusName}")
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse changeStatus(@WeaverUser User user, @PathVariable Long submissionId,
            @PathVariable String submissionStatusName) {
        Submission submission = submissionRepo.read(submissionId);
        ApiResponse response;
        if (submission != null) {
            SubmissionStatus submissionStatus = submissionStatusRepo.findByName(submissionStatusName);
            if (submissionStatus != null) {
                submission = submissionRepo.updateStatus(submission, submissionStatus, user);
                response = new ApiResponse(SUCCESS, submission);
            } else {
                response = new ApiResponse(ERROR,
                        "Could not find a submission status name " + submissionStatusName);
            }
        } else {
            response = new ApiResponse(ERROR, "Could not find a submission with ID " + submissionId);
        }
        processEmailWorkflowRules(user, submission);
        return response;
    }

    @RequestMapping("/batch-update-status/{submissionStatusName}")
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse batchUpdateSubmissionStatuses(@WeaverUser User user,
            @PathVariable String submissionStatusName) {
        submissionRepo.batchDynamicSubmissionQuery(user.getActiveFilter(), user.getSubmissionViewColumns())
                .forEach(submission -> {
                    SubmissionStatus submissionStatus = submissionStatusRepo.findByName(submissionStatusName);
                    submission = submissionRepo.updateStatus(submission, submissionStatus, user);
                    processEmailWorkflowRules(user, submission);
                });
        return new ApiResponse(SUCCESS);

    }

    @RequestMapping("/{submissionId}/publish/{depositLocationId}")
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse publish(@WeaverUser User user, @PathVariable Long submissionId,
            @PathVariable Long depositLocationId) throws Exception {
        Submission submission = submissionRepo.read(submissionId);

        ApiResponse response;
        if (submission != null) {

            SubmissionStatus submissionStatus = submissionStatusRepo.findByName("Published");
            if (submissionStatus != null) {

                DepositLocation depositLocation = depositLocationRepo.findOne(depositLocationId);

                if (depositLocation != null) {

                    Depositor depositor = depositorService.getDepositor(depositLocation.getDepositorName());

                    if (depositor != null) {
                        ExportPackage exportPackage = packagerUtility.packageExport(depositLocation.getPackager(),
                                submission);
                        String depositURL = depositor.deposit(depositLocation, exportPackage);
                        submission.setDepositURL(depositURL);
                        submission = submissionRepo.updateStatus(submission, submissionStatus, user);
                        response = new ApiResponse(SUCCESS, submission);
                    } else {
                        response = new ApiResponse(ERROR,
                                "Could not find a depositor name " + depositLocation.getDepositorName());
                    }
                } else {
                    response = new ApiResponse(ERROR, "Could not find a deposite location id " + depositLocationId);
                }
            } else {
                response = new ApiResponse(ERROR, "Could not find a submission status name Published");
            }
        } else {
            response = new ApiResponse(ERROR, "Could not find a submission with ID " + submissionId);
        }

        processEmailWorkflowRules(user, submission);

        return response;
    }

    @PreAuthorize("hasRole('REVIEWER')")
    @RequestMapping("/batch-export/{packagerName}/{filterId}")
    public void batchExportWithFilter(HttpServletResponse response, @WeaverUser User user,
            @PathVariable String packagerName, @PathVariable Long filterId) throws IOException {
        NamedSearchFilterGroup filter = namedSearchFilterGroupRepo.findOne(filterId);
        processBatchExport(response, user, packagerName, filter);
    }

    @PreAuthorize("hasRole('REVIEWER')")
    @RequestMapping("/batch-export/{packagerName}")
    public void batchExport(HttpServletResponse response, @WeaverUser User user, @PathVariable String packagerName)
            throws IOException {
        NamedSearchFilterGroup activeFilter = user.getActiveFilter();
        processBatchExport(response, user, packagerName, activeFilter);
    }

    @SuppressWarnings("unchecked")
    private void processBatchExport(HttpServletResponse response, User user, String packagerName,
            NamedSearchFilterGroup filter) throws IOException {
        AbstractPackager<?> packager = packagerUtility.getPackager(packagerName);

        List<SubmissionListColumn> columns = filter.getColumnsFlag() ? filter.getSavedColumns()
                : user.getSubmissionViewColumns();
        switch (packagerName.trim()) {
        case "Excel":
            List<Submission> submissions = submissionRepo.batchDynamicSubmissionQuery(filter, columns);

            HSSFWorkbook workbook = new HSSFWorkbook();

            HSSFSheet worksheet = workbook.createSheet();

            int rowCount = 0;

            HSSFRow header = worksheet.createRow(rowCount++);

            for (int i = 0; i < columns.size(); i++) {
                SubmissionListColumn column = columns.get(i);
                header.createCell(i).setCellValue(column.getTitle());
            }

            for (Submission submission : submissions) {
                ExportPackage exportPackage = packagerUtility.packageExport(packager, submission, columns);
                if (exportPackage.isMap()) {
                    Map<String, String> rowData = (Map<String, String>) exportPackage.getPayload();
                    HSSFRow row = worksheet.createRow(rowCount++);
                    for (int i = 0; i < columns.size(); i++) {
                        SubmissionListColumn column = columns.get(i);
                        row.createCell(i).setCellValue(rowData.get(column.getTitle()));
                    }
                }
            }

            for (int i = 0; i < columns.size(); i++) {
                worksheet.autoSizeColumn(i);
            }

            response.setContentType(packager.getMimeType());
            response.setHeader("Content-Disposition",
                    "inline; filename=" + packagerName + "." + packager.getFileExtension());
            workbook.write(response.getOutputStream());

            break;
        case "MarcXML21":
        case "DSpaceMETS":
        case "ProQuest":
            try {
                ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());

                // TODO: need a more dynamic way to achieve this
                if (packagerName.equals("ProQuest")) {
                    // TODO: add filter for UMI Publication true
                }

                for (Submission submission : submissionRepo.batchDynamicSubmissionQuery(filter, columns)) {
                    ExportPackage exportPackage = packagerUtility.packageExport(packager, submission);

                    if (exportPackage.isFile()) {
                        File exportFile = (File) exportPackage.getPayload();
                        if (packagerName.equals("MarcXML21")) {
                            zos.putNextEntry(new ZipEntry("MarcXML21/" + exportFile.getName()));
                        } else {
                            zos.putNextEntry(new ZipEntry(exportFile.getName()));
                        }
                        zos.write(Files.readAllBytes(exportFile.toPath()));
                        zos.closeEntry();
                    }

                }
                zos.close();

                response.setContentType(packager.getMimeType());
                response.setHeader("Content-Disposition",
                        "inline; filename=" + packagerName + "." + packager.getFileExtension());
            } catch (Exception e) {
                response.setContentType("application/json");
                ApiResponse apiResponse = new ApiResponse(ERROR, "Something went wrong with the export!");
                PrintWriter out = response.getWriter();
                out.print(objectMapper.writeValueAsString(apiResponse));
                out.close();
            }
            break;
        case "DSpaceSimple":
            try {
                ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
                for (Submission submission : submissionRepo.batchDynamicSubmissionQuery(filter, columns)) {
                    String submissionName = "submission_" + submission.getId() + "/";
                    zos.putNextEntry(new ZipEntry(submissionName));

                    StringBuilder contentsText = new StringBuilder();
                    ExportPackage exportPackage = packagerUtility.packageExport(packager, submission);

                    if (exportPackage.isMap()) {
                        for (Map.Entry<String, File> fileEntry : ((Map<String, File>) exportPackage.getPayload())
                                .entrySet()) {
                            zos.putNextEntry(new ZipEntry(submissionName + fileEntry.getKey()));
                            contentsText.append("MD " + fileEntry.getKey() + "\n");
                            zos.write(Files.readAllBytes(fileEntry.getValue().toPath()));
                            zos.closeEntry();
                        }
                    }

                    // LICENSES
                    for (FieldValue ldfv : submission.getLicenseDocumentFieldValues()) {
                        Path path = assetService.getAssetsAbsolutePath(ldfv.getValue());
                        byte[] fileBytes = Files.readAllBytes(path);
                        zos.putNextEntry(new ZipEntry(submissionName + ldfv.getFileName()));
                        contentsText.append(ldfv.getFileName() + " bundle:LICENSE\n");
                        zos.write(fileBytes);
                        zos.closeEntry();
                    }

                    // PRIMARY_DOC
                    FieldValue primaryDoc = submission.getPrimaryDocumentFieldValue();
                    Path path = assetService.getAssetsAbsolutePath(primaryDoc.getValue());
                    byte[] fileBytes = Files.readAllBytes(path);
                    zos.putNextEntry(new ZipEntry(submissionName + primaryDoc.getFileName()));
                    contentsText.append(primaryDoc.getFileName() + "  bundle:CONTENT  primary:true\n");
                    zos.write(fileBytes);
                    zos.closeEntry();

                    // SUPPLEMENTAL_DOCS
                    // supplemental, source, administrative
                    // ask Stephanie about administrative
                    List<FieldValue> supplDocs = submission.getSupplementalAndSourceDocumentFieldValues();
                    for (FieldValue supplDoc : supplDocs) {
                        Path supplPath = assetService.getAssetsAbsolutePath(supplDoc.getValue());
                        byte[] supplFileBytes = Files.readAllBytes(supplPath);
                        zos.putNextEntry(new ZipEntry(submissionName + supplDoc.getFileName()));
                        contentsText.append(supplDoc.getFileName() + "  bundle:CONTENT\n");
                        zos.write(supplFileBytes);
                        zos.closeEntry();
                    }

                    // CONTENTS_FILE
                    zos.putNextEntry(new ZipEntry(submissionName + "contents"));
                    zos.write(contentsText.toString().getBytes());
                    zos.closeEntry();

                    zos.closeEntry();
                }
                zos.close();

                response.setContentType(packager.getMimeType());
                response.setHeader("Content-Disposition",
                        "inline; filename=" + packagerName + "." + packager.getFileExtension());
            } catch (Exception e) {
                response.setContentType("application/json");

                ApiResponse apiResponse = new ApiResponse(ERROR, "Something went wrong with the export!");

                PrintWriter out = response.getWriter();
                out.print(objectMapper.writeValueAsString(apiResponse));
                out.close();
            }
            break;

        default:
            response.setContentType("application/json");

            ApiResponse apiResponse = new ApiResponse(ERROR, "No packager " + packagerName + " found!");
            PrintWriter out = response.getWriter();
            out.print(objectMapper.writeValueAsString(apiResponse));
            out.close();
        }

        response.getOutputStream().close();
    }

    @RequestMapping(value = "/batch-assign-to", method = RequestMethod.POST)
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse batchAssignTo(@WeaverUser User user, @RequestBody User assignee) {
        submissionRepo.batchDynamicSubmissionQuery(user.getActiveFilter(), user.getSubmissionViewColumns())
                .forEach(sub -> {
                    sub.setAssignee(assignee);
                    actionLogRepo.createPublicLog(sub, user, "Submission was assigned to " + assignee.getFirstName()
                            + " " + assignee.getLastName() + "(" + assignee.getEmail() + ")");
                    submissionRepo.update(sub);
                });
        return new ApiResponse(SUCCESS);

    }

    @RequestMapping("/batch-publish/{depositLocationId}")
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse batchPublish(@WeaverUser User user, @PathVariable Long depositLocationId) {
        ApiResponse response = new ApiResponse(SUCCESS);
        SubmissionStatus submissionStatus = submissionStatusRepo.findByName("Published");
        if (submissionStatus != null) {
            DepositLocation depositLocation = depositLocationRepo.findOne(depositLocationId);
            if (depositLocation != null) {
                Depositor depositor = depositorService.getDepositor(depositLocation.getDepositorName());
                if (depositor != null) {
                    for (Submission submission : submissionRepo.batchDynamicSubmissionQuery(user.getActiveFilter(),
                            user.getSubmissionViewColumns())) {
                        try {
                            ExportPackage exportPackage = packagerUtility
                                    .packageExport(depositLocation.getPackager(), submission);
                            String depositURL = depositor.deposit(depositLocation, exportPackage);
                            submission.setDepositURL(depositURL);
                            submission = submissionRepo.updateStatus(submission, submissionStatus, user);
                        } catch (Exception e) {
                            throw new DepositException("Failed package export on submission " + submission.getId());
                        }
                    }
                } else {
                    response = new ApiResponse(ERROR,
                            "Could not find a depositor name " + depositLocation.getDepositorName());
                }
            } else {
                response = new ApiResponse(ERROR, "Could not find a deposite location id " + depositLocationId);
            }
        } else {
            response = new ApiResponse(ERROR, "Could not find a submission status name Published");
        }
        return response;
    }

    @RequestMapping(value = "/{submissionId}/submit-date", method = RequestMethod.POST)
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse submitDate(@WeaverUser User user, @PathVariable("submissionId") Long submissionId,
            @RequestBody String newDate) throws ParseException {

        Submission submission = submissionRepo.read(submissionId);

        ApiResponse response = new ApiResponse(SUCCESS);
        if (submission != null) {
            String nd = newDate.replaceAll("[\"]", "");
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
            Calendar cal = Calendar.getInstance();
            cal.setTime(df.parse(nd));

            submission.setSubmissionDate(cal);
            submission = submissionRepo.update(submission);

            SimpleDateFormat logdf = new SimpleDateFormat("MM/dd/yyyy");
            actionLogRepo.createPublicLog(submission, user,
                    "Submission date set to: " + logdf.format(cal.getTime()));

        } else {
            response = new ApiResponse(ERROR, "Could not find a submission with ID " + submissionId);
        }

        return response;
    }

    @RequestMapping(value = "/{submissionId}/assign-to", method = RequestMethod.POST)
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse assign(@WeaverUser User user, @PathVariable("submissionId") Long submissionId,
            @RequestBody User assignee) {
        Submission submission = submissionRepo.read(submissionId);

        if (assignee != null) {
            assignee = userRepo.findByEmail(assignee.getEmail());
        }

        ApiResponse response = new ApiResponse(SUCCESS);

        if (submission != null) {
            submission.setAssignee(assignee);
            submission = submissionRepo.update(submission);

            if (assignee == null) {
                actionLogRepo.createPublicLog(submission, user, "Submission was unassigned");
            } else {
                actionLogRepo.createPublicLog(submission, user, "Submission was assigned to "
                        + assignee.getFirstName() + " " + assignee.getLastName() + "(" + assignee.getEmail() + ")");
            }
        } else {
            response = new ApiResponse(ERROR, "Could not find a submission with ID " + submissionId);
        }

        return response;
    }

    @RequestMapping(value = "/{submissionId}/remove-field-value", method = RequestMethod.POST)
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse removeFieldValue(@PathVariable("submissionId") Long submissionId,
            @RequestBody FieldValue fieldValue) {
        Submission submission = submissionRepo.read(submissionId);
        submission.removeFieldValue(fieldValue);
        submission = submissionRepo.save(submission);
        simpMessagingTemplate.convertAndSend("/channel/submission/" + submission.getId() + "/removed-field-value",
                new ApiResponse(SUCCESS, fieldValue));
        return new ApiResponse(SUCCESS, submission);
    }

    @RequestMapping(value = "/{submissionId}/update-reviewer-notes", method = RequestMethod.POST)
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse updateReviewerNotes(@WeaverUser User user, @PathVariable("submissionId") Long submissionId,
            @RequestBody Map<String, String> requestData) {
        Submission submission = submissionRepo.read(submissionId);
        String reviewerNotes = requestData.get("reviewerNotes");
        submission.setReviewerNotes(reviewerNotes);
        submission = submissionRepo.update(submission);
        actionLogRepo.createPrivateLog(submission, user, "Submission notes changed to \"" + reviewerNotes + "\"");
        return new ApiResponse(SUCCESS, submission);
    }

    @RequestMapping("/{submissionId}/needs-correction")
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse setSubmissionNeedsCorrection(@WeaverUser User user, @PathVariable Long submissionId) {
        Submission submission = submissionRepo.read(submissionId);
        SubmissionStatus needsCorrectionStatus = submissionStatusRepo
                .findByName(NEEDS_CORRECTION_SUBMISSION_STATUS_NAME);
        String oldSubmissionStatusName = submission.getSubmissionStatus().getName();
        submission.setSubmissionStatus(needsCorrectionStatus);
        submission = submissionRepo.update(submission);
        actionLogRepo.createPublicLog(submission, user, "Submission status was changed from "
                + oldSubmissionStatusName + " to " + NEEDS_CORRECTION_SUBMISSION_STATUS_NAME);
        return new ApiResponse(SUCCESS, submission);
    }

    @RequestMapping("/{submissionId}/submit-corrections")
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse setSubmissionCorrectionsReceived(@WeaverUser User user, @PathVariable Long submissionId) {
        Submission submission = submissionRepo.read(submissionId);
        String oldSubmissionStatusName = submission.getSubmissionStatus().getName();
        SubmissionStatus needsCorrectionStatus = submissionStatusRepo
                .findByName(CORRECTIONS_RECEIVED_SUBMISSION_STATUS_NAME);
        submission.setSubmissionStatus(needsCorrectionStatus);
        submission = submissionRepo.update(submission);
        actionLogRepo.createPublicLog(submission, user, "Submission status was changed from "
                + oldSubmissionStatusName + " to " + CORRECTIONS_RECEIVED_SUBMISSION_STATUS_NAME);
        return new ApiResponse(SUCCESS, submission);
    }

    @RequestMapping(value = "/{submissionId}/add-message", method = RequestMethod.POST)
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse addMessage(@WeaverUser User user, @PathVariable Long submissionId,
            @RequestBody String message) {
        Submission submission = submissionRepo.read(submissionId);
        return new ApiResponse(SUCCESS, actionLogRepo.createPublicLog(submission, user, message));
    }

    @JsonView(ApiView.Partial.class)
    @RequestMapping(value = "/query/{page}/{size}", method = RequestMethod.POST)
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse querySubmission(@WeaverUser User user, @PathVariable Integer page,
            @PathVariable Integer size, @RequestBody List<SubmissionListColumn> submissionListColumns)
            throws ExecutionException {
        long startTime = System.nanoTime();
        NamedSearchFilterGroup activeFilter = user.getActiveFilter();
        Page<Submission> submissions = submissionRepo.pageableDynamicSubmissionQuery(activeFilter,
                activeFilter.getColumnsFlag() ? activeFilter.getSavedColumns() : submissionListColumns,
                new PageRequest(page, size));
        long endTime = System.nanoTime();
        long duration = (endTime - startTime);
        LOG.info("Dynamic query took " + (double) (duration / 1000000000.0) + " seconds");
        return new ApiResponse(SUCCESS, new ApiPage<Submission>(submissions));
    }

    @RequestMapping("/file")
    public void submissionFile(HttpServletResponse response, @RequestHeader String uri) throws IOException {
        response.addHeader("Content-Disposition", "attachment");
        Path path = assetService.getAssetsAbsolutePath(uri);
        Files.copy(path, response.getOutputStream());
        response.getOutputStream().flush();
    }

    @RequestMapping(value = "/file-info", method = RequestMethod.POST)
    public ApiResponse submissionFileInfo(@RequestBody Map<String, String> requestData) throws IOException {
        return new ApiResponse(SUCCESS, assetService.getAssetFileInfo(requestData.get("uri")));
    }

    @RequestMapping(value = "/{submissionId}/{documentType}/upload-file", method = RequestMethod.POST)
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse uploadFile(@WeaverUser User user, @PathVariable Long submissionId,
            @PathVariable String documentType, @RequestParam MultipartFile file) throws IOException {
        int hash = user.getEmail().hashCode();
        String fileName = file.getOriginalFilename();

        String[] fileNameParts = fileName.split("\\.");
        String fileExtension = fileNameParts.length > 1 ? fileNameParts[1] : "pdf";

        if (documentTypesToRename.contains(documentType)) {
            String lastName = user.getLastName().toUpperCase();
            int year = Calendar.getInstance().get(Calendar.YEAR);
            fileName = lastName + "-" + documentType + "-" + String.valueOf(year) + "." + fileExtension;
        }

        String uri = documentFolder + File.separator + hash + File.separator + System.currentTimeMillis() + "-"
                + fileName;
        assetService.write(file.getBytes(), uri);
        JsonNode fileInfo = assetService.getAssetFileInfo(uri);
        actionLogRepo.createPublicLog(submissionRepo.read(submissionId), user, documentType + " file "
                + fileInfo.get("name").asText() + " (" + (fileInfo.get("size").asInt() / 1024) + " KB) uploaded");
        return new ApiResponse(SUCCESS, uri);
    }

    @RequestMapping(value = "/{submissionId}/{documentType}/rename-file", method = RequestMethod.POST)
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse renameFile(@WeaverUser User user, @PathVariable Long submissionId,
            @PathVariable String documentType, @RequestBody Map<String, String> requestData) throws IOException {
        String newName = requestData.get("newName");
        String oldUri = requestData.get("uri");
        String newUri = oldUri.replace(oldUri.substring(oldUri.lastIndexOf('/') + 1, oldUri.length()),
                System.currentTimeMillis() + "-" + newName);
        assetService.copy(oldUri, newUri);
        assetService.delete(oldUri);
        JsonNode fileInfo = assetService.getAssetFileInfo(newUri);
        actionLogRepo.createPublicLog(submissionRepo.read(submissionId), user, documentType + " file "
                + fileInfo.get("name").asText() + " (" + (fileInfo.get("size").asInt() / 1024) + " KB) renamed");
        return new ApiResponse(SUCCESS, newUri);
    }

    @RequestMapping("/{submissionId}/{fieldValueId}/remove-file")
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse removeFile(@WeaverUser User user, @PathVariable Long submissionId,
            @PathVariable Long fieldValueId) throws IOException {
        ApiResponse apiResponse = new ApiResponse(SUCCESS);
        int hash = user.getEmail().hashCode();
        FieldValue fileFieldValue = fieldValueRepo.findOne(fieldValueId);
        String documentType = fileFieldValue.getFieldPredicate().getValue();
        String uri = fileFieldValue.getValue();
        if (user.getRole().equals(Role.ROLE_STUDENT) && documentType.equals("_doctype_license")) {
            apiResponse = new ApiResponse(ERROR, "You are not allowed to delete license files!");
        } else {
            if (user.getRole().equals(Role.ROLE_ADMIN) || user.getRole().equals(Role.ROLE_MANAGER)
                    || uri.contains(String.valueOf(hash))) {
                JsonNode fileInfo = assetService.getAssetFileInfo(uri);
                assetService.delete(uri);
                actionLogRepo.createPublicLog(submissionRepo.read(submissionId), user,
                        documentType.substring(9).toUpperCase() + " file " + fileInfo.get("name").asText() + " ("
                                + (fileInfo.get("size").asInt() / 1024) + " KB) removed");
            } else {
                apiResponse = new ApiResponse(ERROR, "This is not your file to delete!");
            }
        }
        return apiResponse;
    }

    @RequestMapping(value = "/{submissionId}/{documentType}/archive-file", method = RequestMethod.POST)
    @PreAuthorize("hasRole('STUDENT')")
    public ApiResponse archiveFile(@WeaverUser User user, @PathVariable Long submissionId,
            @PathVariable String documentType, @RequestBody Map<String, String> requestData) throws IOException {
        String name = requestData.get("name");
        String oldUri = requestData.get("uri");
        String newUri = oldUri.replace(oldUri.substring(oldUri.lastIndexOf('/') + 1, oldUri.length()),
                System.currentTimeMillis() + "-archived-" + name);
        assetService.copy(oldUri, newUri);
        assetService.delete(oldUri);
        JsonNode fileInfo = assetService.getAssetFileInfo(newUri);
        actionLogRepo.createPublicLog(submissionRepo.read(submissionId), user,
                "ARCHIVE - " + documentType + " file " + fileInfo.get("name").asText() + " ("
                        + (fileInfo.get("size").asInt() / 1024) + " KB) archived");
        return new ApiResponse(SUCCESS, newUri);
    }

    @RequestMapping("/{submissionId}/send-advisor-email")
    @PreAuthorize("hasRole('REVIEWER')")
    public ApiResponse sendAdvisorEmail(@WeaverUser User user, @PathVariable Long submissionId) {

        Submission submission = submissionRepo.read(submissionId);

        InputType contactInputType = inputTypeRepo.findByName("INPUT_CONTACT");

        EmailTemplate template = emailTemplateRepo.findByNameAndSystemRequired("SYSTEM Advisor Review Request",
                true);

        String subject = templateUtility.compileString(template.getSubject(), submission);
        String content = templateUtility.compileTemplate(template, submission);

        // TODO: this needs to only send email to the advisor not any field value that
        // is contact type
        submission.getFieldValuesByInputType(contactInputType).forEach(fv -> {

            SimpleMailMessage smm = new SimpleMailMessage();

            smm.setTo(String.join(",", fv.getContacts()));

            String preferedEmail = user.getSetting("preferedEmail");

            if ("true".equals(user.getSetting("ccEmail"))) {
                smm.setBcc(preferedEmail == null ? user.getEmail() : preferedEmail);
            }

            smm.setSubject(subject);
            smm.setText(content);

            emailSender.send(smm);

        });

        actionLogRepo.createPublicLog(submission, user, "Advisor review email manually generated.");

        return new ApiResponse(SUCCESS);
    }

    // TODO: rework, anonymous endpoint for advisor approval, no user available for
    // action log
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "/{submissionId}/update-advisor-approval", method = RequestMethod.POST)
    public ApiResponse updateAdvisorApproval(@PathVariable Long submissionId,
            @RequestBody Map<String, Object> data) {

        Submission submission = submissionRepo.read(submissionId);
        HashMap<String, Object> embargoData = (HashMap<String, Object>) data.get("embargo");
        HashMap<String, Object> advisorData = (HashMap<String, Object>) data.get("advisor");

        Boolean approveAdvisor = (Boolean) advisorData.get("approve");
        Boolean approveEmbargo = (Boolean) embargoData.get("approve");

        Boolean clearApproveEmbargo = (Boolean) embargoData.get("clearApproval");
        Boolean clearApproveAdvisor = (Boolean) advisorData.get("clearApproval");

        String message = (String) data.get("message");

        if (approveAdvisor != null && !clearApproveAdvisor) {
            processAdvisorAction("Application", approveAdvisor, submission);
            submission.setApproveAdvisor(approveAdvisor);
            submission.setApproveAdvisorDate(Calendar.getInstance());
        }

        if (clearApproveAdvisor != null && clearApproveAdvisor) {
            processAdvisorStatusClear("Application", submission.getApproveAdvisor(), submission);
            submission.clearApproveAdvisor();
        }

        if (approveEmbargo != null && !clearApproveEmbargo) {
            processAdvisorAction("Embargo", approveEmbargo, submission);
            submission.setApproveEmbargo(approveEmbargo);
            submission.setApproveEmbargoDate(Calendar.getInstance());
        }

        if (clearApproveEmbargo != null && clearApproveEmbargo) {
            processAdvisorStatusClear("Embargo", submission.getApproveEmbargo(), submission);
            submission.clearApproveEmbargo();
        }

        if (message != null) {
            actionLogRepo.createAdvisorPublicLog(submission, "Advisor comments : " + message);
        }

        return new ApiResponse(SUCCESS, submission);

    }

    private void processAdvisorAction(String type, Boolean approveStatus, Submission submission) {
        String approveAdvisorMessage;
        if (approveStatus == true) {
            approveAdvisorMessage = "The committee approved the " + type + ".";
        } else {
            approveAdvisorMessage = "The committee rejected the " + type + ".";
        }
        actionLogRepo.createAdvisorPublicLog(submission, approveAdvisorMessage);
    }

    private void processAdvisorStatusClear(String type, Boolean approvalState, Submission submission) {
        String clearAdvisorMessage = "The committee has withdrawn its " + type;
        if (approvalState == true) {
            clearAdvisorMessage += " Approval.";
        } else {
            clearAdvisorMessage += " Rejection.";
        }
        actionLogRepo.createAdvisorPublicLog(submission, clearAdvisorMessage);
    }

    private void processEmailWorkflowRules(User user, Submission submission) {

        SimpleMailMessage smm = new SimpleMailMessage();

        List<EmailWorkflowRule> rules = submission.getOrganization().getAggregateEmailWorkflowRules();

        for (EmailWorkflowRule rule : rules) {

            LOG.debug("Email Workflow Rule " + rule.getId() + " firing for submission " + submission.getId());

            if (rule.getSubmissionStatus().equals(submission.getSubmissionStatus()) && !rule.isDisabled()) {

                // TODO: Not all variables are currently being replaced.
                String subject = templateUtility.compileString(rule.getEmailTemplate().getSubject(), submission);
                String content = templateUtility.compileTemplate(rule.getEmailTemplate(), submission);

                for (String email : rule.getEmailRecipient().getEmails(submission)) {

                    try {
                        LOG.debug("\tSending email to recipient at address " + email);

                        smm.setTo(email);

                        String preferedEmail = user.getSetting("preferedEmail");

                        if ("true".equals(user.getSetting("ccEmail"))) {
                            smm.setBcc(preferedEmail == null ? user.getEmail() : preferedEmail);
                        }

                        smm.setSubject(subject);
                        smm.setText(content);

                        emailSender.send(smm);
                    } catch (MailException me) {
                        LOG.error("Problem sending email: " + me.getMessage());
                    }
                }

            } else {
                LOG.debug("\tRule disabled or of irrelevant status condition.");
            }
        }
    }

}