org.wise.portal.presentation.web.controllers.admin.ImportProjectController.java Source code

Java tutorial

Introduction

Here is the source code for org.wise.portal.presentation.web.controllers.admin.ImportProjectController.java

Source

/**
 * Copyright (c) 2008-2016 Regents of the University of California (Regents).
 * Created by WISE, Graduate School of Education, University of California, Berkeley.
 * 
 * This software is distributed under the GNU General Public License, v3,
 * or (at your option) any later version.
 * 
 * Permission is hereby granted, without written agreement and without license
 * or royalty fees, to use, copy, modify, and distribute this software and its
 * documentation for any purpose, provided that the above copyright notice and
 * the following two paragraphs appear in all copies of this software.
 * 
 * REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
 * HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
 * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 * 
 * IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 * SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 * REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.wise.portal.presentation.web.controllers.admin;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.ModelAttribute;
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.multipart.MultipartFile;
import org.wise.portal.domain.module.Curnit;
import org.wise.portal.domain.module.impl.ModuleParameters;
import org.wise.portal.domain.project.Project;
import org.wise.portal.domain.project.ProjectMetadata;
import org.wise.portal.domain.project.ProjectUpload;
import org.wise.portal.domain.project.impl.ProjectMetadataImpl;
import org.wise.portal.domain.project.impl.ProjectParameters;
import org.wise.portal.domain.project.impl.ProjectType;
import org.wise.portal.domain.user.User;
import org.wise.portal.presentation.web.controllers.ControllerUtil;
import org.wise.portal.service.module.CurnitService;
import org.wise.portal.service.project.ProjectService;

import javax.servlet.http.HttpServletResponse;

/**
 * Admin tool for uploading a zipped WISE project.
 * Unzips to curriculum_base_dir and registers the project (ie creates project in DB).
 * 
 * @author Hiroki Terashima
 */
@Controller
public class ImportProjectController {

    @Autowired
    private ProjectService projectService;

    @Autowired
    private CurnitService curnitService;

    @Autowired
    private Properties wiseProperties;

    private String getWISEProjectsURL = "http://wise5.org/wiseup/getProject.php";

    @RequestMapping(value = "/admin/project/importFromHub", method = RequestMethod.POST)
    protected String importFromHub(
            @RequestParam(value = "importableProjectId", required = true) String importableProjectId,
            ModelMap modelMap) throws Exception {

        // URL to get the importableProject zip file
        String getImportableProjectURL = getWISEProjectsURL + "?id=" + importableProjectId;

        try {
            URL url = new URL(getImportableProjectURL);
            URLConnection conn = url.openConnection();
            InputStream in = conn.getInputStream();
            URL downloadFileUrl = conn.getURL();
            String downloadFileUrlString = downloadFileUrl.toString();
            String zipFilename = downloadFileUrlString.substring(downloadFileUrlString.lastIndexOf("/") + 1);

            byte[] fileBytes = IOUtils.toByteArray(in);
            String msg = "Import project complete!";

            Project project = importProject(zipFilename, fileBytes);
            if (project == null) {
                System.err.println("Error occured during project import.");
                msg = "Error occured during project import. Check the log for more information.";
            }

            modelMap.put("msg", msg);
            modelMap.put("newProject", project);
            return "admin/project/import";
        } catch (IOException e) {
            System.err.println("Error occured during project import.");
            e.printStackTrace();
        }

        return "admin/project/import";
    }

    @RequestMapping(value = "/admin/project/import", method = RequestMethod.POST)
    protected String onSubmit(@ModelAttribute("projectZipFile") ProjectUpload projectUpload, ModelMap modelMap)
            throws Exception {
        // TODO: check zip contents for maliciousness. For now, it's only accessible to admin.

        // uploaded file must be a zip file and have a .zip extension
        MultipartFile file = projectUpload.getFile();
        String zipFilename = file.getOriginalFilename();
        byte[] fileBytes = file.getBytes();
        String msg = "Import project complete!";

        Project project = importProject(zipFilename, fileBytes);
        if (project == null) {
            System.err.println("Error occured during project import.");
            msg = "Error occured during project import. Check the log for more information.";
        }

        modelMap.put("msg", msg);
        modelMap.put("newProject", project);
        return "admin/project/import";
    }

