From 9e5236c7121c84e178cdb509cef37d51c280800b Mon Sep 17 00:00:00 2001 From: Georgy Litvinov Date: Mon, 8 Nov 2021 21:25:39 +0100 Subject: [PATCH] Document upload --- .../freemarker/FileUploadController.java | 435 ++++++++++++++++++ .../vitro/webapp/dao/VitroVocabulary.java | 3 + .../filestorage/UploadedFileHelper.java | 16 + .../ObjectPropertyStatementTemplateModel.java | 8 + .../ObjectPropertyTemplateModel.java | 21 +- dependencies/pom.xml | 5 + .../config/example.runtime.properties | 5 + .../display/everytime/storedFileListView.n3 | 1 + .../rdf/tbox/filegraph/vitroPublic.owl | 9 + .../config/listViewConfig-storedFile.xml | 44 ++ .../webapp/js/fileUpload/fileUploadUtils.js | 61 +++ .../body/fileUpload/fileUpload-default.ftl | 32 ++ .../body/individual/individual-vitro.ftl | 2 + .../individual/storedFile-default.ftl | 12 + 14 files changed, 653 insertions(+), 1 deletion(-) create mode 100644 api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FileUploadController.java create mode 100644 home/src/main/resources/rdf/display/everytime/storedFileListView.n3 create mode 100644 webapp/src/main/webapp/config/listViewConfig-storedFile.xml create mode 100644 webapp/src/main/webapp/js/fileUpload/fileUploadUtils.js create mode 100644 webapp/src/main/webapp/templates/freemarker/body/fileUpload/fileUpload-default.ftl create mode 100644 webapp/src/main/webapp/templates/freemarker/body/partials/individual/storedFile-default.ftl diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FileUploadController.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FileUploadController.java new file mode 100644 index 000000000..71e3f555a --- /dev/null +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/controller/freemarker/FileUploadController.java @@ -0,0 +1,435 @@ +package edu.cornell.mannlib.vitro.webapp.controller.freemarker; + +import static edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest.UNAUTHORIZED; +import static edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.PARAMETER_UPLOADED_FILE; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jena.ontology.OntModel; +import org.apache.tika.Tika; +import org.apache.tika.mime.MimeType; +import org.apache.tika.mime.MimeTypeException; +import org.apache.tika.mime.MimeTypes; + +import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.RequestedAction; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.AddObjectPropertyStatement; +import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.propstmt.DropObjectPropertyStatement; +import edu.cornell.mannlib.vitro.webapp.beans.Individual; +import edu.cornell.mannlib.vitro.webapp.beans.Property; +import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties; +import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; +import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; +import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; +import edu.cornell.mannlib.vitro.webapp.filestorage.UploadedFileHelper; +import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo; +import edu.cornell.mannlib.vitro.webapp.i18n.I18n; +import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage; + +@WebServlet(name = "FileUploadController", urlPatterns = { "/uploadFile" }) +public class FileUploadController extends FreemarkerHttpServlet { + private static final String TEMPLATE_VAR_FORM_ACTION = "formAction"; + private static final String TEMPLATE_VAR_MAX_FILE_SIZE = "maxFileSize"; + private static final String PARAM_REFERRER = "referrer"; + private static final String TEMPLATE_VAR_SUPPORTED_TYPES = "supportedTypes"; + private static final String TEMPLATE_VAR_SUPPORTED_MIME_TYPES = "supportedMIMETypes"; + + private static final String TEMPLATE_VAR_ERROR_MESSAGE = "errorMessage"; + private static final String DEFAULT_FILE_NAME = "fileName"; + /** + * + */ + private static final Log log = LogFactory.getLog(FileUploadController.class); + private static final long serialVersionUID = 1L; + private static final String PARAMETER_ACTION = "action"; + private static final String ACTION_DELETE = "delete"; + private static final String ACTION_UPLOAD = "upload"; + private static final String PARAMETER_FILE_URI = "fileUri"; + private static final String PARAMETER_SUBJECT_URI = "subjectUri"; + private static final String CONFIG_MAX_FILE_SIZE = "fileUpload.maxFileSize"; + private static final String ALLOWED_MIME_TYPES = "fileUpload.allowedMIMETypes"; + private static final Long DEFAULT_FILE_SIZE = (long) (10 * 1024 * 1024); + private static final String PARAMETER_PREDICATE_URI = "predicateUri"; + private static final String DEFAULT_TEMPLATE = "fileUpload-default.ftl"; + private static Long maxFileSize = DEFAULT_FILE_SIZE; + private FileStorage fileStorage; + private Set allowedMimeTypes; + + @Override + public void init() throws ServletException { + super.init(); + fileStorage = ApplicationUtils.instance().getFileStorage(); + setMaxFileSize(); + setAllowedMimeTypes(); + } + + @Override + protected AuthorizationRequest requiredActions(VitroRequest vreq) { + RequestedAction ra; + try { + Property predicate = new Property(getPredicateUri(vreq)); + final OntModel jenaOntModel = vreq.getJenaOntModel(); + final String subject = getSubjectUri(vreq); + if (isUpload(vreq)) { + ra = new AddObjectPropertyStatement(jenaOntModel, subject, predicate,RequestedAction.SOME_URI); + } else { // delete + ra = new DropObjectPropertyStatement(jenaOntModel, subject, predicate, getFileUri(vreq)); + } + return ra; + } catch (Exception e) { + return UNAUTHORIZED; + } + } + + private String getFileUri(VitroRequest vreq) { + return vreq.getParameter(PARAMETER_FILE_URI); + } + + @Override + protected ResponseValues processRequest(VitroRequest vreq) { + try { + getReferrer(vreq); + validateRequest(vreq); + if (isUpload(vreq)) { + uploadFile(vreq); + } else if (isDelete(vreq)) { + deleteFile(vreq); + } + } catch (Exception e) { + return pageResponse(vreq, e.getMessage()); + } + return new RedirectResponseValues(getNextPageUrl(vreq), HttpServletResponse.SC_SEE_OTHER); + } + private String getNextPageUrl(VitroRequest vreq) { + return getReferrer(vreq); + } + + private TemplateResponseValues pageResponse(VitroRequest vreq, String error) { + TemplateResponseValues rv = new TemplateResponseValues(DEFAULT_TEMPLATE); + rv.put(PARAMETER_ACTION, vreq.getParameter(PARAMETER_ACTION)); + rv.put(TEMPLATE_VAR_FORM_ACTION, formatRequestUrl(vreq)); + if (!error.isEmpty()) { + rv.put(TEMPLATE_VAR_ERROR_MESSAGE, error); + } + rv.put(PARAM_REFERRER,getReferrer(vreq)); + rv.put(TEMPLATE_VAR_SUPPORTED_TYPES, printAllowedFileExtensions()); + rv.put(TEMPLATE_VAR_SUPPORTED_MIME_TYPES, printAllowedMimeTypes()); + rv.put(TEMPLATE_VAR_MAX_FILE_SIZE, maxFileSizeInMegabytes()); + return rv; + } + + private String formatRequestUrl(VitroRequest vreq) { + String result = vreq.getRequestURL().toString() + "?"; + String query = vreq.getQueryString(); + if (query != null) { + result += query; + } + String referrer = vreq.getParameter(PARAM_REFERRER); + if (referrer == null || referrer.isEmpty()) { + result += "&" + PARAM_REFERRER + "=" + getReferrer(vreq); + } + return result; + } + + private Double maxFileSizeInMegabytes() { + if (maxFileSize > 0) { + return ((double) maxFileSize/1048576); + } else { + return 0.0; + } + } + + private void validateRequest(VitroRequest vreq) throws FileUploadException { + if (isUpload(vreq)) { + validateUploadRequest(vreq); + } else if (isDelete(vreq)) { + validateDeleteRequest(vreq); + } else if (hasAction(vreq)) { + throw new FileUploadException("Only delete and upload actions supported"); + } else { + throw new FileUploadException("No action specified"); + } + } + + private void validateDeleteRequest(VitroRequest vreq) throws FileUploadException { + validateSubjectUri(vreq); + validatePredicateUri(vreq); + validateFileUri(vreq); + } + + private void validateUploadRequest(VitroRequest vreq) throws FileUploadException { + validateSubjectUri(vreq); + validatePredicateUri(vreq); + validateFile(vreq); + } + + private void validateFile(VitroRequest vreq) throws FileUploadException { + Map> map = vreq.getFiles(); + if (map == null) { + throw new FileUploadException("File to upload not found"); + } + List list = map.get(PARAMETER_UPLOADED_FILE); + if ((list == null) || list.isEmpty()) { + throw new FileUploadException("No file uploaded"); + } + FileItem file = list.get(0); + if (file.getSize() == 0) { + throw new FileUploadException("Uploaded file size is 0"); + } + if (file.getSize() > maxFileSize) { + throw new FileUploadException("Uploaded file is too big. Maximum file size is " + maxFileSize + + " bytes. Uploaded file is " + file.getSize() + " bytes."); + } + validateMimeType(file); + } + + private void validateMimeType(FileItem file) throws FileUploadException { + String mime = getMimeType(file); + if (mime.isEmpty()) { + throw new FileUploadException("File type is unrecognized"); + } + String extension = null; + MimeTypes types = MimeTypes.getDefaultMimeTypes(); + try { + MimeType mimeType = types.forName(mime); + extension = mimeType.getExtension(); + } catch (MimeTypeException e) { + log.error(e.getLocalizedMessage()); + } + if (extension == null || extension.isEmpty()) { + throw new FileUploadException("Extension for mime type " + mime + " not found"); + } + if (!allowedMimeTypes.contains(mime.toLowerCase())) { + log.error("File mime type is not allowed. " + printAllowedMimeTypes() + " Current mime type is " + mime); + throw new FileUploadException( + "File type is not allowed. Allowed file types: " + printAllowedFileExtensions() + + " Current file type is " + getExtension(mime).replaceAll("\\.", "")); + } + } + + private String printAllowedFileExtensions() { + StringBuilder sb = new StringBuilder(); + for (Iterator it = allowedMimeTypes.iterator(); it.hasNext();) { + String mimeType = (String) it.next(); + String extension = getExtension(mimeType); + sb.append(extension.replaceAll("\\.", "")); + if (it.hasNext()) { + sb.append(", "); + } else { + sb.append("."); + } + } + return sb.toString(); + } + + private String printAllowedMimeTypes() { + StringBuilder sb = new StringBuilder(); + for (Iterator it = allowedMimeTypes.iterator(); it.hasNext();) { + String mimeType = (String) it.next(); + sb.append(mimeType); + if (it.hasNext()) { + sb.append(", "); + } else { + sb.append("."); + } + } + return sb.toString(); + } + + private boolean hasAction(VitroRequest vreq) { + return vreq.getParameter(PARAMETER_ACTION) != null; + } + + private void deleteFile(VitroRequest vreq) { + String subjectUri = getSubjectUri(vreq); + String predicateUri = getPredicateUri(vreq); + String fileUri = getFileUri(vreq); + WebappDaoFactory webAppDaoFactory = vreq.getUnfilteredWebappDaoFactory(); + UploadedFileHelper fileHelper = new UploadedFileHelper(fileStorage, webAppDaoFactory, getServletContext()); + fileHelper.removeUploadedFile(subjectUri, predicateUri, fileUri); + } + + private void uploadFile(VitroRequest vreq) throws FileUploadException { + String subjectUri = getSubjectUri(vreq); + String predicateUri = getPredicateUri(vreq); + FileItem file = getUploadedFile(vreq); + String uploadedFileName = getUploadedFileName(file); + String storedFileName = createStoredFileName(file); + WebappDaoFactory webAppDaoFactory = vreq.getUnfilteredWebappDaoFactory(); + UploadedFileHelper fileHelper = new UploadedFileHelper(fileStorage, webAppDaoFactory, getServletContext()); + FileInfo fileInfo = createFile(file, storedFileName, fileHelper); + fileHelper.attachFileToSubject(fileInfo, subjectUri, predicateUri); + fileHelper.setPublicFileName(fileInfo, uploadedFileName); + } + + private FileInfo createFile(FileItem file, String storedFileName, UploadedFileHelper fileHelper) + throws FileUploadException { + FileInfo fileInfo = null; + try { + fileInfo = fileHelper.createFile(storedFileName, getMimeType(file), file.getInputStream()); + } catch (Exception e) { + log.error(e.getLocalizedMessage()); + throw new FileUploadException(e.getLocalizedMessage()); + } + return fileInfo; + } + + private String createStoredFileName(FileItem file) { + String mimeString = getMimeType(file); + int length = 64; + boolean useLetters = true; + boolean useNumbers = true; + String storedFileName = RandomStringUtils.random(length, useLetters, useNumbers) + getExtension(mimeString); + return storedFileName; + } + + private String getExtension(String mimeString) { + String extension = ""; + MimeTypes types = MimeTypes.getDefaultMimeTypes(); + try { + MimeType mime = types.forName(mimeString); + extension = mime.getExtension(); + } catch (MimeTypeException e) { + log.error(e.getLocalizedMessage()); + } + return extension; + } + + private String getUploadedFileName(FileItem file) { + String fileName = file.getName(); + if (fileName == null) { + return DEFAULT_FILE_NAME; + } else { + return FilenameUtils.getName(fileName); + } + } + + private String getMimeType(FileItem file) { + Tika tika = new Tika(); + InputStream is; + String mime = ""; + try { + is = file.getInputStream(); + mime = tika.detect(is); + } catch (IOException e) { + log.error(e.getLocalizedMessage()); + } + return mime; + } + + private void validateFileUri(VitroRequest vreq) throws FileUploadException { + String fileUri = getFileUri(vreq); + validateUriNotEmpty(fileUri,"file"); + validateIndividual(vreq, fileUri); + } + + private void validateSubjectUri(VitroRequest vreq) throws FileUploadException { + String subjectUri = getSubjectUri(vreq); + validateUriNotEmpty(subjectUri,"subject"); + validateIndividual(vreq, subjectUri); + } + + private void validatePredicateUri(VitroRequest vreq) throws FileUploadException { + String predicateUri = getPredicateUri(vreq); + validateUriNotEmpty(predicateUri,"predicate"); + validateIndividual(vreq, predicateUri); + } + + private void validateIndividual(VitroRequest vreq, String subjectUri) throws FileUploadException { + Individual subject = vreq.getUnfilteredWebappDaoFactory().getIndividualDao() + .getIndividualByURI(subjectUri); + if (subject == null) { + throw new FileUploadException("Uri " + subjectUri + "doesn't exist"); + } + } + + private void validateUriNotEmpty(String predicateUri,String name) throws FileUploadException { + if (predicateUri == null || predicateUri.trim().isEmpty()) { + throw new FileUploadException("No " + name + " uri was given"); + } + } + + private String getPredicateUri(VitroRequest vreq) { + return vreq.getParameter(PARAMETER_PREDICATE_URI); + } + + private String getSubjectUri(VitroRequest vreq) { + return vreq.getParameter(PARAMETER_SUBJECT_URI); + } + + private FileItem getUploadedFile(VitroRequest vreq) { + return vreq.getFiles().get(PARAMETER_UPLOADED_FILE).get(0); + } + + private boolean isUpload(VitroRequest vreq) { + String action = vreq.getParameter(PARAMETER_ACTION); + return ACTION_UPLOAD.equals(action); + } + + private boolean isDelete(VitroRequest vreq) { + String action = vreq.getParameter(PARAMETER_ACTION); + return ACTION_DELETE.equals(action); + } + + private void setAllowedMimeTypes() { + ConfigurationProperties config = ConfigurationProperties.getBean(getServletContext()); + String allowedTypes = config.getProperty(ALLOWED_MIME_TYPES, ""); + allowedMimeTypes = new HashSet(Arrays.asList(allowedTypes.toLowerCase().trim().split("\\s*,\\s*"))); + } + + private String getReferrer(VitroRequest vreq) { + String referrer = vreq.getParameter(PARAM_REFERRER); + if (referrer == null) { + referrer = vreq.getHeader("Referer"); + } + if (referrer == null) { + referrer = "/"; + } + return referrer; + } + + private void setMaxFileSize() { + ConfigurationProperties config = ConfigurationProperties.getBean(getServletContext()); + String configFileSize = config.getProperty(CONFIG_MAX_FILE_SIZE, DEFAULT_FILE_SIZE.toString()); + try { + maxFileSize = Long.parseLong(configFileSize); + } catch (NumberFormatException e) { + log.error("maxFileSize parsing failed"); + log.error(e); + } + } + + static class FileUploadException extends Exception { + private static final long serialVersionUID = 1L; + private final Object[] parameters; + + FileUploadException(String message, Object... parameters) { + super(message); + this.parameters = parameters; + } + + public String formatMessage(HttpServletRequest req) { + return I18n.text(req, getMessage(), parameters); + } + } +} diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java index fba900cad..a150b5b5a 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/dao/VitroVocabulary.java @@ -234,6 +234,9 @@ public class VitroVocabulary { public static final String IND_MAIN_IMAGE = VITRO_PUBLIC + "mainImage"; public static final String IND_IMAGE = VITRO_PUBLIC + "image"; + public static final String STORED_FILE = VITRO_PUBLIC + "storedFile"; + public static final String PUBLIC_FILENAME = VITRO_PUBLIC + "publicFilename"; + // =============== Date Time with Precision vocabulary =============== private static final String DATETIME_NS = "http://vivoweb.org/ontology/core#"; diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filestorage/UploadedFileHelper.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filestorage/UploadedFileHelper.java index 5f68c93ed..e8c7f1f67 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filestorage/UploadedFileHelper.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/filestorage/UploadedFileHelper.java @@ -253,5 +253,21 @@ public class UploadedFileHelper { } return !stmts.isEmpty(); } + + public void removeUploadedFile(String subjectUri, String predicateUri, String fileUri) { + FileInfo fileInfo = FileInfo.instanceFromSurrogateUri(wadf, fileUri); + objectPropertyStatementDao + .deleteObjectPropertyStatement(new ObjectPropertyStatementImpl(subjectUri, predicateUri, fileUri)); + deleteIfNotReferenced(fileInfo); + } + public void attachFileToSubject(FileInfo fileInfo, String subjectUri, String predicateUri) { + objectPropertyStatementDao + .insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(subjectUri, predicateUri, fileInfo.getUri())); + } + + public void setPublicFileName(FileInfo fileInfo, String uploadedFileName) { + dataPropertyStatementDao.insertNewDataPropertyStatement( + new DataPropertyStatementImpl(fileInfo.getUri(), VitroVocabulary.PUBLIC_FILENAME, uploadedFileName)); + } } diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java index 9eb91b8c0..da38f6ce9 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyStatementTemplateModel.java @@ -61,6 +61,10 @@ public class ObjectPropertyStatementTemplateModel extends PropertyStatementTempl if (VitroVocabulary.IND_MAIN_IMAGE.equals(property.getURI())) { return ObjectPropertyTemplateModel.getImageUploadUrl(subjectUri, "delete"); } + //If object is a File but not associated with main image + if (ObjectPropertyTemplateModel.isFileStoreProperty(property)) { + return ObjectPropertyTemplateModel.getDeleteFileUrl(subjectUri, property.getURI(), objectUri); + } ParamMap params = new ParamMap( "subjectUri", subjectUri, @@ -109,6 +113,10 @@ public class ObjectPropertyStatementTemplateModel extends PropertyStatementTempl if (VitroVocabulary.IND_MAIN_IMAGE.equals(property.getURI())) { return ObjectPropertyTemplateModel.getImageUploadUrl(subjectUri, "edit"); } + if (ObjectPropertyTemplateModel.isFileStoreProperty(property)) { + //Disable file editing + return ""; + } ParamMap params = new ParamMap( "subjectUri", subjectUri, diff --git a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java index 12864d28c..bb6e69bdc 100644 --- a/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java +++ b/api/src/main/java/edu/cornell/mannlib/vitro/webapp/web/templatemodels/individual/ObjectPropertyTemplateModel.java @@ -39,6 +39,7 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel private static final String TYPE = "object"; private static final String EDIT_PATH = "editRequestDispatch"; private static final String IMAGE_UPLOAD_PATH = "/uploadImages"; + private static final String FILE_UPLOAD_PATH = "/uploadFile"; private static final String END_DATE_TIME_VARIABLE = "dateTimeEnd"; private static final Pattern ORDER_BY_END_DATE_TIME_PATTERN = @@ -131,6 +132,8 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel if (propertyUri.equals(VitroVocabulary.IND_MAIN_IMAGE)) { addUrl = getImageUploadUrl(subjectUri, "add"); + } else if (isFileStoreProperty(property)) { + addUrl = getFileUploadUrl(subjectUri,property.getURI()); } else { ParamMap params = new ParamMap( "subjectUri", subjectUri, @@ -149,7 +152,7 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel } } - /** + /** * Pull this into a protected method so we can stub it out in the unit tests. * Other options: * 1) receive a TemplateLoader into the constructor of ObjectPropertyTemplateModel, @@ -378,4 +381,20 @@ public abstract class ObjectPropertyTemplateModel extends PropertyTemplateModel public abstract boolean isCollatedBySubclass(); + + protected static boolean isFileStoreProperty(Property property) { + return property.getRangeVClassURI() != null && property.getRangeVClassURI().equals(VitroVocabulary.FS_FILE_CLASS); + } + + public static String getDeleteFileUrl(String subjectUri, String predicateUri, String objectUri) { + ParamMap params = new ParamMap("subjectUri", subjectUri, "predicateUri", predicateUri, "fileUri", objectUri, + "action", "delete"); + return UrlBuilder.getUrl(FILE_UPLOAD_PATH, params); + } + + private static String getFileUploadUrl(String subjectUri, String predicateUri) { + ParamMap params = new ParamMap("subjectUri", subjectUri, "predicateUri", predicateUri, "action", "upload"); + return UrlBuilder.getUrl(FILE_UPLOAD_PATH, params); + } + } diff --git a/dependencies/pom.xml b/dependencies/pom.xml index a5ac32627..2de3bce23 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -256,6 +256,11 @@ taglibs-standard-impl 1.2.5 + + org.apache.tika + tika-core + 2.1.0 + com.sun.mail diff --git a/home/src/main/resources/config/example.runtime.properties b/home/src/main/resources/config/example.runtime.properties index 8e287ca0e..3f1e4d464 100644 --- a/home/src/main/resources/config/example.runtime.properties +++ b/home/src/main/resources/config/example.runtime.properties @@ -185,3 +185,8 @@ proxy.eligibleTypeList = http://www.w3.org/2002/07/owl#Thing # Vitro does not contain restricted data that should not be shared with others. # # tpf.activeFlag = true +# +# File upload file size in bytes. By default 1048576 bytes (10Mb) +#fileUpload.maxFileSize = 1048576 +#comma separated list of mime types allowed for upload +#fileUpload.allowedMIMETypes = image/png, application/pdf diff --git a/home/src/main/resources/rdf/display/everytime/storedFileListView.n3 b/home/src/main/resources/rdf/display/everytime/storedFileListView.n3 new file mode 100644 index 000000000..fe2e78abe --- /dev/null +++ b/home/src/main/resources/rdf/display/everytime/storedFileListView.n3 @@ -0,0 +1 @@ + "listViewConfig-storedFile.xml" . diff --git a/home/src/main/resources/rdf/tbox/filegraph/vitroPublic.owl b/home/src/main/resources/rdf/tbox/filegraph/vitroPublic.owl index 597799a9b..5d59ed823 100644 --- a/home/src/main/resources/rdf/tbox/filegraph/vitroPublic.owl +++ b/home/src/main/resources/rdf/tbox/filegraph/vitroPublic.owl @@ -43,6 +43,11 @@ + + + + + @@ -67,6 +72,10 @@ + + + + diff --git a/webapp/src/main/webapp/config/listViewConfig-storedFile.xml b/webapp/src/main/webapp/config/listViewConfig-storedFile.xml new file mode 100644 index 000000000..1101f3fcd --- /dev/null +++ b/webapp/src/main/webapp/config/listViewConfig-storedFile.xml @@ -0,0 +1,44 @@ + + + + + + + + + PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> + PREFIX vitro: <http://vitro.mannlib.cornell.edu/ns/vitro/0.7#> + PREFIX vitro-public: <http://vitro.mannlib.cornell.edu/ns/vitro/public#> + + SELECT ?subclass + ?object + ?filename + ?publicFilename + ?localName + ?url + WHERE { + ?subject ?property ?object . + LET (?localName := REPLACE(STR(?object),"^.*(#)(.*)$", "$2")) + ?object vitro-public:filename ?filename . + ?object vitro-public:publicFilename ?publicFilename . + ?object vitro-public:downloadLocation ?url . + + + OPTIONAL { + ?subject ?property ?object . + ?object a ?subclass . + # Require the subclasses retrieved to be in a classgroup, since others are not generally + # for display. See vivo-dev-all thread titled "Internal Entity and mostSpecificType," + # Aug 9-10, 2011. + # ?subclass vitro:inClassGroup ?classgroup + } + FILTER ( REPLACE(STR(?subclass),"^(.*)(#)(.*)$", "$1$2") != "http://vitro.mannlib.cornell.edu/ns/vitro/0.7#" ) + + + } ORDER BY ?subclass ASC( ?publicFilename ) ASC( ?localName ) + + + + diff --git a/webapp/src/main/webapp/js/fileUpload/fileUploadUtils.js b/webapp/src/main/webapp/js/fileUpload/fileUploadUtils.js new file mode 100644 index 000000000..ba4ff2f8c --- /dev/null +++ b/webapp/src/main/webapp/js/fileUpload/fileUploadUtils.js @@ -0,0 +1,61 @@ +/* $This file is distributed under the terms of the license in LICENSE$ */ + +$(document).ready(function(){ + + var xpath = "//attribute::href[contains(., '/uploadFile')]"; + var result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null); + var node = null; + while (node = result.iterateNext()) { + if(isDeleteUploadFile(node)){ + $(node.ownerElement).click(function(){ + var answer = confirm(i18n_confirmDeleteUploadedFile); + return answer; + }); + } else if (isUploadFile(node)){ + $(node.ownerElement).click(function(){ + uploadFileRequest(event.target); + return false; + }); + } + } +}); + +function isDeleteUploadFile(node){ + var url = node.nodeValue; + if (url.match("&action=delete")){ + return true; + } + return false; +} + +function isUploadFile(node){ + var url = node.nodeValue; + if (url.match("&action=upload")){ + return true; + } + return false; +} + +function uploadFileRequest(node){ + var aElement = node.parentElement; + var form = document.createElement("form"); + form.setAttribute("method", "post"); + form.setAttribute("action", aElement.href); + form.setAttribute("enctype","multipart/form-data"); + form.setAttribute("role","form"); + document.body.insertBefore(form, null); + var inputFile = document.createElement("input"); + inputFile.type = "file"; + inputFile.name = "datafile"; + var inputId = "fileUploadInput" + Math.floor(Math.random() * 1000000); + inputFile.setAttribute("id", inputId); + inputFile.setAttribute("style", "display:none;"); + form.insertBefore(inputFile, null); + inputFile.click(); + inputFile.addEventListener("change", onFileSelect); +} + +function onFileSelect(e) { + e.target.parentElement.submit(); + } + diff --git a/webapp/src/main/webapp/templates/freemarker/body/fileUpload/fileUpload-default.ftl b/webapp/src/main/webapp/templates/freemarker/body/fileUpload/fileUpload-default.ftl new file mode 100644 index 000000000..dc9cea267 --- /dev/null +++ b/webapp/src/main/webapp/templates/freemarker/body/fileUpload/fileUpload-default.ftl @@ -0,0 +1,32 @@ +<#-- $This file is distributed under the terms of the license in LICENSE$ --> + + +${scripts.add('')} + +<#assign i18n = i18n() > + +
+

${i18n.file_upload_heading}

+ + <#if errorMessage??> + + + <#if action?? && action == "upload" > +
+ + + <#if supportedMIMETypes??> + MIME Types: ${supportedMIMETypes} + + +

${i18n.maximum_file_size(maxFileSize)}

+ + + ${i18n.or} ${i18n.cancel_link} +
+ +
diff --git a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl index 2817b59d3..caf9b60a7 100644 --- a/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl +++ b/webapp/src/main/webapp/templates/freemarker/body/individual/individual-vitro.ftl @@ -106,9 +106,11 @@ ${headScripts.add('')} ${scripts.add('', + '', '', '')} diff --git a/webapp/src/main/webapp/templates/freemarker/body/partials/individual/storedFile-default.ftl b/webapp/src/main/webapp/templates/freemarker/body/partials/individual/storedFile-default.ftl new file mode 100644 index 000000000..32164e7cb --- /dev/null +++ b/webapp/src/main/webapp/templates/freemarker/body/partials/individual/storedFile-default.ftl @@ -0,0 +1,12 @@ +<#-- $This file is distributed under the terms of the license in LICENSE$ --> + +<#-- Default object property statement template. + + This template must be self-contained and not rely on other variables set for the individual page, because it + is also used to generate the property statement during a deletion. + --> +<@showFiles statement individual /> + +<#macro showFiles statement individual> + ${statement.publicFilename} +