Skip to content

Commit

Permalink
Upload documents in Vitro|VIVO (#251)
Browse files Browse the repository at this point in the history
* feat: introduced file upload

* fix: added file upload config settings

* fix: reverted auto formatting of upload file helper

* fix: replaced missed condition

* Improved naming, internationalization, removed file extensions as misleading information

* Fixed wrong bytes number representing 10Mb

* fix: show no media types allowed if no media types defined in runtime.properties

* fix: Add vitro-languages-home-core as installer dependency to have Vitro RDF internationalization files

* fix: change vitro-languages-core-home dependency type from tar.gz to pom
  • Loading branch information
litvinovg committed Apr 14, 2022
1 parent eff04e0 commit ebc9237
Show file tree
Hide file tree
Showing 15 changed files with 642 additions and 1 deletion.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -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#";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,21 @@ private boolean isFileReferenced(String surrogateUri) {
}
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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ private String makeDeleteUrl() {
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,
Expand Down Expand Up @@ -109,6 +113,10 @@ private String makeEditUrl() {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -131,6 +132,8 @@ protected void setAddUrl(Property property) {

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,
Expand All @@ -149,7 +152,7 @@ protected void setAddUrl(Property property) {
}
}

/**
/**
* 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,
Expand Down Expand Up @@ -378,4 +381,20 @@ public String getTemplate() {

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);
}

}
5 changes: 5 additions & 0 deletions dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,11 @@
<artifactId>taglibs-standard-impl</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>com.sun.mail</groupId>
Expand Down
5 changes: 5 additions & 0 deletions home/src/main/resources/config/example.runtime.properties
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,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 10485760 bytes (10Mb)
#fileUpload.maxFileSize = 10485760
#comma separated list of mime types allowed for upload
#fileUpload.allowedMIMETypes = image/png, application/pdf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<http://vitro.mannlib.cornell.edu/ns/vitro/public#storedFile> <http://vitro.mannlib.cornell.edu/ontologies/display/1.1#listViewConfigFile> "listViewConfig-storedFile.xml" .
9 changes: 9 additions & 0 deletions home/src/main/resources/rdf/tbox/filegraph/vitroPublic.owl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<owl:DatatypeProperty rdf:about="#publicFilename">
<rdfs:domain><owl:Class rdf:about="#File"/></rdfs:domain>
<rdfs:range rdf:resource="&xsd;string"/>
</owl:DatatypeProperty>

<owl:DatatypeProperty rdf:about="#attribution">
<rdfs:domain><owl:Class rdf:about="#File"/></rdfs:domain>
<rdfs:range rdf:resource="&xsd;string"/>
Expand All @@ -67,6 +72,10 @@
<rdfs:range><owl:Class rdf:about="#File"/></rdfs:range>
</owl:ObjectProperty>

<owl:ObjectProperty rdf:about="#storedFile">
<rdfs:range><owl:Class rdf:about="#File"/></rdfs:range>
</owl:ObjectProperty>

<owl:ObjectProperty rdf:about="#image">
<rdfs:range><owl:Class rdf:about="#File"/></rdfs:range>
</owl:ObjectProperty>
Expand Down
6 changes: 6 additions & 0 deletions installer/home/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,11 @@
<artifactId>vitro-home</artifactId>
<type>tar.gz</type>
</dependency>
<dependency>
<groupId>org.vivoweb</groupId>
<artifactId>vitro-languages-home-core</artifactId>
<version>${project.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</project>
44 changes: 44 additions & 0 deletions webapp/src/main/webapp/config/listViewConfig-storedFile.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- $This file is distributed under the terms of the license in LICENSE$ -->

<!-- Default list view config file for object properties
See guidelines at https://wiki.duraspace.org/x/eYXVAw -->

<list-view-config>
<query-select>

PREFIX rdfs: &lt;http://www.w3.org/2000/01/rdf-schema#&gt;
PREFIX vitro: &lt;http://vitro.mannlib.cornell.edu/ns/vitro/0.7#&gt;
PREFIX vitro-public: &lt;http://vitro.mannlib.cornell.edu/ns/vitro/public#&gt;

SELECT <collated> ?subclass </collated>
?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 .

<collated>
OPTIONAL {
<precise-subquery>?subject ?property ?object .</precise-subquery>
?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#" )
</collated>

} ORDER BY <collated> ?subclass </collated> ASC( ?publicFilename ) ASC( ?localName )
</query-select>

<template>storedFile-default.ftl</template>
</list-view-config>
61 changes: 61 additions & 0 deletions webapp/src/main/webapp/js/fileUpload/fileUploadUtils.js
Original file line number Diff line number Diff line change
@@ -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();
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<#-- $This file is distributed under the terms of the license in LICENSE$ -->


${scripts.add('<script type="text/javascript" src="${urls.base}/js/fileUpload/fileUploadUtils.js"></script>')}

<#assign i18n = i18n() >

<section id="fileploadContainer" role="region">
<h2>${i18n.file_upload_heading}</h2>

<#if errorMessage??>
<section id="error-alert" role="alert"><img src="${urls.images}/iconAlert.png" alt="${i18n.alt_error_alert}" />
<p>${errorMessage}</p>
</section>
</#if>
<#if !supportedMediaTypes?has_content>
<section id="error-alert" role="alert"><img src="${urls.images}/iconAlert.png" alt="${i18n.alt_error_alert}" />
<p>${i18n.file_upload_no_allowed_media}</p>
</section>
</#if>
<#if action?? && action == "upload" >
<form id="fileUploadForm" action="${formAction}" enctype="multipart/form-data" method="post" role="form">
<#if supportedMediaTypes?has_content>
<label>${i18n.file_upload_supported_media}</label>
<p>${supportedMediaTypes}</p>
</#if>
<input id="datafile" type="file" name="datafile" size="30" />
<p class="note">${i18n.maximum_file_size(maxFileSize)}</p>
<input class="submit" type="submit" value="${i18n.file_upload_submit_label}"/>
<span class="or"> ${i18n.or} <a class="cancel" href="${referrer}" title="${i18n.cancel_title}">${i18n.cancel_link}</a></span>
</form>
</#if>
</section>
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,11 @@ ${headScripts.add('<script type="text/javascript" src="${urls.base}/js/jquery_pl
'<script type="text/javascript" src="${urls.base}/js/tiny_mce/tiny_mce.js"></script>')}

${scripts.add('<script type="text/javascript" src="${urls.base}/js/imageUpload/imageUploadUtils.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/fileUpload/fileUploadUtils.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/individual/moreLessController.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/individual/individualUriRdf.js"></script>')}

<script type="text/javascript">
i18n_confirmDelete = "${i18n().confirm_delete?js_string}";
i18n_confirmDeleteUploadedFile = "${i18n().file_upload_confirm_delete?js_string}";
</script>
Original file line number Diff line number Diff line change
@@ -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>
<a download="${statement.publicFilename}" title="${i18n().name}" href="${profileUrl(statement.url)}" >${statement.publicFilename}</a>
</#macro>

0 comments on commit ebc9237

Please sign in to comment.