    private Project importProject(String zipFilename, byte[] fileBytes) throws Exception {

        // upload the zipfile to curriculum_base_dir
        String curriculumBaseDir = wiseProperties.getProperty("curriculum_base_dir");

        // make sure the curriculum_base_dir exists
        if (!new File(curriculumBaseDir).exists()) {
            throw new Exception("Curriculum upload directory \"" + curriculumBaseDir
                    + "\" does not exist. Please verify the path you specified for curriculum_base_dir in wise.properties.");
        }

        // save the upload zip file in the curriculum folder.
        String sep = "/";
        long timeInMillis = Calendar.getInstance().getTimeInMillis();
        String filename = zipFilename.substring(0, zipFilename.indexOf(".zip"));
        String newFilename = filename;
        if (new File(curriculumBaseDir + sep + filename).exists()) {
            // if this directory already exists, add a date time in milliseconds to the filename to make it unique
            newFilename = filename + "-" + timeInMillis;
        }
        String newFileFullPath = curriculumBaseDir + sep + newFilename + ".zip";

        // copy the zip file inside curriculum_base_dir temporarily
        File uploadedFile = new File(newFileFullPath);
        uploadedFile.createNewFile();
        FileCopyUtils.copy(fileBytes, uploadedFile);

        // make a new folder where the contents of the zip should go
        String newFileFullDir = curriculumBaseDir + sep + newFilename;
        File newFileFullDirFile = new File(newFileFullDir);
        newFileFullDirFile.mkdir();

        Integer projectVersion = 0;

        // unzip the zip file
        try {
            ZipFile zipFile = new ZipFile(newFileFullPath);
            Enumeration entries = zipFile.entries();

            int i = 0; // index used later to check for first folder in the zip file

            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();

                if (entry.getName().startsWith("__MACOSX")) {
                    // if this entry starts with __MACOSX, this zip file was created by a user using mac's "compress" feature.
                    // ignore it.
                    continue;
                }

                if (entry.isDirectory()) {
                    // first check to see if the user has changed the zip file name and therefore the zipfile name
                    // is no longer the same as the name of the first folder in the top-level of the zip file.
                    // if this is the case, import will fail, so throw an error.
                    if (i == 0) {
                        if (!entry.getName().startsWith(filename)) {
                            throw new Exception(
                                    "Zip file name \"" + entry.getName() + "\" does not match folder name \""
                                            + filename + "\". Do not change zip filename");
                        }
                        i++;
                    }

                    // Assume directories are stored parents first then children.
                    System.out.println("Extracting directory: " + entry.getName());
                    // This is not robust, just for demonstration purposes.
                    (new File(entry.getName().replace(filename, newFileFullDir))).mkdir();
                    continue;
                }

                if ("wise4.project.json".equals(entry.getName().substring(entry.getName().lastIndexOf(sep) + 1))) {
                    projectVersion = 4;
                } else if ("project.json".equals(entry.getName().substring(entry.getName().lastIndexOf(sep) + 1))) {
                    projectVersion = 5;
                }

                System.out.println("Extracting file: " + entry.getName());
                copyInputStream(zipFile.getInputStream(entry), new BufferedOutputStream(
                        new FileOutputStream(entry.getName().replaceFirst(filename, newFileFullDir))));
            }

            zipFile.close();
        } catch (IOException ioe) {
            System.err.println("Unhandled exception during project import. Project was not properly imported.");
            ioe.printStackTrace();
            throw ioe;
        }

        // remove the temp zip file
        uploadedFile.delete();

        // now create a project in the db
        String path = "";
        String name = "";

        // get the project path and name from zip file
        try {
            if (projectVersion == 4) {
                path = sep + newFilename + sep + "wise4.project.json";
                String projectJSONFilePath = newFileFullDir + sep + "wise4.project.json";
                String projectJSONStr = FileUtils.readFileToString(new File(projectJSONFilePath));
                JSONObject projectJSONObj = new JSONObject(projectJSONStr);
                name = projectJSONObj.getString("title");
            } else if (projectVersion == 5) {
                path = sep + newFilename + sep + "project.json";
                String projectJSONFilePath = newFileFullDir + sep + "project.json";
                String projectJSONStr = FileUtils.readFileToString(new File(projectJSONFilePath));
                JSONObject projectJSONObj = new JSONObject(projectJSONStr);
                name = projectJSONObj.getJSONObject("metadata").getString("title");
            } else if (projectVersion == 0) {
                System.err.println("Could not determine project version during project import.");
                return null;
            }
        } catch (Exception e) {
            // there was an error getting project title.
            name = "Undefined";
        }

        User signedInUser = ControllerUtil.getSignedInUser();

        ModuleParameters mParams = new ModuleParameters();
        mParams.setUrl(path);
        Curnit curnit = curnitService.createCurnit(mParams);

        ProjectParameters pParams = new ProjectParameters();
        pParams.setCurnitId(curnit.getId());
        pParams.setOwner(signedInUser);
        pParams.setProjectname(name);
        pParams.setProjectType(ProjectType.LD);
        pParams.setWiseVersion(projectVersion);

        ProjectMetadata metadata = null;

        // see if a file called wise4.project-meta.json exists. if yes, try parsing it.
        try {
            metadata = new ProjectMetadataImpl();
            if (projectVersion == 4) {
                String projectMetadataFilePath = newFileFullDir + sep + "wise4.project-meta.json";
                String projectMetadataStr = FileUtils.readFileToString(new File(projectMetadataFilePath));
                JSONObject metadataJSONObj = new JSONObject(projectMetadataStr);
                metadata.populateFromJSON(metadataJSONObj);
            } else if (projectVersion == 5) {
                String projectFilePath = newFileFullDir + sep + "project.json";
                String projectStr = FileUtils.readFileToString(new File(projectFilePath));
                JSONObject projectJSONObj = new JSONObject(projectStr);
                JSONObject metadataJSONObj = projectJSONObj.getJSONObject("metadata");
                metadata.populateFromJSON(metadataJSONObj);
            }
        } catch (Exception e) {
            // if there is any error during the parsing of the metadata, set the metadata to null
            System.err.println("Error parsing metadata while import project.");
            e.printStackTrace();
            metadata = null;
        }

        // If metadata is null at this point, either wise4.project-meta.json was not
        // found in the zip file, or there was an error parsing.
        // Set a new fresh metadata object
        if (metadata == null) {
            metadata = new ProjectMetadataImpl();
            metadata.setTitle(name);
        }

        pParams.setMetadata(metadata);

        return projectService.createProject(pParams);
    }

    @RequestMapping(value = "/admin/project/getImportableProjects", method = RequestMethod.GET)
    public void getImportableProjects(HttpServletResponse response) {
        try {
            URL url = new URL(getWISEProjectsURL);
            URLConnection conn = url.openConnection();
            InputStream in = conn.getInputStream();
            String projectsString = IOUtils.toString(in, "UTF-8");
            response.getWriter().print(projectsString);
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RequestMapping(value = "/admin/project/import", method = RequestMethod.GET)
    public void initializeForm(ModelMap modelMap) {

        modelMap.put("projectZipFile", new ProjectUpload());
    }

    public static final void copyInputStream(InputStream in, OutputStream out) throws IOException {
        byte[] buffer = new byte[1024];
        int len;

        while ((len = in.read(buffer)) >= 0)
            out.write(buffer, 0, len);

        in.close();
        out.close();
    }
}