NIHVIVO-157 manual merge of the changes from branch NIHVIVO-157-file-storage
This commit is contained in:
parent
083aa4e530
commit
fb7d5bbba9
51 changed files with 3535 additions and 1198 deletions
|
@ -38,7 +38,6 @@ public class EntitySerializer implements Serializer {
|
||||||
public static final String IMAGEFILE_MBER= "imageFile";
|
public static final String IMAGEFILE_MBER= "imageFile";
|
||||||
public static final String ANCHOR_MBER= "anchor";
|
public static final String ANCHOR_MBER= "anchor";
|
||||||
public static final String BLURB_MBER= "blurb";
|
public static final String BLURB_MBER= "blurb";
|
||||||
public static final String IMAGETHUMB_MBER= "imageThumb";
|
|
||||||
public static final String CITATION_MBER= "citation";
|
public static final String CITATION_MBER= "citation";
|
||||||
public static final String STATUS_MBER= "status";
|
public static final String STATUS_MBER= "status";
|
||||||
public static final String PROPERTYLIST_MBER= "propertyList";
|
public static final String PROPERTYLIST_MBER= "propertyList";
|
||||||
|
@ -82,10 +81,9 @@ public class EntitySerializer implements Serializer {
|
||||||
context.serialize(new QName("", SUNRISE_MBER), null, ent.getSunrise());
|
context.serialize(new QName("", SUNRISE_MBER), null, ent.getSunrise());
|
||||||
context.serialize(new QName("", SUNSET_MBER), null, ent.getSunset());
|
context.serialize(new QName("", SUNSET_MBER), null, ent.getSunset());
|
||||||
context.serialize(new QName("", TIMEKEY_MBER), null, ent.getTimekey());
|
context.serialize(new QName("", TIMEKEY_MBER), null, ent.getTimekey());
|
||||||
context.serialize(new QName("", IMAGEFILE_MBER), null, ent.getImageFile());
|
context.serialize(new QName("", IMAGEFILE_MBER), null, ent.getMainImageUri());
|
||||||
context.serialize(new QName("", ANCHOR_MBER), null, ent.getAnchor());
|
context.serialize(new QName("", ANCHOR_MBER), null, ent.getAnchor());
|
||||||
context.serialize(new QName("", BLURB_MBER), null, ent.getBlurb());
|
context.serialize(new QName("", BLURB_MBER), null, ent.getBlurb());
|
||||||
context.serialize(new QName("", IMAGETHUMB_MBER), null, ent.getImageThumb());
|
|
||||||
context.serialize(new QName("", CITATION_MBER), null, ent.getCitation());
|
context.serialize(new QName("", CITATION_MBER), null, ent.getCitation());
|
||||||
context.serialize(new QName("", STATUS_MBER), null, ent.getStatus());
|
context.serialize(new QName("", STATUS_MBER), null, ent.getStatus());
|
||||||
context.serialize(new QName("", LINKSLIST_MBER), null, ent.getLinksList());
|
context.serialize(new QName("", LINKSLIST_MBER), null, ent.getLinksList());
|
||||||
|
|
|
@ -37,3 +37,4 @@ log4j.logger.org.apache.catalina=INFO
|
||||||
log4j.logger.org.diretwebremoting=ERROR
|
log4j.logger.org.diretwebremoting=ERROR
|
||||||
|
|
||||||
log4j.logger.edu.cornell.mannlib.vitro.webapp.ConfigurationProperties=INFO
|
log4j.logger.edu.cornell.mannlib.vitro.webapp.ConfigurationProperties=INFO
|
||||||
|
log4j.logger.edu.cornell.mannlib.vitro.webapp.filestorage.updater.FileStorageUpdater=INFO
|
||||||
|
|
|
@ -74,13 +74,26 @@
|
||||||
|
|
||||||
<!-- Invokes process to perform updates to align with ontology changes if needed -->
|
<!-- Invokes process to perform updates to align with ontology changes if needed -->
|
||||||
<!-- Needs to run before submodels are attached and Pellet is set up -->
|
<!-- Needs to run before submodels are attached and Pellet is set up -->
|
||||||
|
|
||||||
<listener>
|
<listener>
|
||||||
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase</listener-class>
|
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
|
|
||||||
<!-- Attaching submodels permits extra RDF files to be made visible without storing the data in the DB. -->
|
<listener>
|
||||||
|
<listener-class>
|
||||||
|
edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup
|
||||||
|
</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
<!-- Invokes a process to move any uploaded files into the new file storage system. -->
|
||||||
|
<!-- Needs to run after FileStorageSetup and JenaDataSourceSetup. -->
|
||||||
|
<!-- Should run before Pellet is set up. -->
|
||||||
|
<listener>
|
||||||
|
<listener-class>
|
||||||
|
edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateUploadedFiles
|
||||||
|
</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
|
<!-- Attaching submodels permits extra RDF files to be made visible without storing the data in the DB. -->
|
||||||
<listener>
|
<listener>
|
||||||
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.AttachSubmodels</listener-class>
|
<listener-class>edu.cornell.mannlib.vitro.webapp.servlet.setup.AttachSubmodels</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
|
@ -144,9 +157,10 @@
|
||||||
</listener-class>
|
</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
|
|
||||||
|
<!-- On shutdown, this will kill the background thread started by Apache Commons File Upload -->
|
||||||
<listener>
|
<listener>
|
||||||
<listener-class>
|
<listener-class>
|
||||||
edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup
|
org.apache.commons.fileupload.servlet.FileCleanerCleanup
|
||||||
</listener-class>
|
</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
|
|
||||||
|
@ -166,6 +180,7 @@
|
||||||
</listener>
|
</listener>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|
||||||
<!-- Filters ********************************************************** -->
|
<!-- Filters ********************************************************** -->
|
||||||
<!-- in 2.4 spec, filter chain order is first by filter-mapping <url-pattern> order in web.xml,
|
<!-- in 2.4 spec, filter chain order is first by filter-mapping <url-pattern> order in web.xml,
|
||||||
then filter-mapping <servlet-name> order in web.xml -->
|
then filter-mapping <servlet-name> order in web.xml -->
|
||||||
|
@ -1053,14 +1068,17 @@
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>uploadImages</servlet-name>
|
<servlet-name>uploadImages</servlet-name>
|
||||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.UploadImagesServlet</servlet-class>
|
<servlet-class>edu.cornell.mannlib.vitro.webapp.controller.edit.UploadImagesServlet</servlet-class>
|
||||||
<init-param>
|
|
||||||
<param-name>workspaceDir</param-name>
|
|
||||||
<!-- This is the source directory, so can't be automatically prepended with something like /usr/local/tomcat/webapps/vivo -->
|
|
||||||
<param-value>/usr/local/src/Vitro/dream/common/web</param-value>
|
|
||||||
</init-param>
|
|
||||||
<!--load-on-startup>2</load-on-startup-->
|
|
||||||
</servlet>
|
</servlet>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>serveFiles</servlet-name>
|
||||||
|
<servlet-class>edu.cornell.mannlib.vitro.webapp.filestorage.serving.FileServingServlet</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>serveFiles</servlet-name>
|
||||||
|
<url-pattern>/file/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>generic_editprep</servlet-name>
|
<servlet-name>generic_editprep</servlet-name>
|
||||||
<servlet-class>edu.cornell.mannlib.vitro.webapp.GenericDBEditPrep</servlet-class>
|
<servlet-class>edu.cornell.mannlib.vitro.webapp.GenericDBEditPrep</servlet-class>
|
||||||
|
|
|
@ -109,8 +109,7 @@ public class CuratorEditingPolicy implements VisitingPolicyIface {
|
||||||
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
|
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
|
||||||
|
|
||||||
this.editableVitroUris.add(VitroVocabulary.CITATION);
|
this.editableVitroUris.add(VitroVocabulary.CITATION);
|
||||||
this.editableVitroUris.add(VitroVocabulary.IMAGEFILE);
|
this.editableVitroUris.add(VitroVocabulary.IND_MAIN_IMAGE);
|
||||||
this.editableVitroUris.add(VitroVocabulary.IMAGETHUMB);
|
|
||||||
|
|
||||||
this.editableVitroUris.add(VitroVocabulary.LINK);
|
this.editableVitroUris.add(VitroVocabulary.LINK);
|
||||||
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);
|
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);
|
||||||
|
|
|
@ -112,8 +112,7 @@ public class DbAdminEditingPolicy implements VisitingPolicyIface {
|
||||||
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
|
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
|
||||||
|
|
||||||
this.editableVitroUris.add(VitroVocabulary.CITATION);
|
this.editableVitroUris.add(VitroVocabulary.CITATION);
|
||||||
this.editableVitroUris.add(VitroVocabulary.IMAGEFILE);
|
this.editableVitroUris.add(VitroVocabulary.IND_MAIN_IMAGE);
|
||||||
this.editableVitroUris.add(VitroVocabulary.IMAGETHUMB);
|
|
||||||
|
|
||||||
this.editableVitroUris.add(VitroVocabulary.LINK);
|
this.editableVitroUris.add(VitroVocabulary.LINK);
|
||||||
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);
|
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);
|
||||||
|
|
|
@ -110,8 +110,7 @@ public class EditorEditingPolicy implements VisitingPolicyIface{
|
||||||
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
|
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
|
||||||
|
|
||||||
this.editableVitroUris.add(VitroVocabulary.CITATION);
|
this.editableVitroUris.add(VitroVocabulary.CITATION);
|
||||||
this.editableVitroUris.add(VitroVocabulary.IMAGEFILE);
|
this.editableVitroUris.add(VitroVocabulary.IND_MAIN_IMAGE);
|
||||||
this.editableVitroUris.add(VitroVocabulary.IMAGETHUMB);
|
|
||||||
|
|
||||||
this.editableVitroUris.add(VitroVocabulary.LINK);
|
this.editableVitroUris.add(VitroVocabulary.LINK);
|
||||||
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);
|
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);
|
||||||
|
|
|
@ -5,8 +5,6 @@ package edu.cornell.mannlib.vitro.webapp.auth.policy;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -110,8 +108,7 @@ public class SelfEditingPolicy implements VisitingPolicyIface {
|
||||||
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
|
this.editableVitroUris.add(VitroVocabulary.TIMEKEY);
|
||||||
|
|
||||||
this.editableVitroUris.add(VitroVocabulary.CITATION);
|
this.editableVitroUris.add(VitroVocabulary.CITATION);
|
||||||
this.editableVitroUris.add(VitroVocabulary.IMAGEFILE);
|
this.editableVitroUris.add(VitroVocabulary.IND_MAIN_IMAGE);
|
||||||
this.editableVitroUris.add(VitroVocabulary.IMAGETHUMB);
|
|
||||||
|
|
||||||
this.editableVitroUris.add(VitroVocabulary.LINK);
|
this.editableVitroUris.add(VitroVocabulary.LINK);
|
||||||
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);
|
this.editableVitroUris.add(VitroVocabulary.PRIMARY_LINK);
|
||||||
|
|
|
@ -93,11 +93,11 @@ public interface Individual extends ResourceBean, VitroTimeWindowedResource, Com
|
||||||
String getStatus();
|
String getStatus();
|
||||||
void setStatus(String s);
|
void setStatus(String s);
|
||||||
|
|
||||||
String getImageFile();
|
void setMainImageUri(String mainImageUri);
|
||||||
void setImageFile(String imageFile);
|
String getMainImageUri();
|
||||||
|
|
||||||
String getImageThumb();
|
String getImageUrl();
|
||||||
void setImageThumb(String imageThumb);
|
String getThumbUrl();
|
||||||
|
|
||||||
String getUrl();
|
String getUrl();
|
||||||
void setUrl(String url);
|
void setUrl(String url);
|
||||||
|
|
|
@ -14,6 +14,14 @@ import java.util.*;
|
||||||
* Represents a single entity record.
|
* Represents a single entity record.
|
||||||
*/
|
*/
|
||||||
public class IndividualImpl extends BaseResourceBean implements Individual, Comparable<Individual> {
|
public class IndividualImpl extends BaseResourceBean implements Individual, Comparable<Individual> {
|
||||||
|
/**
|
||||||
|
* This can be used as a "not initialized" indicator for a property that
|
||||||
|
* could validly be set to <code>null</code>. If <code>get()</code> is
|
||||||
|
* called on such a property, and the property has this value, the correct
|
||||||
|
* value can be fetched and cached.
|
||||||
|
*/
|
||||||
|
protected static final String NOT_INITIALIZED = "__%NOT_INITIALIZED%__";
|
||||||
|
|
||||||
public String name = null;
|
public String name = null;
|
||||||
public String vClassURI = null;
|
public String vClassURI = null;
|
||||||
protected VClass vClass = null;
|
protected VClass vClass = null;
|
||||||
|
@ -35,10 +43,11 @@ public class IndividualImpl extends BaseResourceBean implements Individual, Comp
|
||||||
protected String moniker = null;
|
protected String moniker = null;
|
||||||
protected String url = null;
|
protected String url = null;
|
||||||
protected String description = null;
|
protected String description = null;
|
||||||
protected String imageFile = null;
|
|
||||||
protected String anchor = null;
|
protected String anchor = null;
|
||||||
protected String blurb = null;
|
protected String blurb = null;
|
||||||
protected String imageThumb = null;
|
protected String mainImageUri = NOT_INITIALIZED;
|
||||||
|
protected String imageUrl;
|
||||||
|
protected String thumbUrl;
|
||||||
protected String citation = null;
|
protected String citation = null;
|
||||||
protected int statusId = 0;
|
protected int statusId = 0;
|
||||||
protected String status = null;
|
protected String status = null;
|
||||||
|
@ -249,18 +258,29 @@ public class IndividualImpl extends BaseResourceBean implements Individual, Comp
|
||||||
public String getStatus() {return status;}
|
public String getStatus() {return status;}
|
||||||
public void setStatus(String s) {status=s; }
|
public void setStatus(String s) {status=s; }
|
||||||
|
|
||||||
public String getImageFile() {
|
|
||||||
return imageFile;
|
@Override
|
||||||
|
public String getMainImageUri() {
|
||||||
|
return (mainImageUri == NOT_INITIALIZED) ? null : mainImageUri;
|
||||||
}
|
}
|
||||||
public void setImageFile(String imageFile) {
|
|
||||||
this.imageFile = imageFile;
|
@Override
|
||||||
|
public void setMainImageUri(String mainImageUri) {
|
||||||
|
this.mainImageUri = mainImageUri;
|
||||||
|
this.imageUrl = null;
|
||||||
|
this.thumbUrl = null;
|
||||||
}
|
}
|
||||||
public String getImageThumb() {
|
|
||||||
return imageThumb;
|
@Override
|
||||||
|
public String getImageUrl() {
|
||||||
|
return "imageUrl";
|
||||||
}
|
}
|
||||||
public void setImageThumb(String imageThumb) {
|
|
||||||
this.imageThumb = imageThumb;
|
@Override
|
||||||
|
public String getThumbUrl() {
|
||||||
|
return "thumbUrl";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUrl() {
|
public String getUrl() {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import com.hp.hpl.jena.ontology.OntModel;
|
||||||
import com.hp.hpl.jena.rdf.model.Literal;
|
import com.hp.hpl.jena.rdf.model.Literal;
|
||||||
import com.hp.hpl.jena.rdf.model.Model;
|
import com.hp.hpl.jena.rdf.model.Model;
|
||||||
import com.hp.hpl.jena.rdf.model.ModelFactory;
|
import com.hp.hpl.jena.rdf.model.ModelFactory;
|
||||||
import com.hp.hpl.jena.rdf.model.ModelMaker;
|
|
||||||
import com.hp.hpl.jena.rdf.model.Property;
|
import com.hp.hpl.jena.rdf.model.Property;
|
||||||
import com.hp.hpl.jena.rdf.model.RDFNode;
|
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||||
import com.hp.hpl.jena.rdf.model.Resource;
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
@ -42,6 +41,10 @@ import edu.cornell.mannlib.vitro.webapp.beans.Portal;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
|
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQuery;
|
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQuery;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
|
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
|
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
|
||||||
|
@ -98,6 +101,13 @@ public class EntityController extends VitroHttpServlet {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is an uploaded file, redirect to its "alias URL".
|
||||||
|
String aliasUrl = getAliasUrlForBytestreamIndividual(indiv);
|
||||||
|
if (aliasUrl != null) {
|
||||||
|
res.sendRedirect(req.getContextPath() + aliasUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
doHtml( vreq, res , indiv);
|
doHtml( vreq, res , indiv);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -477,6 +487,47 @@ public class EntityController extends VitroHttpServlet {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this entity represents a File Bytestream, get its alias URL so we can
|
||||||
|
* properly serve the file contents.
|
||||||
|
*/
|
||||||
|
private String getAliasUrlForBytestreamIndividual(Individual entity)
|
||||||
|
throws IOException {
|
||||||
|
if (!FileModelHelper.isFileBytestream(entity)) {
|
||||||
|
log.debug("Entity at '" + entity.getURI()
|
||||||
|
+ "' is not recognized as a FileByteStream.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStorage fs = (FileStorage) getServletContext().getAttribute(
|
||||||
|
FileStorageSetup.ATTRIBUTE_NAME);
|
||||||
|
if (fs == null) {
|
||||||
|
log.error("Servlet context does not contain file storage at '"
|
||||||
|
+ FileStorageSetup.ATTRIBUTE_NAME + "'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filename = fs.getFilename(entity.getURI());
|
||||||
|
if (filename == null) {
|
||||||
|
log.error("Entity at '" + entity.getURI()
|
||||||
|
+ "' is recognized as a FileByteStream, "
|
||||||
|
+ "but the file system does not recognize it.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String url = FileServingHelper.getBytestreamAliasUrl(entity.getURI(),
|
||||||
|
filename);
|
||||||
|
if (url.equals(entity.getURI())) {
|
||||||
|
log.error("Entity at '" + entity.getURI()
|
||||||
|
+ "' is recognized as a FileByteStream, "
|
||||||
|
+ "but can't be translated to an alias URL.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Alias URL for '" + entity.getURI() + "' is '" + url + "'");
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth ) {
|
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth ) {
|
||||||
Resource subj = newModel.getResource(entity.getURI());
|
Resource subj = newModel.getResource(entity.getURI());
|
||||||
|
|
||||||
|
|
|
@ -116,8 +116,6 @@ public class CloneEntityServlet extends BaseEditController {
|
||||||
ind.setBlurb("");
|
ind.setBlurb("");
|
||||||
ind.setDescription("");
|
ind.setDescription("");
|
||||||
ind.setCitation("");
|
ind.setCitation("");
|
||||||
ind.setImageFile("");
|
|
||||||
ind.setImageThumb("");
|
|
||||||
|
|
||||||
String cloneURI=individualDao.insertNewIndividual(ind);
|
String cloneURI=individualDao.insertNewIndividual(ind);
|
||||||
if (cloneURI == null){ log.error("Error inserting cloned individual"); return; }
|
if (cloneURI == null){ log.error("Error inserting cloned individual"); return; }
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -43,6 +43,10 @@ import edu.cornell.mannlib.vitro.webapp.controller.Controllers;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
|
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQuery;
|
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQuery;
|
||||||
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
|
import edu.cornell.mannlib.vitro.webapp.search.beans.VitroQueryWrapper;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
|
import edu.cornell.mannlib.vitro.webapp.utils.NamespaceMapper;
|
||||||
|
@ -109,6 +113,13 @@ public class IndividualController extends FreeMarkerHttpServlet {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is an uploaded file, redirect to its "alias URL".
|
||||||
|
String aliasUrl = getAliasUrlForBytestreamIndividual(indiv);
|
||||||
|
if (aliasUrl != null) {
|
||||||
|
res.sendRedirect(req.getContextPath() + aliasUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
doHtml( vreq, res , indiv);
|
doHtml( vreq, res , indiv);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -488,6 +499,47 @@ public class IndividualController extends FreeMarkerHttpServlet {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this entity represents a File Bytestream, get its alias URL so we can
|
||||||
|
* properly serve the file contents.
|
||||||
|
*/
|
||||||
|
private String getAliasUrlForBytestreamIndividual(Individual entity)
|
||||||
|
throws IOException {
|
||||||
|
if (!FileModelHelper.isFileBytestream(entity)) {
|
||||||
|
log.debug("Entity at '" + entity.getURI()
|
||||||
|
+ "' is not recognized as a FileByteStream.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStorage fs = (FileStorage) getServletContext().getAttribute(
|
||||||
|
FileStorageSetup.ATTRIBUTE_NAME);
|
||||||
|
if (fs == null) {
|
||||||
|
log.error("Servlet context does not contain file storage at '"
|
||||||
|
+ FileStorageSetup.ATTRIBUTE_NAME + "'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filename = fs.getFilename(entity.getURI());
|
||||||
|
if (filename == null) {
|
||||||
|
log.error("Entity at '" + entity.getURI()
|
||||||
|
+ "' is recognized as a FileByteStream, "
|
||||||
|
+ "but the file system does not recognize it.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String url = FileServingHelper.getBytestreamAliasUrl(entity.getURI(),
|
||||||
|
filename);
|
||||||
|
if (url.equals(entity.getURI())) {
|
||||||
|
log.error("Entity at '" + entity.getURI()
|
||||||
|
+ "' is recognized as a FileByteStream, "
|
||||||
|
+ "but can't be translated to an alias URL.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Alias URL for '" + entity.getURI() + "' is '" + url + "'");
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth ) {
|
private Model getRDF(Individual entity, OntModel contextModel, Model newModel, int recurseDepth ) {
|
||||||
Resource subj = newModel.getResource(entity.getURI());
|
Resource subj = newModel.getResource(entity.getURI());
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,14 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.dao;
|
package edu.cornell.mannlib.vitro.webapp.dao;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.hp.hpl.jena.rdf.model.Resource;
|
|
||||||
import com.hp.hpl.jena.vocabulary.XSD;
|
|
||||||
import com.hp.hpl.jena.ontology.AnnotationProperty;
|
|
||||||
|
|
||||||
public class VitroVocabulary {
|
public class VitroVocabulary {
|
||||||
|
|
||||||
|
|
||||||
public static final String vitroURI = "http://vitro.mannlib.cornell.edu/ns/vitro/0.7#";
|
public static final String vitroURI = "http://vitro.mannlib.cornell.edu/ns/vitro/0.7#";
|
||||||
|
|
||||||
|
public static final String VITRO_PUBLIC = "http://vitro.mannlib.cornell.edu/ns/vitro/public#";
|
||||||
|
|
||||||
|
|
||||||
/** BJL23 2008-02-25:
|
/** BJL23 2008-02-25:
|
||||||
* This is a hack. The classic Vitro code is heavily reliant on simple identifiers, and it will take some doing to completely
|
* This is a hack. The classic Vitro code is heavily reliant on simple identifiers, and it will take some doing to completely
|
||||||
|
@ -91,8 +87,6 @@ public class VitroVocabulary {
|
||||||
public static final String DISPLAY_LIMIT = vitroURI+"displayLimitAnnot";
|
public static final String DISPLAY_LIMIT = vitroURI+"displayLimitAnnot";
|
||||||
|
|
||||||
public static final String CITATION = vitroURI+"citation";
|
public static final String CITATION = vitroURI+"citation";
|
||||||
public static final String IMAGEFILE = vitroURI+"imageFile";
|
|
||||||
public static final String IMAGETHUMB = vitroURI+"imageThumb";
|
|
||||||
|
|
||||||
// ================== property related =================================
|
// ================== property related =================================
|
||||||
|
|
||||||
|
@ -284,5 +278,18 @@ public class VitroVocabulary {
|
||||||
|
|
||||||
public static final String ONTOLOGY_PREFIX_ANNOT = vitroURI + "ontologyPrefixAnnot";
|
public static final String ONTOLOGY_PREFIX_ANNOT = vitroURI + "ontologyPrefixAnnot";
|
||||||
|
|
||||||
|
// =============== file storage vocabulary ================================
|
||||||
|
|
||||||
|
public static final String FS_FILE_CLASS = VITRO_PUBLIC + "File";
|
||||||
|
public static final String FS_BYTESTREAM_CLASS = VITRO_PUBLIC + "FileByteStream";
|
||||||
|
|
||||||
|
public static final String FS_FILENAME = VITRO_PUBLIC + "filename";
|
||||||
|
public static final String FS_MIME_TYPE = VITRO_PUBLIC + "mimeType";
|
||||||
|
public static final String FS_ATTRIBUTION = VITRO_PUBLIC + "attribution";
|
||||||
|
public static final String FS_DOWNLOAD_LOCATION = VITRO_PUBLIC + "downloadLocation";
|
||||||
|
public static final String FS_THUMBNAIL_IMAGE = VITRO_PUBLIC + "thumbnailImage";
|
||||||
|
|
||||||
|
public static final String IND_MAIN_IMAGE = VITRO_PUBLIC + "mainImage";
|
||||||
|
public static final String IND_IMAGE = VITRO_PUBLIC + "image";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,14 +242,20 @@ public class IndividualFiltering implements Individual {
|
||||||
return _innerIndividual.getFlag3Set();
|
return _innerIndividual.getFlag3Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMainImageUri() {
|
||||||
|
return _innerIndividual.getMainImageUri();
|
||||||
|
}
|
||||||
|
|
||||||
public String getImageFile() {
|
@Override
|
||||||
return _innerIndividual.getImageFile();
|
public String getImageUrl() {
|
||||||
|
return _innerIndividual.getImageUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getImageThumb() {
|
@Override
|
||||||
return _innerIndividual.getImageThumb();
|
public String getThumbUrl() {
|
||||||
|
return _innerIndividual.getThumbUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -405,16 +411,11 @@ public class IndividualFiltering implements Individual {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setImageFile(String imageFile) {
|
@Override
|
||||||
_innerIndividual.setImageFile(imageFile);
|
public void setMainImageUri(String mainImageUri) {
|
||||||
|
_innerIndividual.setMainImageUri(mainImageUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setImageThumb(String imageThumb) {
|
|
||||||
_innerIndividual.setImageThumb(imageThumb);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void setKeywords(List<String> keywords) {
|
public void setKeywords(List<String> keywords) {
|
||||||
_innerIndividual.setKeywords(keywords);
|
_innerIndividual.setKeywords(keywords);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
|
|
||||||
import com.hp.hpl.jena.ontology.DatatypeProperty;
|
|
||||||
import com.hp.hpl.jena.ontology.OntClass;
|
import com.hp.hpl.jena.ontology.OntClass;
|
||||||
import com.hp.hpl.jena.ontology.OntModel;
|
import com.hp.hpl.jena.ontology.OntModel;
|
||||||
import com.hp.hpl.jena.ontology.OntResource;
|
import com.hp.hpl.jena.ontology.OntResource;
|
||||||
|
@ -56,7 +55,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
|
import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.KeywordDao;
|
import edu.cornell.mannlib.vitro.webapp.dao.KeywordDao;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualCreationEvent;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualCreationEvent;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualDeletionEvent;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualDeletionEvent;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualUpdateEvent;
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.IndividualUpdateEvent;
|
||||||
|
@ -313,8 +311,10 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao {
|
||||||
addPropertyDateTimeValue(ind,SUNSET,ent.getSunset(), ontModel);
|
addPropertyDateTimeValue(ind,SUNSET,ent.getSunset(), ontModel);
|
||||||
addPropertyDateTimeValue(ind,TIMEKEY,ent.getTimekey(), ontModel);
|
addPropertyDateTimeValue(ind,TIMEKEY,ent.getTimekey(), ontModel);
|
||||||
addPropertyDateTimeValue(ind,MODTIME,Calendar.getInstance().getTime(),ontModel);
|
addPropertyDateTimeValue(ind,MODTIME,Calendar.getInstance().getTime(),ontModel);
|
||||||
addPropertyStringValue(ind,IMAGETHUMB,ent.getImageThumb(),ontModel);
|
if (ent.getMainImageUri() != null) {
|
||||||
addPropertyStringValue(ind,IMAGEFILE,ent.getImageFile(),ontModel);
|
addPropertyResourceURIValue(ind, IND_MAIN_IMAGE, ent.getMainImageUri());
|
||||||
|
}
|
||||||
|
|
||||||
if (ent.getAnchor()!= null && ent.getAnchor().length()>0 && LINK != null) {
|
if (ent.getAnchor()!= null && ent.getAnchor().length()>0 && LINK != null) {
|
||||||
com.hp.hpl.jena.ontology.Individual primaryLink = ontModel.createIndividual(entURI+"_primaryLink", LINK);
|
com.hp.hpl.jena.ontology.Individual primaryLink = ontModel.createIndividual(entURI+"_primaryLink", LINK);
|
||||||
primaryLink.addProperty(RDF.type, LINK);
|
primaryLink.addProperty(RDF.type, LINK);
|
||||||
|
@ -379,8 +379,9 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao {
|
||||||
ent.getFlag1Numeric();
|
ent.getFlag1Numeric();
|
||||||
ent.getFlag1Set();
|
ent.getFlag1Set();
|
||||||
ent.getFlag2Set();
|
ent.getFlag2Set();
|
||||||
ent.getImageFile();
|
ent.getMainImageUri();
|
||||||
ent.getImageThumb();
|
ent.getImageUrl();
|
||||||
|
ent.getThumbUrl();
|
||||||
ent.getKeywords();
|
ent.getKeywords();
|
||||||
ent.getKeywordString();
|
ent.getKeywordString();
|
||||||
ent.getLinksList();
|
ent.getLinksList();
|
||||||
|
@ -472,9 +473,8 @@ public class IndividualDaoJena extends JenaBaseDao implements IndividualDao {
|
||||||
updatePropertyDateTimeValue(ind,SUNRISE,ent.getSunrise(), ontModel);
|
updatePropertyDateTimeValue(ind,SUNRISE,ent.getSunrise(), ontModel);
|
||||||
updatePropertyDateTimeValue(ind,SUNSET,ent.getSunset(), ontModel);
|
updatePropertyDateTimeValue(ind,SUNSET,ent.getSunset(), ontModel);
|
||||||
updatePropertyDateTimeValue(ind,TIMEKEY,ent.getTimekey(), ontModel);
|
updatePropertyDateTimeValue(ind,TIMEKEY,ent.getTimekey(), ontModel);
|
||||||
updatePropertyStringValue(ind,IMAGETHUMB,ent.getImageThumb(),ontModel);
|
|
||||||
updatePropertyStringValue(ind,IMAGEFILE,ent.getImageFile(),ontModel);
|
|
||||||
updatePropertyDateTimeValue(ind,MODTIME,Calendar.getInstance().getTime(),ontModel);
|
updatePropertyDateTimeValue(ind,MODTIME,Calendar.getInstance().getTime(),ontModel);
|
||||||
|
updatePropertyResourceURIValue(ind, IND_MAIN_IMAGE, ent.getMainImageUri(), ontModel);
|
||||||
if (ent.getAnchor()!= null && ent.getAnchor().length()>0) {
|
if (ent.getAnchor()!= null && ent.getAnchor().length()>0) {
|
||||||
if (LINK != null && PRIMARY_LINK != null) {
|
if (LINK != null && PRIMARY_LINK != null) {
|
||||||
boolean updatedExisting = false;
|
boolean updatedExisting = false;
|
||||||
|
|
|
@ -40,6 +40,8 @@ import edu.cornell.mannlib.vitro.webapp.beans.ObjectProperty;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.FlagMathUtils;
|
import edu.cornell.mannlib.vitro.webapp.utils.FlagMathUtils;
|
||||||
|
|
||||||
public class IndividualJena extends IndividualImpl implements Individual {
|
public class IndividualJena extends IndividualImpl implements Individual {
|
||||||
|
@ -474,32 +476,53 @@ public class IndividualJena extends IndividualImpl implements Individual {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getImageFile() {
|
@Override
|
||||||
if (this.imageFile != null) {
|
public String getMainImageUri() {
|
||||||
return imageFile;
|
if (this.mainImageUri != NOT_INITIALIZED) {
|
||||||
|
return mainImageUri;
|
||||||
} else {
|
} else {
|
||||||
ind.getOntModel().enterCriticalSection(Lock.READ);
|
for (ObjectPropertyStatement stmt : getObjectPropertyStatements()) {
|
||||||
try {
|
if (stmt.getPropertyURI()
|
||||||
imageFile = webappDaoFactory.getJenaBaseDao().getPropertyStringValue(ind,webappDaoFactory.getJenaBaseDao().IMAGEFILE);
|
.equals(VitroVocabulary.IND_MAIN_IMAGE)) {
|
||||||
return imageFile;
|
mainImageUri = stmt.getObjectURI();
|
||||||
} finally {
|
return mainImageUri;
|
||||||
ind.getOntModel().leaveCriticalSection();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getImageThumb() {
|
@Override
|
||||||
if (this.imageThumb != null) {
|
public String getImageUrl() {
|
||||||
return imageThumb;
|
if (this.imageUrl != null) {
|
||||||
|
log.debug("imageUrl was cached for " + getURI() + ": '"
|
||||||
|
+ this.imageUrl + "'");
|
||||||
|
return imageUrl;
|
||||||
} else {
|
} else {
|
||||||
ind.getOntModel().enterCriticalSection(Lock.READ);
|
String imageUri = FileModelHelper.getMainImageBytestreamUri(this);
|
||||||
try {
|
String filename = FileModelHelper.getMainImageFilename(this);
|
||||||
imageThumb = webappDaoFactory.getJenaBaseDao().getPropertyStringValue(ind,webappDaoFactory.getJenaBaseDao().IMAGETHUMB);
|
imageUrl = FileServingHelper.getBytestreamAliasUrl(imageUri,
|
||||||
return imageThumb;
|
filename);
|
||||||
} finally {
|
log.debug("figured imageUrl for " + getURI() + ": '"
|
||||||
ind.getOntModel().leaveCriticalSection();
|
+ this.imageUrl + "'");
|
||||||
|
return imageUrl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getThumbUrl() {
|
||||||
|
if (this.thumbUrl != null) {
|
||||||
|
log.debug("thumbUrl was cached for " + getURI() + ": '"
|
||||||
|
+ this.thumbUrl + "'");
|
||||||
|
return thumbUrl;
|
||||||
|
} else {
|
||||||
|
String imageUri = FileModelHelper.getThumbnailBytestreamUri(this);
|
||||||
|
String filename = FileModelHelper.getThumbnailFilename(this);
|
||||||
|
thumbUrl = FileServingHelper.getBytestreamAliasUrl(imageUri, filename);
|
||||||
|
log.debug("figured thumbUrl for " + getURI() + ": '"
|
||||||
|
+ this.thumbUrl + "'");
|
||||||
|
return thumbUrl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAnchor() {
|
public String getAnchor() {
|
||||||
|
|
|
@ -60,8 +60,6 @@ public class JenaBaseDaoCon {
|
||||||
protected DatatypeProperty MODTIME = _constModel.createDatatypeProperty(VitroVocabulary.MODTIME);
|
protected DatatypeProperty MODTIME = _constModel.createDatatypeProperty(VitroVocabulary.MODTIME);
|
||||||
protected DatatypeProperty TIMEKEY = _constModel.createDatatypeProperty(VitroVocabulary.TIMEKEY);
|
protected DatatypeProperty TIMEKEY = _constModel.createDatatypeProperty(VitroVocabulary.TIMEKEY);
|
||||||
protected DatatypeProperty CITATION = _constModel.createDatatypeProperty(VitroVocabulary.CITATION);
|
protected DatatypeProperty CITATION = _constModel.createDatatypeProperty(VitroVocabulary.CITATION);
|
||||||
protected DatatypeProperty IMAGETHUMB = _constModel.createDatatypeProperty(VitroVocabulary.IMAGETHUMB);
|
|
||||||
protected DatatypeProperty IMAGEFILE = _constModel.createDatatypeProperty(VitroVocabulary.IMAGEFILE);
|
|
||||||
|
|
||||||
protected DatatypeProperty DISPLAY_RANK = _constModel.createDatatypeProperty(VitroVocabulary.DISPLAY_RANK);
|
protected DatatypeProperty DISPLAY_RANK = _constModel.createDatatypeProperty(VitroVocabulary.DISPLAY_RANK);
|
||||||
protected AnnotationProperty DISPLAY_RANK_ANNOT = _constModel.createAnnotationProperty(VitroVocabulary.DISPLAY_RANK_ANNOT);
|
protected AnnotationProperty DISPLAY_RANK_ANNOT = _constModel.createAnnotationProperty(VitroVocabulary.DISPLAY_RANK_ANNOT);
|
||||||
|
@ -183,6 +181,17 @@ public class JenaBaseDaoCon {
|
||||||
|
|
||||||
protected AnnotationProperty ONTOLOGY_PREFIX_ANNOT = _constModel.createAnnotationProperty(VitroVocabulary.ONTOLOGY_PREFIX_ANNOT);
|
protected AnnotationProperty ONTOLOGY_PREFIX_ANNOT = _constModel.createAnnotationProperty(VitroVocabulary.ONTOLOGY_PREFIX_ANNOT);
|
||||||
|
|
||||||
|
protected OntClass FS_FILE = _constModel.createClass(VitroVocabulary.FS_FILE_CLASS);
|
||||||
|
protected OntClass FS_BYTESTREAM = _constModel.createClass(VitroVocabulary.FS_BYTESTREAM_CLASS);
|
||||||
|
protected ObjectProperty FS_DOWNLOAD_LOCATION = _constModel.createObjectProperty(VitroVocabulary.FS_DOWNLOAD_LOCATION);
|
||||||
|
protected ObjectProperty FS_THUMBNAIL_IMAGE = _constModel.createObjectProperty(VitroVocabulary.FS_THUMBNAIL_IMAGE);
|
||||||
|
protected DatatypeProperty FS_FILENAME = _constModel.createDatatypeProperty(VitroVocabulary.FS_FILENAME);
|
||||||
|
protected DatatypeProperty FS_MIME_TYPE = _constModel.createDatatypeProperty(VitroVocabulary.FS_MIME_TYPE);
|
||||||
|
protected DatatypeProperty FS_ATTRIBUTION = _constModel.createDatatypeProperty(VitroVocabulary.FS_ATTRIBUTION);
|
||||||
|
|
||||||
|
protected ObjectProperty IND_MAIN_IMAGE = _constModel.createObjectProperty(VitroVocabulary.IND_MAIN_IMAGE);
|
||||||
|
protected ObjectProperty IND_IMAGE = _constModel.createObjectProperty(VitroVocabulary.IND_IMAGE);
|
||||||
|
|
||||||
public OntModel getConstModel() {
|
public OntModel getConstModel() {
|
||||||
return _constModel;
|
return _constModel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class TabEntityFactoryGalleryJena extends TabEntityFactoryJena
|
||||||
implements TabEntityFactory {
|
implements TabEntityFactory {
|
||||||
|
|
||||||
private TabEntityFactory _innerFactory = null;
|
private TabEntityFactory _innerFactory = null;
|
||||||
public final UnaryFunctor<Individual,Boolean>onlyWithThumbs = new OnlyWithThumbs();
|
public final UnaryFunctor<Individual,Boolean>onlyWithMainImage = new OnlyWithMainImage();
|
||||||
|
|
||||||
public TabEntityFactoryGalleryJena(TabEntityFactory innerEntFactory, Tab tab, int auth_level, ApplicationBean appBean, WebappDaoFactoryJena wadf) {
|
public TabEntityFactoryGalleryJena(TabEntityFactory innerEntFactory, Tab tab, int auth_level, ApplicationBean appBean, WebappDaoFactoryJena wadf) {
|
||||||
super(tab, auth_level, appBean, wadf);
|
super(tab, auth_level, appBean, wadf);
|
||||||
|
@ -79,7 +79,7 @@ public class TabEntityFactoryGalleryJena extends TabEntityFactoryJena
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.EMPTY_LIST;
|
||||||
|
|
||||||
List filteredEnts = new LinkedList( );
|
List filteredEnts = new LinkedList( );
|
||||||
Filter.filter(ents,onlyWithThumbs,filteredEnts);
|
Filter.filter(ents,onlyWithMainImage,filteredEnts);
|
||||||
|
|
||||||
if( filteredEnts.size() <= numberOfrequestedEnts)
|
if( filteredEnts.size() <= numberOfrequestedEnts)
|
||||||
return filteredEnts;
|
return filteredEnts;
|
||||||
|
@ -94,10 +94,10 @@ public class TabEntityFactoryGalleryJena extends TabEntityFactoryJena
|
||||||
return entsOut;
|
return entsOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OnlyWithThumbs extends UnaryFunctor<Individual,Boolean>{
|
private class OnlyWithMainImage extends UnaryFunctor<Individual,Boolean>{
|
||||||
@Override
|
@Override
|
||||||
public Boolean fn(Individual arg) {
|
public Boolean fn(Individual arg) {
|
||||||
return arg.getImageThumb() != null;
|
return arg.getMainImageUri()!= null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,411 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatementImpl;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.IndividualImpl;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.VClass;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyStatementDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* A collection of methods to help manipulate the model, with regard to uploaded
|
||||||
|
* files.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Some of the public methods are static, since the Individual that is passed as
|
||||||
|
* a parameter holds all necessary references for the operation. Other methods
|
||||||
|
* require an instance, which is initialized with a {@link WebappDaoFactory}.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class FileModelHelper {
|
||||||
|
private static final Logger log = Logger.getLogger(FileModelHelper.class);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Static methods -- the Individual holds all necessary references.
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a FileByteStream individual?
|
||||||
|
*/
|
||||||
|
public static boolean isFileBytestream(Individual entity) {
|
||||||
|
for (VClass vClass : entity.getVClasses()) {
|
||||||
|
if (VitroVocabulary.FS_BYTESTREAM_CLASS.equals(vClass.getURI())) {
|
||||||
|
log.debug("Entity '" + entity.getURI() + "' is a bytestream");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.debug("Entity '" + entity.getURI() + "' is not a bytestream");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate the file surrogate for the main image of this entity.
|
||||||
|
*
|
||||||
|
* @return the surrogate, or <code>null</code> if there is no such image, or
|
||||||
|
* if the entity itself is <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static Individual getMainImage(Individual entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Individual mainFile = entity
|
||||||
|
.getRelatedIndividual(VitroVocabulary.IND_MAIN_IMAGE);
|
||||||
|
|
||||||
|
if (mainFile == null) {
|
||||||
|
log.debug("Entity '" + entity.getURI()
|
||||||
|
+ "' had no associated main image.");
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
log.debug("Entity '" + entity.getURI()
|
||||||
|
+ "' had associated main image: '" + mainFile.getURI()
|
||||||
|
+ "'");
|
||||||
|
return mainFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate the file surrogate for the thumbnail of this file.
|
||||||
|
*
|
||||||
|
* @return the surrogate, or <code>null</code> if there is no thumbnail, or
|
||||||
|
* if the file itself is <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static Individual getThumbnailForImage(Individual fileSurrogate) {
|
||||||
|
if (fileSurrogate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Individual thumbFile = fileSurrogate
|
||||||
|
.getRelatedIndividual(VitroVocabulary.FS_THUMBNAIL_IMAGE);
|
||||||
|
|
||||||
|
if (thumbFile == null) {
|
||||||
|
log.warn("Main image file '" + fileSurrogate.getURI()
|
||||||
|
+ "' had no associated thumbnail.");
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
log.debug("Main image file '" + fileSurrogate.getURI()
|
||||||
|
+ "' had associated thumbnail: '" + thumbFile.getURI()
|
||||||
|
+ "'");
|
||||||
|
return thumbFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate the bytestream object for this file.
|
||||||
|
*
|
||||||
|
* @return the bytestream object, or <code>null</code> if there is no
|
||||||
|
* bytestream, or if the file itself is <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static Individual getBytestreamForFile(Individual fileSurrogate) {
|
||||||
|
if (fileSurrogate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Individual byteStream = fileSurrogate
|
||||||
|
.getRelatedIndividual(VitroVocabulary.FS_DOWNLOAD_LOCATION);
|
||||||
|
|
||||||
|
if (byteStream == null) {
|
||||||
|
log.error("File surrogate '" + fileSurrogate.getURI()
|
||||||
|
+ "' had no associated bytestream.");
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
log.debug("File surroage'" + fileSurrogate.getURI()
|
||||||
|
+ "' had associated bytestream: '" + byteStream.getURI()
|
||||||
|
+ "'");
|
||||||
|
return byteStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the filename for this file.
|
||||||
|
*
|
||||||
|
* @return the filename, or <code>null</code> if the file itself is
|
||||||
|
* <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static String getFilename(Individual fileSurrogate) {
|
||||||
|
if (fileSurrogate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filename = fileSurrogate
|
||||||
|
.getDataValue(VitroVocabulary.FS_FILENAME);
|
||||||
|
|
||||||
|
if (filename == null) {
|
||||||
|
log.error("File had no filename: '" + fileSurrogate.getURI() + "'");
|
||||||
|
} else {
|
||||||
|
log.debug("Filename for '" + fileSurrogate.getURI() + "' was '"
|
||||||
|
+ filename + "'");
|
||||||
|
}
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the MIME type for this file.
|
||||||
|
*
|
||||||
|
* @return the MIME type, or <code>null</code> if the file itself is
|
||||||
|
* <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static String getMimeType(Individual fileSurrogate) {
|
||||||
|
if (fileSurrogate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String mimeType = fileSurrogate
|
||||||
|
.getDataValue(VitroVocabulary.FS_MIME_TYPE);
|
||||||
|
|
||||||
|
if (mimeType == null) {
|
||||||
|
log.error("File had no mimeType: '" + fileSurrogate.getURI() + "'");
|
||||||
|
} else {
|
||||||
|
log.debug("mimeType for '" + fileSurrogate.getURI() + "' was '"
|
||||||
|
+ mimeType + "'");
|
||||||
|
}
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the URI for this individual, or <code>null</code> if the
|
||||||
|
* individual is <code>null</code>.
|
||||||
|
*/
|
||||||
|
private static String getUri(Individual entity) {
|
||||||
|
if (entity == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return entity.getURI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate the URI of the bytestream of the main image for this entity.
|
||||||
|
*
|
||||||
|
* @return the URI, or <code>null</code> if there is no such bytestream, or
|
||||||
|
* if the entity itself is <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static String getMainImageBytestreamUri(Individual entity) {
|
||||||
|
Individual mainFile = getMainImage(entity);
|
||||||
|
Individual byteStream = getBytestreamForFile(mainFile);
|
||||||
|
return getUri(byteStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the filename of the main image for this entity.
|
||||||
|
*
|
||||||
|
* @return the filename, or <code>null</code> if there is no such image.
|
||||||
|
*/
|
||||||
|
public static String getMainImageFilename(Individual entity) {
|
||||||
|
Individual mainFile = getMainImage(entity);
|
||||||
|
return getFilename(mainFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locate the individual that represents the bytestream of the thumbnail of
|
||||||
|
* the main image for this entity.
|
||||||
|
*
|
||||||
|
* @return the URI, or <code>null</code> if there is no such thumbnail
|
||||||
|
* image, or if the entity itself is <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static String getThumbnailBytestreamUri(Individual entity) {
|
||||||
|
Individual mainFile = getMainImage(entity);
|
||||||
|
Individual thumbFile = getThumbnailForImage(mainFile);
|
||||||
|
Individual byteStream = getBytestreamForFile(thumbFile);
|
||||||
|
return getUri(byteStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the filename of the thumbnail of the main image.
|
||||||
|
*
|
||||||
|
* @return the filename, or <code>null</code> if there is no such thumbnail
|
||||||
|
* image, or if the entity itself is <code>null</code>.
|
||||||
|
*/
|
||||||
|
public static String getThumbnailFilename(Individual entity) {
|
||||||
|
Individual mainFile = getMainImage(entity);
|
||||||
|
Individual thumbFile = getThumbnailForImage(mainFile);
|
||||||
|
return getFilename(thumbFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Instance methods -- need access to a WebappDaoFactory
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
private final IndividualDao individualDao;
|
||||||
|
private final ObjectPropertyStatementDao objectPropertyStatementDao;
|
||||||
|
private final DataPropertyStatementDao dataPropertyStatementDao;
|
||||||
|
|
||||||
|
public FileModelHelper(WebappDaoFactory webappDaoFactory) {
|
||||||
|
this.individualDao = webappDaoFactory.getIndividualDao();
|
||||||
|
this.objectPropertyStatementDao = webappDaoFactory
|
||||||
|
.getObjectPropertyStatementDao();
|
||||||
|
this.dataPropertyStatementDao = webappDaoFactory
|
||||||
|
.getDataPropertyStatementDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some of these methods require an Individual as an argument.
|
||||||
|
*/
|
||||||
|
public Individual getIndividualByUri(String uri) {
|
||||||
|
return individualDao.getIndividualByURI(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this URI represents a ByteStream object, we need to find it's
|
||||||
|
* surrogate object in order to find the mime type.
|
||||||
|
*
|
||||||
|
* @return the mime type, or <code>null</code> if we couldn't find the mime
|
||||||
|
* type, or if the bytestream object itself is null.
|
||||||
|
*/
|
||||||
|
public String getMimeTypeForBytestream(String bytestreamUri) {
|
||||||
|
if (bytestreamUri == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectPropertyStatement opStmt = new ObjectPropertyStatementImpl(null,
|
||||||
|
VitroVocabulary.FS_DOWNLOAD_LOCATION, bytestreamUri);
|
||||||
|
List<ObjectPropertyStatement> stmts = objectPropertyStatementDao
|
||||||
|
.getObjectPropertyStatements(opStmt);
|
||||||
|
if (stmts.size() > 1) {
|
||||||
|
String uris = "";
|
||||||
|
for (ObjectPropertyStatement stmt : stmts) {
|
||||||
|
uris += "'" + stmt.getSubjectURI() + "' ";
|
||||||
|
}
|
||||||
|
log.warn("Found " + stmts.size() + " Individuals that claim '"
|
||||||
|
+ bytestreamUri + "' as its bytestream:" + uris);
|
||||||
|
}
|
||||||
|
if (stmts.isEmpty()) {
|
||||||
|
log.warn("No individual claims '" + "' as its bytestream.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Individual surrogate = individualDao.getIndividualByURI(stmts.get(0)
|
||||||
|
.getSubjectURI());
|
||||||
|
|
||||||
|
return getMimeType(surrogate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a file surrogate individual in the model.
|
||||||
|
*/
|
||||||
|
public Individual createFileIndividual(String mimeType, String filename,
|
||||||
|
Individual byteStream) {
|
||||||
|
Individual file = new IndividualImpl();
|
||||||
|
file.setVClassURI(VitroVocabulary.FS_FILE_CLASS);
|
||||||
|
|
||||||
|
String uri = null;
|
||||||
|
try {
|
||||||
|
uri = individualDao.insertNewIndividual(file);
|
||||||
|
} catch (InsertException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Failed to create the file individual.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataPropertyStatementDao
|
||||||
|
.insertNewDataPropertyStatement(new DataPropertyStatementImpl(
|
||||||
|
uri, VitroVocabulary.FS_FILENAME, filename));
|
||||||
|
dataPropertyStatementDao
|
||||||
|
.insertNewDataPropertyStatement(new DataPropertyStatementImpl(
|
||||||
|
uri, VitroVocabulary.FS_MIME_TYPE, mimeType));
|
||||||
|
objectPropertyStatementDao
|
||||||
|
.insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(
|
||||||
|
uri, VitroVocabulary.FS_DOWNLOAD_LOCATION, byteStream
|
||||||
|
.getURI()));
|
||||||
|
|
||||||
|
return individualDao.getIndividualByURI(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a bytestream individual in the model.
|
||||||
|
*/
|
||||||
|
public Individual createByteStreamIndividual() {
|
||||||
|
Individual byteStream = new IndividualImpl();
|
||||||
|
byteStream.setVClassURI(VitroVocabulary.FS_BYTESTREAM_CLASS);
|
||||||
|
|
||||||
|
String uri = null;
|
||||||
|
try {
|
||||||
|
uri = individualDao.insertNewIndividual(byteStream);
|
||||||
|
} catch (InsertException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Failed to create the bytestream individual.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return individualDao.getIndividualByURI(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store this file surrogate as the main image on this entity.
|
||||||
|
*/
|
||||||
|
public void setAsMainImageOnEntity(Individual person,
|
||||||
|
Individual imageSurrogate) {
|
||||||
|
person.setMainImageUri(imageSurrogate.getURI());
|
||||||
|
individualDao.updateIndividual(person);
|
||||||
|
log.debug("Set main image '" + getUri(imageSurrogate) + "' on '"
|
||||||
|
+ person.getURI() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the current main image from this entity.
|
||||||
|
*
|
||||||
|
* @return the file surrogate, or <code>null</code> if there was none.
|
||||||
|
*/
|
||||||
|
public Individual removeMainImage(Individual person) {
|
||||||
|
Individual mainImage = getMainImage(person);
|
||||||
|
person.setMainImageUri(null);
|
||||||
|
individualDao.updateIndividual(person);
|
||||||
|
log.debug("Removed main image '" + getUri(mainImage) + "' from '"
|
||||||
|
+ person.getURI() + "'");
|
||||||
|
return mainImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store this file surrogate as the thumnail on this entity.
|
||||||
|
*/
|
||||||
|
public void setThumbnailOnIndividual(Individual entity,
|
||||||
|
Individual thumbnailSurrogate) {
|
||||||
|
String mainImageUri = entity.getMainImageUri();
|
||||||
|
objectPropertyStatementDao
|
||||||
|
.insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(
|
||||||
|
mainImageUri, VitroVocabulary.FS_THUMBNAIL_IMAGE,
|
||||||
|
thumbnailSurrogate.getURI()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are there any ObjectPropertyStatements in the model whose object is this
|
||||||
|
* file surrogate?
|
||||||
|
*/
|
||||||
|
public boolean isFileReferenced(Individual surrogate) {
|
||||||
|
ObjectPropertyStatement opStmt = new ObjectPropertyStatementImpl(null,
|
||||||
|
null, surrogate.getURI());
|
||||||
|
List<ObjectPropertyStatement> stmts = objectPropertyStatementDao
|
||||||
|
.getObjectPropertyStatements(opStmt);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug(stmts.size() + " statements referencing '"
|
||||||
|
+ surrogate.getURI() + "'");
|
||||||
|
for (ObjectPropertyStatement stmt : stmts) {
|
||||||
|
log.debug("'" + stmt.getSubjectURI() + "' -- '"
|
||||||
|
+ stmt.getPropertyURI() + "' -- '"
|
||||||
|
+ stmt.getObjectURI() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !stmts.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is being deleted; remove both the surrogate and its bytestream
|
||||||
|
* from the model.
|
||||||
|
*/
|
||||||
|
public void removeFileFromModel(Individual surrogate) {
|
||||||
|
Individual bytestream = getBytestreamForFile(surrogate);
|
||||||
|
individualDao.deleteIndividual(bytestream);
|
||||||
|
individualDao.deleteIndividual(surrogate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static methods to help when serving uploaded files.
|
||||||
|
*/
|
||||||
|
public class FileServingHelper {
|
||||||
|
private static final Logger log = Logger.getLogger(FileServingHelper.class);
|
||||||
|
|
||||||
|
private static final String DEFAULT_PATH = "/individual/";
|
||||||
|
private static final String FILE_PATH = "/file/";
|
||||||
|
private static final String DEFAULT_NAMESPACE = initializeDefaultNamespace();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At startup, get the default namespace from the configuration properties,
|
||||||
|
* and trim off the suffix.
|
||||||
|
*/
|
||||||
|
private static String initializeDefaultNamespace() {
|
||||||
|
String defaultNamespace = ConfigurationProperties
|
||||||
|
.getProperty(FileStorageSetup.PROPERTY_DEFAULT_NAMESPACE);
|
||||||
|
if (defaultNamespace == null) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Configuration properties must contain a value for '"
|
||||||
|
+ FileStorageSetup.PROPERTY_DEFAULT_NAMESPACE + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defaultNamespace.endsWith(DEFAULT_PATH)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Default namespace does not match the expected form: '"
|
||||||
|
+ defaultNamespace + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultNamespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Combine the URI and the filename to produce a relative URL for the file
|
||||||
|
* (relative to the context of the webapp).
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This should involve stripping the default namespace from the front of the
|
||||||
|
* URL, replacing it with the file prefix, and adding the filename to the
|
||||||
|
* end.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return <ul>
|
||||||
|
* <li>the translated URL, if the URI was in the default namespace,</li>
|
||||||
|
* <li>the original URI, if it wasn't in the default namespace,</li>
|
||||||
|
* <li>null, if the original URI or the filename was null.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static String getBytestreamAliasUrl(String uri, String filename) {
|
||||||
|
if ((uri == null) || (filename == null)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!uri.startsWith(DEFAULT_NAMESPACE)) {
|
||||||
|
log.warn("uri does not start with the default namespace: '" + uri
|
||||||
|
+ "'");
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
String remainder = uri.substring(DEFAULT_NAMESPACE.length());
|
||||||
|
String separator = remainder.endsWith("/") ? "" : "/";
|
||||||
|
return FILE_PATH + remainder + separator + filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** No need for instances because all of the methods are static. */
|
||||||
|
private FileServingHelper() {
|
||||||
|
// nothing to instantiate.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Take a relative URL (relative to the context of the webapp) and produce
|
||||||
|
* the URI for the file bytestream.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This should involve removing the filename from the end of the URL, and
|
||||||
|
* replacing the file prefix with the default namespace.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return the URI, or <code>null</code> if the URL couldn't be translated.
|
||||||
|
*/
|
||||||
|
public static String getBytestreamUri(String path) {
|
||||||
|
if (path == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!path.startsWith(FILE_PATH)) {
|
||||||
|
log.warn("path does not start with a file prefix: '" + path + "'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String remainder = path.substring(FILE_PATH.length());
|
||||||
|
|
||||||
|
int slashHere = remainder.lastIndexOf('/');
|
||||||
|
if (slashHere == -1) {
|
||||||
|
log.debug("path does not include a filename: '" + path + "'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
remainder = remainder.substring(0, slashHere);
|
||||||
|
|
||||||
|
return DEFAULT_NAMESPACE + remainder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,10 +23,13 @@ import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default implementation of {@link FileStorage}.
|
* The default implementation of {@link FileStorage}.
|
||||||
*/
|
*/
|
||||||
public class FileStorageImpl implements FileStorage {
|
public class FileStorageImpl implements FileStorage {
|
||||||
|
private static final Logger log = Logger.getLogger(FileStorageImpl.class);
|
||||||
|
|
||||||
private final File baseDir;
|
private final File baseDir;
|
||||||
private final File rootDir;
|
private final File rootDir;
|
||||||
|
@ -290,6 +293,8 @@ public class FileStorageImpl implements FileStorage {
|
||||||
public String getFilename(String id) throws IOException {
|
public String getFilename(String id) throws IOException {
|
||||||
File dir = FileStorageHelper.getPathToIdDirectory(id,
|
File dir = FileStorageHelper.getPathToIdDirectory(id,
|
||||||
this.namespacesMap, this.rootDir);
|
this.namespacesMap, this.rootDir);
|
||||||
|
log.debug("ID '" + id + "' translates to this directory path: '" + dir
|
||||||
|
+ "'");
|
||||||
|
|
||||||
if ((!dir.exists()) || (!dir.isDirectory())) {
|
if ((!dir.exists()) || (!dir.isDirectory())) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -28,13 +28,13 @@ public class FileStorageSetup implements ServletContextListener {
|
||||||
* The default implementation will use this key to ask
|
* The default implementation will use this key to ask
|
||||||
* {@link ConfigurationProperties} for the file storage base directory.
|
* {@link ConfigurationProperties} for the file storage base directory.
|
||||||
*/
|
*/
|
||||||
static final String PROPERTY_FILE_STORAGE_BASE_DIR = "upload.directory";
|
public static final String PROPERTY_FILE_STORAGE_BASE_DIR = "upload.directory";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default implementation will use this key to ask
|
* The default implementation will use this key to ask
|
||||||
* {@link ConfigurationProperties} for the default URI namespace.
|
* {@link ConfigurationProperties} for the default URI namespace.
|
||||||
*/
|
*/
|
||||||
static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace";
|
public static final String PROPERTY_DEFAULT_NAMESPACE = "Vitro.defaultNamespace";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an implementation of {@link FileStorage} and store it in the
|
* Create an implementation of {@link FileStorage} and store it in the
|
||||||
|
@ -46,7 +46,7 @@ public class FileStorageSetup implements ServletContextListener {
|
||||||
FileStorage fs;
|
FileStorage fs;
|
||||||
try {
|
try {
|
||||||
File baseDirectory = figureBaseDir();
|
File baseDirectory = figureBaseDir();
|
||||||
Collection<String> fileNamespace = figureFileNamespace();
|
Collection<String> fileNamespace = confirmDefaultNamespace();
|
||||||
fs = new FileStorageImpl(baseDirectory, fileNamespace);
|
fs = new FileStorageImpl(baseDirectory, fileNamespace);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
|
@ -75,16 +75,15 @@ public class FileStorageSetup implements ServletContextListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the configuration property for the default namespace, and derive the
|
* Get the configuration property for the default namespace, and confirm
|
||||||
* file namespace from it. The default namespace is assumed to be in this
|
* that it is in the proper form. The default namespace is assumed to be in
|
||||||
* form: <code>http://vivo.mydomain.edu/individual/</code>
|
* this form: <code>http://vivo.mydomain.edu/individual/</code>
|
||||||
*
|
*
|
||||||
* For use by the constructor in implementations of {@link FileStorage}.
|
* For use by the constructor in implementations of {@link FileStorage}.
|
||||||
*
|
*
|
||||||
* @returns the file namespace is assumed to be in this form:
|
* @returns a collection containing the default namespace.
|
||||||
* <code>http://vivo.mydomain.edu/file/</code>
|
|
||||||
*/
|
*/
|
||||||
private Collection<String> figureFileNamespace() {
|
private Collection<String> confirmDefaultNamespace() {
|
||||||
String defaultNamespace = ConfigurationProperties
|
String defaultNamespace = ConfigurationProperties
|
||||||
.getProperty(PROPERTY_DEFAULT_NAMESPACE);
|
.getProperty(PROPERTY_DEFAULT_NAMESPACE);
|
||||||
if (defaultNamespace == null) {
|
if (defaultNamespace == null) {
|
||||||
|
@ -94,7 +93,6 @@ public class FileStorageSetup implements ServletContextListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
String defaultSuffix = "/individual/";
|
String defaultSuffix = "/individual/";
|
||||||
String fileSuffix = "/file/";
|
|
||||||
|
|
||||||
if (!defaultNamespace.endsWith(defaultSuffix)) {
|
if (!defaultNamespace.endsWith(defaultSuffix)) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
@ -102,10 +100,7 @@ public class FileStorageSetup implements ServletContextListener {
|
||||||
+ defaultNamespace + "'");
|
+ defaultNamespace + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
int hostLength = defaultNamespace.length() - defaultSuffix.length();
|
return Collections.singleton(defaultNamespace);
|
||||||
String fileNamespace = defaultNamespace.substring(0, hostLength)
|
|
||||||
+ fileSuffix;
|
|
||||||
return Collections.singleton(fileNamespace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.serving;
|
||||||
|
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
|
import javax.servlet.UnavailableException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroHttpServlet;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Handles a request to serve an uploaded file from the file storage system.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The path of the request should be the "alias URL" of the desired file. We
|
||||||
|
* need to:
|
||||||
|
* <ul>
|
||||||
|
* <li>Use the alias URL to find the URI of the file bytestream object.</li>
|
||||||
|
* <li>Find the file surrogate object to get the MIME type of the file, and
|
||||||
|
* confirm the filename.</li>
|
||||||
|
* <li>Set the MIME type on the output stream and serve the bytes.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If the request is superficially correct, but no such file can be found,
|
||||||
|
* return a 404. If there is a break in the data structures within the model or
|
||||||
|
* the file system, return a 500.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class FileServingServlet extends VitroHttpServlet {
|
||||||
|
private static final Logger log = Logger
|
||||||
|
.getLogger(FileServingServlet.class);
|
||||||
|
|
||||||
|
private FileStorage fileStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a reference to the File Storage system.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void init() throws ServletException {
|
||||||
|
Object o = getServletContext().getAttribute(
|
||||||
|
FileStorageSetup.ATTRIBUTE_NAME);
|
||||||
|
if (o instanceof FileStorage) {
|
||||||
|
fileStorage = (FileStorage) o;
|
||||||
|
} else {
|
||||||
|
throw new UnavailableException(
|
||||||
|
"The ServletContext did not hold a FileStorage object at '"
|
||||||
|
+ FileStorageSetup.ATTRIBUTE_NAME
|
||||||
|
+ "'; found this instead: " + o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest rawRequest,
|
||||||
|
HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
VitroRequest request = new VitroRequest(rawRequest);
|
||||||
|
|
||||||
|
// Use the alias URL to get the URI of the bytestream object.
|
||||||
|
String path = request.getServletPath() + request.getPathInfo();
|
||||||
|
log.debug("Path is '" + path + "'");
|
||||||
|
|
||||||
|
String uri = FileServingHelper.getBytestreamUri(path);
|
||||||
|
log.debug("Bytestream URI is '" + uri + "'");
|
||||||
|
if (uri == null) {
|
||||||
|
String message = "The request path is not valid for the File servlet: '"
|
||||||
|
+ path + "'";
|
||||||
|
log.error(message);
|
||||||
|
response.sendError(SC_INTERNAL_SERVER_ERROR, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the file exists, with the requested URI and filename.
|
||||||
|
String requestedFilename = getFilename(path);
|
||||||
|
String actualFilename = fileStorage.getFilename(uri);
|
||||||
|
if (actualFilename == null) {
|
||||||
|
log.debug("Requested a non-existent file: " + path);
|
||||||
|
response.sendError(SC_NOT_FOUND, ("File not found: " + path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!actualFilename.equals(requestedFilename)) {
|
||||||
|
log.warn("The requested filename does not match the "
|
||||||
|
+ "actual filename; request: '" + path + "', actual: '"
|
||||||
|
+ actualFilename + "'");
|
||||||
|
response.sendError(SC_NOT_FOUND, ("File not found: " + path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the MIME type.
|
||||||
|
String mimeType = new FileModelHelper(getWebappDaoFactory())
|
||||||
|
.getMimeTypeForBytestream(uri);
|
||||||
|
|
||||||
|
// Open the actual byte stream.
|
||||||
|
InputStream in;
|
||||||
|
try {
|
||||||
|
in = fileStorage.getInputStream(uri, actualFilename);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
log.error(e);
|
||||||
|
response.sendError(SC_INTERNAL_SERVER_ERROR, e.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Everything is ready and working. Set the status and the content type,
|
||||||
|
* and send the image bytes.
|
||||||
|
*/
|
||||||
|
response.setStatus(SC_OK);
|
||||||
|
|
||||||
|
if (mimeType != null) {
|
||||||
|
response.setContentType(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
ServletOutputStream out = null;
|
||||||
|
try {
|
||||||
|
out = response.getOutputStream();
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int howMany;
|
||||||
|
while (-1 != (howMany = in.read(buffer))) {
|
||||||
|
out.write(buffer, 0, howMany);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (out != null) {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename is the portion of the path after the last slash.
|
||||||
|
*/
|
||||||
|
private String getFilename(String path) {
|
||||||
|
int slashHere = path.lastIndexOf('/');
|
||||||
|
if (slashHere == -1) {
|
||||||
|
return path;
|
||||||
|
} else {
|
||||||
|
return path.substring(slashHere + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A POST request is treated the same as a GET request.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void doPost(HttpServletRequest request,
|
||||||
|
HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
doGet(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.ResIterator;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust any individual that has a thumbnail with no main image.
|
||||||
|
*/
|
||||||
|
public class AllThumbsAdjuster extends FsuScanner {
|
||||||
|
protected final File imageDirectory;
|
||||||
|
|
||||||
|
public AllThumbsAdjuster(FSUController controller) {
|
||||||
|
super(controller);
|
||||||
|
this.imageDirectory = controller.getImageDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For every individual with thumbnails but no main images, create a main
|
||||||
|
* image from the first thumbnail.
|
||||||
|
*/
|
||||||
|
public void adjust() {
|
||||||
|
updateLog.section("Creating main images for thumbnails "
|
||||||
|
+ "that have none.");
|
||||||
|
|
||||||
|
ResIterator haveThumb = model.listResourcesWithProperty(thumbProperty);
|
||||||
|
try {
|
||||||
|
while (haveThumb.hasNext()) {
|
||||||
|
Resource resource = haveThumb.next();
|
||||||
|
|
||||||
|
if (resource.getProperty(imageProperty) == null) {
|
||||||
|
createMainImageFromThumbnail(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
haveThumb.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This individual has a thumbnail but no main image. Create one.
|
||||||
|
* <ul>
|
||||||
|
* <li>Figure a name for the main image.</li>
|
||||||
|
* <li>Copy the thumbnail image file into the main image file.</li>
|
||||||
|
* <li>Set that file as an image (old-style) on the individual.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private void createMainImageFromThumbnail(Resource resource) {
|
||||||
|
String thumbFilename = getValues(resource, thumbProperty).get(0);
|
||||||
|
String mainFilename = addFilenamePrefix("_main_image_", thumbFilename);
|
||||||
|
updateLog.log(resource, "creating a main file at '" + mainFilename
|
||||||
|
+ "' to match the thumbnail at '" + thumbFilename + "'");
|
||||||
|
|
||||||
|
try {
|
||||||
|
File thumbFile = new File(imageDirectory, thumbFilename);
|
||||||
|
File mainFile = new File(imageDirectory, mainFilename);
|
||||||
|
mainFile = checkNameConflicts(mainFile);
|
||||||
|
FileUtil.copyFile(thumbFile, mainFile);
|
||||||
|
|
||||||
|
resource.addProperty(imageProperty, mainFilename);
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateLog.error(resource, "failed to create main file '"
|
||||||
|
+ mainFilename + "'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.Literal;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Property;
|
||||||
|
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||||
|
import com.hp.hpl.jena.rdf.model.ResIterator;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Statement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any image properties (main or thumbnail) that point to files that
|
||||||
|
* don't actually exist.
|
||||||
|
*/
|
||||||
|
public class DeadEndPropertyRemover extends FsuScanner {
|
||||||
|
protected final File imageDirectory;
|
||||||
|
|
||||||
|
public DeadEndPropertyRemover(FSUController controller) {
|
||||||
|
super(controller);
|
||||||
|
this.imageDirectory = controller.getImageDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove dead end properties for both main images and thumbnails.
|
||||||
|
*/
|
||||||
|
public void remove() {
|
||||||
|
updateLog.section("Removing image properties whose "
|
||||||
|
+ "referenced files do not exist.");
|
||||||
|
|
||||||
|
removeDeadEndProperties(imageProperty, "main image");
|
||||||
|
removeDeadEndProperties(thumbProperty, "thumbnail");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check all of the individuals that possess this property.
|
||||||
|
*/
|
||||||
|
private void removeDeadEndProperties(Property prop, String label) {
|
||||||
|
ResIterator resources = model.listResourcesWithProperty(prop);
|
||||||
|
try {
|
||||||
|
while (resources.hasNext()) {
|
||||||
|
Resource resource = resources.next();
|
||||||
|
removeDeadEndPropertiesFromResource(resource, prop, label);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
resources.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check these statments on this resource. If any of them does not point to
|
||||||
|
* an existing file, remove the statement.
|
||||||
|
*/
|
||||||
|
private void removeDeadEndPropertiesFromResource(Resource resource,
|
||||||
|
Property prop, String label) {
|
||||||
|
for (Statement stmt : getStatements(resource, prop)) {
|
||||||
|
RDFNode node = stmt.getObject();
|
||||||
|
if (node.isLiteral()) {
|
||||||
|
String filename = ((Literal)node).getString();
|
||||||
|
File file = new File(imageDirectory, filename);
|
||||||
|
if (!file.exists()) {
|
||||||
|
updateLog.warn(resource, "removing link to " + label + " '"
|
||||||
|
+ filename + "': file does not exist at '"
|
||||||
|
+ file.getAbsolutePath() + "'.");
|
||||||
|
model.remove(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.Model;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that can initialize one of the {@link FsuScanner}s.
|
||||||
|
*/
|
||||||
|
public interface FSUController {
|
||||||
|
|
||||||
|
/** The Jena model. */
|
||||||
|
Model getModel();
|
||||||
|
|
||||||
|
/** The update log. */
|
||||||
|
FSULog getUpdateLog();
|
||||||
|
|
||||||
|
/** The directory where the old-style images were stored. */
|
||||||
|
File getImageDirectory();
|
||||||
|
|
||||||
|
/** The file storage system. */
|
||||||
|
FileStorage getFileStorage();
|
||||||
|
|
||||||
|
/** A file model helper with access to the DAO layer. */
|
||||||
|
FileModelHelper getFileModelHelper();
|
||||||
|
|
||||||
|
/** Where to store the files that were translated. */
|
||||||
|
File getTranslatedDirectory();
|
||||||
|
|
||||||
|
/** Where to store the files that weren't in use anyway. */
|
||||||
|
File getUnreferencedDirectory();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.Literal;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Model;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Property;
|
||||||
|
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Statement;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the log file for the {@link FileStorageUpdater}. Be sure to call
|
||||||
|
* {@link #close()} when finished.
|
||||||
|
*/
|
||||||
|
public class FSULog {
|
||||||
|
private final SimpleDateFormat timeStamper = new SimpleDateFormat(
|
||||||
|
"yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
private final File logFile;
|
||||||
|
private final PrintWriter writer;
|
||||||
|
private boolean open;
|
||||||
|
|
||||||
|
FSULog(File logDirectory) throws IOException {
|
||||||
|
this.logFile = generateTimestampedFilename(logDirectory);
|
||||||
|
this.writer = new PrintWriter(this.logFile);
|
||||||
|
open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a filename for the log file that contains a timestamp, so if we
|
||||||
|
* run the process more than once, we will see multiple files.
|
||||||
|
*/
|
||||||
|
private File generateTimestampedFilename(File upgradeDirectory) {
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH-mm-sss");
|
||||||
|
String filename = "upgradeLog." + sdf.format(new Date()) + ".txt";
|
||||||
|
return new File(upgradeDirectory, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where are we writing the output?
|
||||||
|
*/
|
||||||
|
String getFilename() {
|
||||||
|
return this.logFile.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write this message.
|
||||||
|
*/
|
||||||
|
void log(String message) {
|
||||||
|
writer.println(timeStamper.format(new Date()) + " INFO " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write this message about this resource.
|
||||||
|
*/
|
||||||
|
void log(Resource resource, String message) {
|
||||||
|
log(showResource(resource) + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write this warning message.
|
||||||
|
*/
|
||||||
|
public void warn(String message) {
|
||||||
|
writer.println(timeStamper.format(new Date()) + " WARN " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write this warning message about this resource.
|
||||||
|
*/
|
||||||
|
public void warn(Resource resource, String message) {
|
||||||
|
warn(showResource(resource) + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write this error message.
|
||||||
|
*/
|
||||||
|
void error(String message) {
|
||||||
|
writer.println(timeStamper.format(new Date()) + " ERROR " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write this exception as an error message..
|
||||||
|
*/
|
||||||
|
void error(Exception e) {
|
||||||
|
error(e.toString());
|
||||||
|
e.printStackTrace(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an error message with this exception.
|
||||||
|
*/
|
||||||
|
public void error(String message, Exception e) {
|
||||||
|
error(message);
|
||||||
|
e.printStackTrace(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an error message about this resource and with this exception.
|
||||||
|
*/
|
||||||
|
public void error(Resource resource, String message) {
|
||||||
|
error(showResource(resource) + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an error message about this resource and with this exception.
|
||||||
|
*/
|
||||||
|
public void error(Resource resource, String message, Exception e) {
|
||||||
|
error(showResource(resource) + message, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a section heading.
|
||||||
|
*/
|
||||||
|
public void section(String message) {
|
||||||
|
log(">>>>>>>>>> ");
|
||||||
|
log(">>>>>>>>>> " + message);
|
||||||
|
log(">>>>>>>>>> ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the writer, if not already closed.
|
||||||
|
*/
|
||||||
|
public void close() {
|
||||||
|
if (open) {
|
||||||
|
writer.close();
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the resource label and URI for output in a message.
|
||||||
|
*/
|
||||||
|
private String showResource(Resource resource) {
|
||||||
|
return "On resource '" + getLabel(resource) + "' (" + getUri(resource)
|
||||||
|
+ "), ";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the URI for this resource, if there is one.
|
||||||
|
*/
|
||||||
|
private String getUri(Resource resource) {
|
||||||
|
if (resource != null) {
|
||||||
|
String uri = resource.getURI();
|
||||||
|
if (uri != null) {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "no URI";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the label for this resource, if there is one.
|
||||||
|
*/
|
||||||
|
private String getLabel(Resource resource) {
|
||||||
|
if (resource != null) {
|
||||||
|
Model model = resource.getModel();
|
||||||
|
if (model != null) {
|
||||||
|
Property prop = model.createProperty(VitroVocabulary.LABEL);
|
||||||
|
Statement stmt = resource.getProperty(prop);
|
||||||
|
if (stmt != null) {
|
||||||
|
RDFNode node = stmt.getObject();
|
||||||
|
if (node.isLiteral()) {
|
||||||
|
return ((Literal) node).getString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "no label";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,272 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.Model;
|
||||||
|
import com.hp.hpl.jena.rdf.model.ResIterator;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Clean up any files that are stored in the old directory structure and
|
||||||
|
* referenced by old-style image properties.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Besides converting the files to the new framework, this process will produce
|
||||||
|
* these artifacts:
|
||||||
|
* <ul>
|
||||||
|
* <li>A log file in the uploaded files directory, with a timestamped name, such
|
||||||
|
* as <code>upgrade/upgradeLog2010-06-20T14-55-00.txt</code>, for example. If
|
||||||
|
* for any reason, the upgrade process must run again, the log will not be
|
||||||
|
* overwritten.</li>
|
||||||
|
* <li>A directory of "deleted" files - these were extra thumbnail files or
|
||||||
|
* extra main image files -- see the details below.</li>
|
||||||
|
* <li>A directory of "unreferenced" files - these were in the image directory,
|
||||||
|
* but not connected to any entity.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* We consider some special cases:
|
||||||
|
* <ul>
|
||||||
|
* <li>An individual may refer to an image file that does not actually exist. If
|
||||||
|
* so, that reference will be deleted.</li>
|
||||||
|
* <li>There may be more than one reference to the same image file. If so, all
|
||||||
|
* but the first such reference will be deleted.</li>
|
||||||
|
* <li>
|
||||||
|
* In the old style, it was possible to have a main image without a thumbnail.
|
||||||
|
* If we find that, we will generate a scaled down copy of the main image, store
|
||||||
|
* that as a thumbnail image file, and then proceed.</li>
|
||||||
|
* <li>
|
||||||
|
* In the old style, it was possible to have a thumbnail without a main image.
|
||||||
|
* If we find that, we will make a copy of the thumbnail image file, declare
|
||||||
|
* that copy to be the main image, and then proceed.</li>
|
||||||
|
* <li>
|
||||||
|
* We may find individuals with more than one main image, or more than one
|
||||||
|
* thumbnail. If so, we will discard all but the first one (move them to the
|
||||||
|
* "deleted" directory).</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Aside from these special cases, we will:
|
||||||
|
* <ul>
|
||||||
|
* <li>Translate the main image.
|
||||||
|
* <ul>
|
||||||
|
* <li>Store the image in the new file system.</li>
|
||||||
|
* <li>Delete the image from the old images directory.</li>
|
||||||
|
* <li>Create a ByteStream individual for the main image.</li>
|
||||||
|
* <li>Create a Surrogate individual for the main image.</li>
|
||||||
|
* <li>Tie these together and attach to the entity that owns the image.</li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* <li>Translate the thumbnail.
|
||||||
|
* <ul>
|
||||||
|
* <li>Store the thumbnail in the new file system.</li>
|
||||||
|
* <li>Create a ByteStream individual for the thumbnail.</li>
|
||||||
|
* <li>Create a Surrogate individual for the thumbnail.</li>
|
||||||
|
* <li>Tie these together and attach to the Surrogate for the main image.</li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* After processing all of these cases, there may be some images remaining in
|
||||||
|
* the "images" directory. These will be moved to the "unreferenced" directory,
|
||||||
|
* while preserving any internal tree structure.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class FileStorageUpdater implements FSUController {
|
||||||
|
private static final Log log = LogFactory.getLog(FileStorageUpdater.class);
|
||||||
|
|
||||||
|
/** How wide should a generated thumbnail image be (in pixels)? */
|
||||||
|
public static final int THUMBNAIL_WIDTH = 150;
|
||||||
|
|
||||||
|
/** How high should a generated thumbnail image be (in pixels)? */
|
||||||
|
public static final int THUMBNAIL_HEIGHT = 150;
|
||||||
|
|
||||||
|
/** How is the main image referenced in the old scheme? */
|
||||||
|
public static final String IMAGEFILE = VitroVocabulary.vitroURI
|
||||||
|
+ "imageFile";
|
||||||
|
|
||||||
|
/** How is the thumbnail referenced in the old scheme? */
|
||||||
|
public static final String IMAGETHUMB = VitroVocabulary.vitroURI
|
||||||
|
+ "imageThumb";
|
||||||
|
|
||||||
|
private final Model model;
|
||||||
|
|
||||||
|
private final FileStorage fileStorage;
|
||||||
|
private final FileModelHelper fileModelHelper;
|
||||||
|
private final File imageDirectory;
|
||||||
|
private final File upgradeDirectory;
|
||||||
|
|
||||||
|
private FSULog updateLog;
|
||||||
|
|
||||||
|
public FileStorageUpdater(WebappDaoFactory wadf, Model model,
|
||||||
|
FileStorage fileStorage, File uploadDirectory) {
|
||||||
|
this.model = model;
|
||||||
|
this.fileStorage = fileStorage;
|
||||||
|
this.fileModelHelper = new FileModelHelper(wadf);
|
||||||
|
this.imageDirectory = new File(uploadDirectory, "images");
|
||||||
|
this.upgradeDirectory = new File(uploadDirectory, "upgrade");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Go through all of the individuals who have image files or thumbnail
|
||||||
|
* files, adjusting them to the new way.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If there is nothing to do, don't even create a log file, just exit.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If there is something to do, go through the whole process.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* At the end, there should be nothing to do. If that's true, clean out the
|
||||||
|
* old images directory.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void update() {
|
||||||
|
// If there is nothing to do, we're done: don't even create a log file.
|
||||||
|
if (!isThereAnythingToDo()) {
|
||||||
|
log.debug("Found no pre-1.1 file references.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the upgrade directory and the log file.
|
||||||
|
setup();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Remove any image properties that don't point to literals.
|
||||||
|
new NonLiteralPropertyRemover(this).remove();
|
||||||
|
|
||||||
|
// Remove any image properties that point to files that don't exist.
|
||||||
|
new DeadEndPropertyRemover(this).remove();
|
||||||
|
|
||||||
|
// No resource may have multiple main images or multiple thumbnails.
|
||||||
|
new MultiplePropertyRemover(this).remove();
|
||||||
|
|
||||||
|
// Create a main image for any thumbnail that doesn't have one.
|
||||||
|
new AllThumbsAdjuster(this).adjust();
|
||||||
|
|
||||||
|
// Create a thumbnail for any main image that doesn't have one.
|
||||||
|
new NoThumbsAdjuster(this).adjust();
|
||||||
|
|
||||||
|
// Copy all images into the new file storage system, translating
|
||||||
|
// into the new schema. Get a list of all the images we translated.
|
||||||
|
ImageSchemaTranslater translater = new ImageSchemaTranslater(this);
|
||||||
|
Collection<String> translatedFiles = translater.translate();
|
||||||
|
|
||||||
|
if (isThereAnythingToDo()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"FileStorageUpdate was unsuccessful -- "
|
||||||
|
+ "model still contains pre-1.1 file references.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean out the old image directory, separating into files which
|
||||||
|
// were translated, and files for which we found no reference.
|
||||||
|
new ImageDirectoryCleaner(this).clean(translatedFiles);
|
||||||
|
} finally {
|
||||||
|
updateLog.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Finished updating pre-1.1 file references.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the model. If there are any resources with old-style image
|
||||||
|
* properties, we have work to do.
|
||||||
|
*/
|
||||||
|
private boolean isThereAnythingToDo() {
|
||||||
|
ResIterator haveImage = model.listResourcesWithProperty(model
|
||||||
|
.createProperty(IMAGEFILE));
|
||||||
|
try {
|
||||||
|
if (haveImage.hasNext()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
haveImage.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResIterator haveThumb = model.listResourcesWithProperty(model
|
||||||
|
.createProperty(IMAGETHUMB));
|
||||||
|
try {
|
||||||
|
if (haveThumb.hasNext()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
haveThumb.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the upgrade directory. Create the log file. If we fail, drop dead.
|
||||||
|
*/
|
||||||
|
private void setup() {
|
||||||
|
try {
|
||||||
|
this.upgradeDirectory.mkdirs();
|
||||||
|
updateLog = new FSULog(this.upgradeDirectory);
|
||||||
|
log.info("Updating pre-1.1 file references. Log file is "
|
||||||
|
+ updateLog.getFilename());
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (updateLog != null) {
|
||||||
|
updateLog.close();
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("can't create log file: '"
|
||||||
|
+ updateLog.getFilename() + "'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Methods to set up the individual scanners.
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Model getModel() {
|
||||||
|
return this.model;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FSULog getUpdateLog() {
|
||||||
|
return this.updateLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileModelHelper getFileModelHelper() {
|
||||||
|
return this.fileModelHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileStorage getFileStorage() {
|
||||||
|
return this.fileStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getImageDirectory() {
|
||||||
|
return this.imageDirectory;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getTranslatedDirectory() {
|
||||||
|
return new File(this.upgradeDirectory, "translatedImages");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getUnreferencedDirectory() {
|
||||||
|
return new File(this.upgradeDirectory, "unreferencedImages");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of static routines for moving, copying and deleting files.
|
||||||
|
*/
|
||||||
|
public class FileUtil {
|
||||||
|
/**
|
||||||
|
* Copy a file from one location to another, and remove it from the original
|
||||||
|
* location.
|
||||||
|
*/
|
||||||
|
public static void moveFile(File from, File to) throws IOException {
|
||||||
|
copyFile(from, to);
|
||||||
|
deleteFile(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a file from one location to another.
|
||||||
|
*/
|
||||||
|
public static void copyFile(File from, File to) throws IOException {
|
||||||
|
if (!from.exists()) {
|
||||||
|
throw new FileNotFoundException("File '" + from.getAbsolutePath()
|
||||||
|
+ "' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream in = null;
|
||||||
|
try {
|
||||||
|
in = new FileInputStream(from);
|
||||||
|
writeFile(in, to);
|
||||||
|
} finally {
|
||||||
|
if (in != null) {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a file with the contents of this data stream.
|
||||||
|
*
|
||||||
|
* @param stream
|
||||||
|
* the data stream. You must close it afterward.
|
||||||
|
*/
|
||||||
|
public static void writeFile(InputStream stream, File to)
|
||||||
|
throws IOException {
|
||||||
|
if (to.exists()) {
|
||||||
|
throw new IOException("File '" + to.getAbsolutePath()
|
||||||
|
+ "' already exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
File parent = to.getParentFile();
|
||||||
|
if (!parent.exists()) {
|
||||||
|
parent.mkdirs();
|
||||||
|
if (!parent.exists()) {
|
||||||
|
throw new IOException("Can't create parent directory for '"
|
||||||
|
+ to.getAbsolutePath() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream out = null;
|
||||||
|
try {
|
||||||
|
out = new FileOutputStream(to);
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int howMany;
|
||||||
|
while (-1 != (howMany = stream.read(buffer))) {
|
||||||
|
out.write(buffer, 0, howMany);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (out != null) {
|
||||||
|
try {
|
||||||
|
out.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete this file, and make sure that it's gone.
|
||||||
|
*/
|
||||||
|
public static void deleteFile(File file) throws IOException {
|
||||||
|
file.delete();
|
||||||
|
if (file.exists()) {
|
||||||
|
throw new IOException("Failed to delete file '"
|
||||||
|
+ file.getAbsolutePath() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** No need to instantiate it -- all methods are static. */
|
||||||
|
private FileUtil() {
|
||||||
|
// Nothing to instantiate.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.Literal;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Model;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Property;
|
||||||
|
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Statement;
|
||||||
|
import com.hp.hpl.jena.rdf.model.StmtIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for the tools that scan the model. Holds some useful fields and
|
||||||
|
* some utility methods.
|
||||||
|
*/
|
||||||
|
public abstract class FsuScanner {
|
||||||
|
protected final Model model;
|
||||||
|
protected final FSULog updateLog;
|
||||||
|
|
||||||
|
protected final Property imageProperty;
|
||||||
|
protected final Property thumbProperty;
|
||||||
|
|
||||||
|
public FsuScanner(FSUController controller) {
|
||||||
|
this.model = controller.getModel();
|
||||||
|
this.updateLog = controller.getUpdateLog();
|
||||||
|
|
||||||
|
this.imageProperty = model.createProperty(FileStorageUpdater.IMAGEFILE);
|
||||||
|
this.thumbProperty = model
|
||||||
|
.createProperty(FileStorageUpdater.IMAGETHUMB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all of the specified properties on a resource, and return a
|
||||||
|
* {@link List} of the {@link String} values.
|
||||||
|
*/
|
||||||
|
protected List<String> getValues(Resource resource, Property property) {
|
||||||
|
List<String> list = new ArrayList<String>();
|
||||||
|
StmtIterator stmts = resource.listProperties(property);
|
||||||
|
try {
|
||||||
|
while (stmts.hasNext()) {
|
||||||
|
Statement stmt = stmts.next();
|
||||||
|
RDFNode object = stmt.getObject();
|
||||||
|
if (object.isLiteral()) {
|
||||||
|
list.add(((Literal) object).getString());
|
||||||
|
} else {
|
||||||
|
updateLog.error(resource,
|
||||||
|
"property value was not a literal: "
|
||||||
|
+ "property is '" + property.getURI()
|
||||||
|
+ "', value is '" + object + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
stmts.close();
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read all of the specified properties on a resource, and return a
|
||||||
|
* {@link List} of the {@link Statement}s.
|
||||||
|
*/
|
||||||
|
protected List<Statement> getStatements(Resource resource, Property property) {
|
||||||
|
List<Statement> list = new ArrayList<Statement>();
|
||||||
|
StmtIterator stmts = resource.listProperties(property);
|
||||||
|
try {
|
||||||
|
while (stmts.hasNext()) {
|
||||||
|
list.add(stmts.next());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
stmts.close();
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the filename within a path so we can add this prefix to it, while
|
||||||
|
* retaining the path.
|
||||||
|
*/
|
||||||
|
protected String addFilenamePrefix(String prefix, String path) {
|
||||||
|
int slashHere = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
|
||||||
|
if (slashHere == -1) {
|
||||||
|
return prefix + path;
|
||||||
|
} else {
|
||||||
|
String dirs = path.substring(0, slashHere + 1);
|
||||||
|
String filename = path.substring(slashHere + 1);
|
||||||
|
return dirs + prefix + filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We are about to create a file - if a file of this name already exists,
|
||||||
|
* increment the name until we have no collision.
|
||||||
|
*
|
||||||
|
* @return the original file, or the file with the incremented name.
|
||||||
|
*/
|
||||||
|
protected File checkNameConflicts(final File file) {
|
||||||
|
if (!file.exists()) {
|
||||||
|
// No conflict.
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
File parent = file.getParentFile();
|
||||||
|
String filename = file.getName();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
File newFile = new File(parent, i + filename);
|
||||||
|
if (!newFile.exists()) {
|
||||||
|
updateLog.log("File '" + file + "' already exists, using '"
|
||||||
|
+ newFile + "' to avoid conflict.");
|
||||||
|
return newFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLog.error("File '" + file
|
||||||
|
+ "' already exists. Unable to avoid conflict.");
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean out the old image directory. Copy the files into the upgrade directory,
|
||||||
|
* separating into the ones that we translated, and the ones that weren't
|
||||||
|
* referenced.
|
||||||
|
*/
|
||||||
|
public class ImageDirectoryCleaner extends FsuScanner {
|
||||||
|
protected final File imageDirectory;
|
||||||
|
protected final File translatedDirectory;
|
||||||
|
protected final File unreferencedDirectory;
|
||||||
|
|
||||||
|
public ImageDirectoryCleaner(FSUController controller) {
|
||||||
|
super(controller);
|
||||||
|
this.imageDirectory = controller.getImageDirectory();
|
||||||
|
this.translatedDirectory = controller.getTranslatedDirectory();
|
||||||
|
this.unreferencedDirectory = controller.getUnreferencedDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all of the files from the old image directory.
|
||||||
|
*/
|
||||||
|
public void clean(Collection<String> translatedFiles) {
|
||||||
|
updateLog.section("Cleaning the old image directory of "
|
||||||
|
+ "files that were translated.");
|
||||||
|
removeTranslatedFiles(translatedFiles);
|
||||||
|
|
||||||
|
updateLog.section("Cleaning the old image directory of "
|
||||||
|
+ "files that were not referenced.");
|
||||||
|
removeRemainingFiles(imageDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move all of the files that we translated into the new system.
|
||||||
|
*/
|
||||||
|
private void removeTranslatedFiles(Collection<String> translatedFiles) {
|
||||||
|
for (String path : translatedFiles) {
|
||||||
|
updateLog.log("moving image file '" + path
|
||||||
|
+ "' to the 'translated' directory.");
|
||||||
|
File oldFile = new File(imageDirectory, path);
|
||||||
|
File deletedFile = new File(translatedDirectory, path);
|
||||||
|
try {
|
||||||
|
FileUtil.moveFile(oldFile, deletedFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateLog.error("Failed to move translated file '"
|
||||||
|
+ oldFile.getAbsolutePath() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go through the images directory, and discard any that remain. They must
|
||||||
|
* not have been referenced by any existing individuals.
|
||||||
|
*/
|
||||||
|
private void removeRemainingFiles(File directory) {
|
||||||
|
updateLog.log("Cleaning image directory '" + directory + "'");
|
||||||
|
try {
|
||||||
|
File targetDirectory = makeCorrespondingDirectory(directory);
|
||||||
|
File[] children = directory.listFiles();
|
||||||
|
if (children != null) {
|
||||||
|
for (File child : children) {
|
||||||
|
if (child.isDirectory()) {
|
||||||
|
removeRemainingFiles(child);
|
||||||
|
} else {
|
||||||
|
moveUnreferencedFile(targetDirectory, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateLog.error("Failed to clean images directory '"
|
||||||
|
+ directory.getAbsolutePath() + "'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move this file from its current location to its new home in the
|
||||||
|
* "unreferenced" directory. Log it.
|
||||||
|
*/
|
||||||
|
private void moveUnreferencedFile(File targetDirectory, File file) {
|
||||||
|
updateLog.log("Moving image file '" + file.getPath()
|
||||||
|
+ "' to the 'unreferenced' directory");
|
||||||
|
try {
|
||||||
|
File newFile = new File(targetDirectory, file.getName());
|
||||||
|
FileUtil.moveFile(file, newFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateLog.error("Can't move unreferenced file '"
|
||||||
|
+ file.getAbsolutePath() + "'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Figure out the path from the "images" directory to this one, and create a
|
||||||
|
* corresponding directory in the "unreferenced" area.
|
||||||
|
*/
|
||||||
|
private File makeCorrespondingDirectory(File directory) throws IOException {
|
||||||
|
String imagesPath = imageDirectory.getAbsolutePath();
|
||||||
|
String thisPath = directory.getAbsolutePath();
|
||||||
|
|
||||||
|
if (!thisPath.startsWith(imagesPath)) {
|
||||||
|
throw new IOException("Can't make a corresponding directory for '"
|
||||||
|
+ thisPath + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
String suffix = thisPath.substring(imagesPath.length());
|
||||||
|
|
||||||
|
File corresponding = new File(unreferencedDirectory, suffix);
|
||||||
|
corresponding.mkdirs();
|
||||||
|
if (!corresponding.exists()) {
|
||||||
|
throw new IOException("Failed to create corresponding directory '"
|
||||||
|
+ corresponding.getAbsolutePath() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return corresponding;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,191 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.ResIterator;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make copies of the main image and thumbnail in the new file storage system,
|
||||||
|
* and in the model. Remove the old properties, but don't remove the old files
|
||||||
|
* yet, in case someone else is referring to them also.
|
||||||
|
*/
|
||||||
|
public class ImageSchemaTranslater extends FsuScanner {
|
||||||
|
protected final File imageDirectory;
|
||||||
|
protected final FileModelHelper fileModelHelper;
|
||||||
|
protected final FileStorage fileStorage;
|
||||||
|
|
||||||
|
public ImageSchemaTranslater(FSUController controller) {
|
||||||
|
super(controller);
|
||||||
|
this.imageDirectory = controller.getImageDirectory();
|
||||||
|
this.fileStorage = controller.getFileStorage();
|
||||||
|
this.fileModelHelper = controller.getFileModelHelper();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By the time we get here, any individual with a main image also has a
|
||||||
|
* thumbnail, and vice versa, and exactly one of each. For each one,
|
||||||
|
* translate the main image and the thumbnail into the new system.
|
||||||
|
*/
|
||||||
|
public Collection<String> translate() {
|
||||||
|
updateLog.section("Copying images into the new file storage, "
|
||||||
|
+ "and adding them to the new model.");
|
||||||
|
|
||||||
|
SortedSet<String> translated = new TreeSet<String>();
|
||||||
|
ResIterator haveImage = model.listResourcesWithProperty(imageProperty);
|
||||||
|
try {
|
||||||
|
while (haveImage.hasNext()) {
|
||||||
|
Resource resource = haveImage.next();
|
||||||
|
translateImages(resource, translated);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
haveImage.close();
|
||||||
|
}
|
||||||
|
return translated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This individual should have exactly one main image and exactly one
|
||||||
|
* thumbnail.
|
||||||
|
* <ul>
|
||||||
|
* <li>Translate the first main image into the new system.</li>
|
||||||
|
* <li>Translate the first thumbnail into the new system.</li>
|
||||||
|
* <li>Remove all old-style main image properties.</li>
|
||||||
|
* <li>Remove all old-style thumbnail properties.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private void translateImages(Resource resource,
|
||||||
|
Collection<String> translated) {
|
||||||
|
List<String> mainImages = getValues(resource, imageProperty);
|
||||||
|
if (mainImages.size() != 1) {
|
||||||
|
updateLog.error(resource, "has " + mainImages.size()
|
||||||
|
+ " main images: " + mainImages);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
translateMainImage(resource, mainImages.get(0));
|
||||||
|
translated.add(mainImages.get(0));
|
||||||
|
resource.removeAll(imageProperty);
|
||||||
|
|
||||||
|
List<String> thumbnails = getValues(resource, thumbProperty);
|
||||||
|
if (thumbnails.size() != 1) {
|
||||||
|
updateLog.error(resource, "has " + thumbnails.size()
|
||||||
|
+ " thumbnails: " + thumbnails);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
translateThumbnail(resource, thumbnails.get(0));
|
||||||
|
translated.add(thumbnails.get(0));
|
||||||
|
resource.removeAll(thumbProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the main image into the new system
|
||||||
|
*/
|
||||||
|
private void translateMainImage(Resource resource, String path) {
|
||||||
|
Individual file = translateFile(resource, path, "main image");
|
||||||
|
Individual person = fileModelHelper.getIndividualByUri(resource
|
||||||
|
.getURI());
|
||||||
|
fileModelHelper.setAsMainImageOnEntity(person, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the thumbnail into the new system.
|
||||||
|
*/
|
||||||
|
private void translateThumbnail(Resource resource, String path) {
|
||||||
|
Individual file = translateFile(resource, path, "thumbnail");
|
||||||
|
Individual person = fileModelHelper.getIndividualByUri(resource
|
||||||
|
.getURI());
|
||||||
|
fileModelHelper.setThumbnailOnIndividual(person, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate an image file into the new system
|
||||||
|
* <ul>
|
||||||
|
* <li>Create a new File, FileByteStream.</li>
|
||||||
|
* <li>Attempt to infer MIME type.</li>
|
||||||
|
* <li>Copy into the File system.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return the new File surrogate.
|
||||||
|
*/
|
||||||
|
private Individual translateFile(Resource resource, String path,
|
||||||
|
String label) {
|
||||||
|
File oldFile = new File(imageDirectory, path);
|
||||||
|
String filename = getSimpleFilename(path);
|
||||||
|
String mimeType = guessMimeType(resource, filename);
|
||||||
|
|
||||||
|
// Create the file individuals in the model
|
||||||
|
Individual byteStream = fileModelHelper.createByteStreamIndividual();
|
||||||
|
Individual file = fileModelHelper.createFileIndividual(mimeType,
|
||||||
|
filename, byteStream);
|
||||||
|
|
||||||
|
updateLog.log(resource, "translating " + label + " '" + path
|
||||||
|
+ "' into the file storage as '" + file.getURI() + "'");
|
||||||
|
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
// Store the file in the FileStorage system.
|
||||||
|
inputStream = new FileInputStream(oldFile);
|
||||||
|
fileStorage.createFile(byteStream.getURI(), filename, inputStream);
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateLog.error(resource, "Can't create the " + label + " file. ",
|
||||||
|
e);
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
try {
|
||||||
|
inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any path parts, and just get the filename and extension.
|
||||||
|
*/
|
||||||
|
private String getSimpleFilename(String path) {
|
||||||
|
return FilenameUtils.getName(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guess what the MIME type might be.
|
||||||
|
*/
|
||||||
|
private String guessMimeType(Resource resource, String filename) {
|
||||||
|
if (filename.endsWith(".gif") || filename.endsWith(".GIF")) {
|
||||||
|
return "image/gif";
|
||||||
|
} else if (filename.endsWith(".png") || filename.endsWith(".PNG")) {
|
||||||
|
return "image/png";
|
||||||
|
} else if (filename.endsWith(".jpg") || filename.endsWith(".JPG")) {
|
||||||
|
return "image/jpeg";
|
||||||
|
} else if (filename.endsWith(".jpeg") || filename.endsWith(".JPEG")) {
|
||||||
|
return "image/jpeg";
|
||||||
|
} else if (filename.endsWith(".jpe") || filename.endsWith(".JPE")) {
|
||||||
|
return "image/jpeg";
|
||||||
|
} else {
|
||||||
|
updateLog.warn(resource,
|
||||||
|
"can't recognize the MIME type of this image file: '"
|
||||||
|
+ filename + "'");
|
||||||
|
return "image";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.Literal;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Property;
|
||||||
|
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||||
|
import com.hp.hpl.jena.rdf.model.ResIterator;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Statement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a resource has more than one image or more than one thumbnail, this
|
||||||
|
* discards the extras.
|
||||||
|
*/
|
||||||
|
public class MultiplePropertyRemover extends FsuScanner {
|
||||||
|
|
||||||
|
public MultiplePropertyRemover(FSUController controller) {
|
||||||
|
super(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By now, we have removed any non-literals or dead ends, so keep the first
|
||||||
|
* one and discard any extras.
|
||||||
|
*/
|
||||||
|
public void remove() {
|
||||||
|
updateLog.section("Checking for resources with more "
|
||||||
|
+ "than one main image, or more than one thumbnail.");
|
||||||
|
|
||||||
|
removeExtraProperties(imageProperty, "main image");
|
||||||
|
removeExtraProperties(thumbProperty, "thumbnail");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check each resource that has this property.
|
||||||
|
*/
|
||||||
|
public void removeExtraProperties(Property prop, String label) {
|
||||||
|
ResIterator resources = model.listResourcesWithProperty(prop);
|
||||||
|
try {
|
||||||
|
while (resources.hasNext()) {
|
||||||
|
Resource resource = resources.next();
|
||||||
|
removeExtraProperties(resource, prop, label);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
resources.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this resource has more than one of this property, delete the extras.
|
||||||
|
*/
|
||||||
|
private void removeExtraProperties(Resource resource, Property prop,
|
||||||
|
String label) {
|
||||||
|
List<Statement> stmts = getStatements(resource, prop);
|
||||||
|
for (int i = 1; i < stmts.size(); i++) {
|
||||||
|
Statement stmt = stmts.get(i);
|
||||||
|
RDFNode node = stmt.getObject();
|
||||||
|
if (node.isLiteral()) {
|
||||||
|
String value = ((Literal) node).getString();
|
||||||
|
updateLog.warn(resource, "removing extra " + label
|
||||||
|
+ " property: '" + value + "'");
|
||||||
|
} else {
|
||||||
|
updateLog.warn(resource, "removing extra " + label
|
||||||
|
+ " property: '" + node + "'");
|
||||||
|
}
|
||||||
|
model.remove(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.ResIterator;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust any individual that has a main image but no thumbnail.
|
||||||
|
*/
|
||||||
|
public class NoThumbsAdjuster extends FsuScanner {
|
||||||
|
protected final File imageDirectory;
|
||||||
|
|
||||||
|
public NoThumbsAdjuster(FSUController controller) {
|
||||||
|
super(controller);
|
||||||
|
this.imageDirectory = controller.getImageDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For every individual with main images but no thumbnails, create a
|
||||||
|
* thumbnail from the first main image.
|
||||||
|
*/
|
||||||
|
public void adjust() {
|
||||||
|
updateLog.section("Creating thumbnails to match main images.");
|
||||||
|
|
||||||
|
ResIterator haveImage = model.listResourcesWithProperty(imageProperty);
|
||||||
|
try {
|
||||||
|
while (haveImage.hasNext()) {
|
||||||
|
Resource resource = haveImage.next();
|
||||||
|
|
||||||
|
if (resource.getProperty(thumbProperty) == null) {
|
||||||
|
createThumbnailFromMainImage(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
haveImage.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This individual has a main image but no thumbnail. Create one.
|
||||||
|
* <ul>
|
||||||
|
* <li>Figure a name for the thumbnail image.</li>
|
||||||
|
* <li>Make a scaled copy of the main image into the thumbnail.</li>
|
||||||
|
* <li>Set that file as a thumbnail (old-style) on the individual.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private void createThumbnailFromMainImage(Resource resource) {
|
||||||
|
String mainFilename = getValues(resource, imageProperty).get(0);
|
||||||
|
String thumbFilename = addFilenamePrefix("_thumbnail_", mainFilename);
|
||||||
|
updateLog.log(resource, "creating a thumbnail at '" + thumbFilename
|
||||||
|
+ "' from the main image at '" + mainFilename + "'");
|
||||||
|
|
||||||
|
File mainFile = new File(imageDirectory, mainFilename);
|
||||||
|
File thumbFile = new File(imageDirectory, thumbFilename);
|
||||||
|
thumbFile = checkNameConflicts(thumbFile);
|
||||||
|
try {
|
||||||
|
generateThumbnailImage(mainFile, thumbFile,
|
||||||
|
FileStorageUpdater.THUMBNAIL_WIDTH,
|
||||||
|
FileStorageUpdater.THUMBNAIL_HEIGHT);
|
||||||
|
|
||||||
|
resource.addProperty(thumbProperty, thumbFilename);
|
||||||
|
} catch (IOException e) {
|
||||||
|
updateLog.error(resource, "failed to create thumbnail file '"
|
||||||
|
+ thumbFilename + "'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read in the main image, and scale it to a thumbnail that maintains the
|
||||||
|
* aspect ratio, but doesn't exceed either of these dimensions.
|
||||||
|
*/
|
||||||
|
private void generateThumbnailImage(File mainFile, File thumbFile,
|
||||||
|
int maxWidth, int maxHeight) throws IOException {
|
||||||
|
BufferedImage bsrc = ImageIO.read(mainFile);
|
||||||
|
|
||||||
|
double scale = Math.min(((double) maxWidth) / bsrc.getWidth(),
|
||||||
|
((double) maxHeight) / bsrc.getHeight());
|
||||||
|
AffineTransform at = AffineTransform.getScaleInstance(scale, scale);
|
||||||
|
int newWidth = (int) (scale * bsrc.getWidth());
|
||||||
|
int newHeight = (int) (scale * bsrc.getHeight());
|
||||||
|
updateLog.log("Scaling '" + mainFile + "' by a factor of " + scale
|
||||||
|
+ ", from " + bsrc.getWidth() + "x" + bsrc.getHeight() + " to "
|
||||||
|
+ newWidth + "x" + newHeight);
|
||||||
|
|
||||||
|
BufferedImage bdest = new BufferedImage(newWidth, newHeight,
|
||||||
|
BufferedImage.TYPE_INT_RGB);
|
||||||
|
Graphics2D g = bdest.createGraphics();
|
||||||
|
|
||||||
|
g.drawRenderedImage(bsrc, at);
|
||||||
|
|
||||||
|
ImageIO.write(bdest, "JPG", thumbFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.updater;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.rdf.model.Literal;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Property;
|
||||||
|
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||||
|
import com.hp.hpl.jena.rdf.model.ResIterator;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Statement;
|
||||||
|
import com.hp.hpl.jena.rdf.model.StmtIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All image properties should have literal values. Burn any that don't.
|
||||||
|
*/
|
||||||
|
public class NonLiteralPropertyRemover extends FsuScanner {
|
||||||
|
|
||||||
|
public NonLiteralPropertyRemover(FSUController controller) {
|
||||||
|
super(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any image properties whose objects are not {@link Literal}s.
|
||||||
|
*/
|
||||||
|
public void remove() {
|
||||||
|
updateLog.section("Checking for image properties whose objects "
|
||||||
|
+ "are not literals.");
|
||||||
|
|
||||||
|
removeNonLiterals(imageProperty, "image file");
|
||||||
|
removeNonLiterals(thumbProperty, "thumnail");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check all resources for bogus values on this property.
|
||||||
|
*/
|
||||||
|
private void removeNonLiterals(Property prop, String label) {
|
||||||
|
ResIterator resources = model.listResourcesWithProperty(prop);
|
||||||
|
try {
|
||||||
|
while (resources.hasNext()) {
|
||||||
|
Resource resource = resources.next();
|
||||||
|
removeNonLiterals(resource, prop, label);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
resources.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check this resource for bogus values onthis property.
|
||||||
|
*/
|
||||||
|
private void removeNonLiterals(Resource resource, Property prop,
|
||||||
|
String label) {
|
||||||
|
List<RDFNode> bogusValues = new ArrayList<RDFNode>();
|
||||||
|
StmtIterator stmts = resource.listProperties(prop);
|
||||||
|
try {
|
||||||
|
while (stmts.hasNext()) {
|
||||||
|
Statement stmt = stmts.next();
|
||||||
|
RDFNode object = stmt.getObject();
|
||||||
|
if (!object.isLiteral()) {
|
||||||
|
bogusValues.add(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
stmts.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RDFNode bogusValue : bogusValues) {
|
||||||
|
updateLog.warn(resource, "discarding " + label
|
||||||
|
+ " property with non-literal as object: '" + bogusValue
|
||||||
|
+ "'");
|
||||||
|
model.createStatement(resource, prop, bogusValue).remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.servlet.setup;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
|
import javax.servlet.ServletContextListener;
|
||||||
|
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.ontology.OntModel;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.jena.JenaBaseDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.updater.FileStorageUpdater;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
public class UpdateUploadedFiles implements ServletContextListener {
|
||||||
|
private static final Logger log = Logger
|
||||||
|
.getLogger(UpdateUploadedFiles.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nothing to do on teardown.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void contextDestroyed(ServletContextEvent sce) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the ontology model, the old upload directory, and the file
|
||||||
|
* storage system are all valid. Then do the update.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void contextInitialized(ServletContextEvent sce) {
|
||||||
|
try {
|
||||||
|
ServletContext ctx = sce.getServletContext();
|
||||||
|
|
||||||
|
WebappDaoFactory wadf = (WebappDaoFactory) ctx
|
||||||
|
.getAttribute("webappDaoFactory");
|
||||||
|
if (wadf == null) {
|
||||||
|
throw new IllegalStateException("Webapp DAO Factory is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
OntModel jenaOntModel = (OntModel) ctx
|
||||||
|
.getAttribute(JenaBaseDao.JENA_ONT_MODEL_ATTRIBUTE_NAME);
|
||||||
|
if (jenaOntModel == null) {
|
||||||
|
throw new IllegalStateException("Ontology model is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
FileStorage fileStorage = (FileStorage) ctx
|
||||||
|
.getAttribute(FileStorageSetup.ATTRIBUTE_NAME);
|
||||||
|
if (fileStorage == null) {
|
||||||
|
throw new IllegalStateException("File storage system is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
String uploadDirectoryName = ConfigurationProperties
|
||||||
|
.getProperty(FileStorageSetup.PROPERTY_FILE_STORAGE_BASE_DIR);
|
||||||
|
if (uploadDirectoryName == null) {
|
||||||
|
throw new IllegalStateException("Upload directory name is null");
|
||||||
|
}
|
||||||
|
File uploadDirectory = new File(uploadDirectoryName);
|
||||||
|
if (!uploadDirectory.exists()) {
|
||||||
|
throw new IllegalStateException("Upload directory '"
|
||||||
|
+ uploadDirectory.getAbsolutePath()
|
||||||
|
+ "' does not exist.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FileStorageUpdater fsu = new FileStorageUpdater(wadf, jenaOntModel,
|
||||||
|
fileStorage, uploadDirectory);
|
||||||
|
fsu.update();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Unknown problem", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,6 @@ public class FrontEndEditingUtils {
|
||||||
private static final List<String> VITRO_NS_DATA_PROPS = Arrays.asList(VitroVocabulary.BLURB,
|
private static final List<String> VITRO_NS_DATA_PROPS = Arrays.asList(VitroVocabulary.BLURB,
|
||||||
VitroVocabulary.CITATION,
|
VitroVocabulary.CITATION,
|
||||||
VitroVocabulary.DESCRIPTION,
|
VitroVocabulary.DESCRIPTION,
|
||||||
VitroVocabulary.IMAGETHUMB,
|
|
||||||
VitroVocabulary.LABEL,
|
VitroVocabulary.LABEL,
|
||||||
VitroVocabulary.MONIKER
|
VitroVocabulary.MONIKER
|
||||||
// VitroVocabulary.RDF_TYPE,
|
// VitroVocabulary.RDF_TYPE,
|
||||||
|
|
|
@ -108,11 +108,11 @@ public class IndividualView extends ViewObject {
|
||||||
return individual.getKeywords();
|
return individual.getKeywords();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getImageFile() {
|
public String getImageUrl() {
|
||||||
return individual.getImageFile();
|
return individual.getImageUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getImageThumb() {
|
public String getThumbUrl() {
|
||||||
return individual.getImageThumb();
|
return individual.getThumbUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,7 +235,7 @@ public class PropertyEditLinks extends TagSupport{
|
||||||
if( contains( allowedAccessTypeArray, EditLinkAccess.ADDNEW ) ){
|
if( contains( allowedAccessTypeArray, EditLinkAccess.ADDNEW ) ){
|
||||||
log.debug("vitro namespace property "+propertyUri+" gets an \"add\" link");
|
log.debug("vitro namespace property "+propertyUri+" gets an \"add\" link");
|
||||||
LinkStruct ls = null;
|
LinkStruct ls = null;
|
||||||
if (propertyUri.equals(VitroVocabulary.IMAGETHUMB)) {
|
if (propertyUri.equals(VitroVocabulary.IND_MAIN_IMAGE)) {
|
||||||
ls = getImageLink(subjectUri, contextPath, "add");
|
ls = getImageLink(subjectUri, contextPath, "add");
|
||||||
} else {
|
} else {
|
||||||
String url = makeRelativeHref(contextPath +"edit/editDatapropStmtRequestDispatch.jsp",
|
String url = makeRelativeHref(contextPath +"edit/editDatapropStmtRequestDispatch.jsp",
|
||||||
|
@ -346,7 +346,7 @@ public class PropertyEditLinks extends TagSupport{
|
||||||
|
|
||||||
LinkStruct[] links = new LinkStruct[2];
|
LinkStruct[] links = new LinkStruct[2];
|
||||||
|
|
||||||
if (predicateUri.equals(VitroVocabulary.IMAGETHUMB)) {
|
if (predicateUri.equals(VitroVocabulary.IND_MAIN_IMAGE)) {
|
||||||
if( contains( allowedAccessTypeArray, EditLinkAccess.MODIFY ) ){
|
if( contains( allowedAccessTypeArray, EditLinkAccess.MODIFY ) ){
|
||||||
log.debug("permission found to UPDATE vitro namepsace property statement "+ predicateUri);
|
log.debug("permission found to UPDATE vitro namepsace property statement "+ predicateUri);
|
||||||
links[0] = getImageLink(subjectUri, contextPath, "edit");
|
links[0] = getImageLink(subjectUri, contextPath, "edit");
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.dao.jena;
|
package edu.cornell.mannlib.vitro.webapp.dao.jena;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
@ -15,7 +17,6 @@ import com.hp.hpl.jena.ontology.OntProperty;
|
||||||
import com.hp.hpl.jena.ontology.Restriction;
|
import com.hp.hpl.jena.ontology.Restriction;
|
||||||
import com.hp.hpl.jena.rdf.model.Model;
|
import com.hp.hpl.jena.rdf.model.Model;
|
||||||
import com.hp.hpl.jena.rdf.model.ModelFactory;
|
import com.hp.hpl.jena.rdf.model.ModelFactory;
|
||||||
import com.hp.hpl.jena.rdf.model.StmtIterator;
|
|
||||||
import com.hp.hpl.jena.vocabulary.OWL;
|
import com.hp.hpl.jena.vocabulary.OWL;
|
||||||
import com.hp.hpl.jena.vocabulary.RDF;
|
import com.hp.hpl.jena.vocabulary.RDF;
|
||||||
import com.hp.hpl.jena.vocabulary.RDFS;
|
import com.hp.hpl.jena.vocabulary.RDFS;
|
||||||
|
@ -111,11 +112,7 @@ public class JenaBaseDaoTest {
|
||||||
|
|
||||||
Model expectedModel = (ModelFactory.createOntologyModel()).read(new StringReader(expected), "", "N3");
|
Model expectedModel = (ModelFactory.createOntologyModel()).read(new StringReader(expected), "", "N3");
|
||||||
|
|
||||||
//modtime times make it difficult to compare graphs
|
assertEquivalentModels(expectedModel, ontModel);
|
||||||
wipeOutModTime(expectedModel);
|
|
||||||
wipeOutModTime(ontModel);
|
|
||||||
|
|
||||||
Assert.assertTrue( ontModel.isIsomorphicWith(expectedModel) );
|
|
||||||
} catch (InsertException e) {
|
} catch (InsertException e) {
|
||||||
Assert.fail(e.getMessage());
|
Assert.fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -192,11 +189,7 @@ public class JenaBaseDaoTest {
|
||||||
|
|
||||||
Model expectedModel = (ModelFactory.createOntologyModel()).read(new StringReader(expected), "", "N3");
|
Model expectedModel = (ModelFactory.createOntologyModel()).read(new StringReader(expected), "", "N3");
|
||||||
|
|
||||||
//modtime times make it difficult to compare graphs
|
assertEquivalentModels(expectedModel, model);
|
||||||
wipeOutModTime(expectedModel);
|
|
||||||
wipeOutModTime(model);
|
|
||||||
|
|
||||||
Assert.assertTrue( model.isIsomorphicWith(expectedModel));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -256,11 +249,7 @@ public class JenaBaseDaoTest {
|
||||||
|
|
||||||
Model expectedModel = (ModelFactory.createOntologyModel()).read(new StringReader(expected), "", "N3");
|
Model expectedModel = (ModelFactory.createOntologyModel()).read(new StringReader(expected), "", "N3");
|
||||||
|
|
||||||
//modtime times make it difficult to compare graphs
|
assertEquivalentModels(expectedModel, ontModel);
|
||||||
wipeOutModTime(expectedModel);
|
|
||||||
wipeOutModTime(ontModel);
|
|
||||||
|
|
||||||
Assert.assertTrue( ontModel.isIsomorphicWith(expectedModel) );
|
|
||||||
} catch (InsertException e) {
|
} catch (InsertException e) {
|
||||||
Assert.fail(e.getMessage());
|
Assert.fail(e.getMessage());
|
||||||
}
|
}
|
||||||
|
@ -367,16 +356,6 @@ public class JenaBaseDaoTest {
|
||||||
// wipeOutModTime(model);
|
// wipeOutModTime(model);
|
||||||
// Assert.assertTrue( model.isIsomorphicWith(expectedModel));
|
// Assert.assertTrue( model.isIsomorphicWith(expectedModel));
|
||||||
// }
|
// }
|
||||||
void printModels(Model expected, Model result){
|
|
||||||
System.out.println("Expected:");
|
|
||||||
expected.write(System.out);
|
|
||||||
System.out.println("Result:");
|
|
||||||
result.write(System.out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wipeOutModTime(Model model){
|
|
||||||
model.removeAll(null, model.createProperty(VitroVocabulary.MODTIME), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
/**
|
/**
|
||||||
|
@ -479,4 +458,31 @@ public class JenaBaseDaoTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare the contents of the expected model with the actual model (not counting modification times).
|
||||||
|
*/
|
||||||
|
private void assertEquivalentModels(Model expected, Model actual) {
|
||||||
|
// modtime times make it difficult to compare graphs
|
||||||
|
wipeOutModTime(expected);
|
||||||
|
wipeOutModTime(actual);
|
||||||
|
|
||||||
|
if (actual.isIsomorphicWith(expected)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
PrintStream p = new PrintStream(out, true);
|
||||||
|
p.println("Models do not match: expected <");
|
||||||
|
expected.write(out);
|
||||||
|
p.println("> but was <");
|
||||||
|
actual.write(out);
|
||||||
|
p.println(">");
|
||||||
|
Assert.fail(out.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void wipeOutModTime(Model model){
|
||||||
|
model.removeAll(null, model.createProperty(VitroVocabulary.MODTIME), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.naming.InitialContext;
|
||||||
|
|
||||||
|
import org.apache.log4j.Level;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import stubs.javax.naming.InitialContextStub;
|
||||||
|
import stubs.javax.naming.spi.InitialContextFactoryStub;
|
||||||
|
import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.ConfigurationProperties;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public class FileServingHelperTest extends AbstractTestClass {
|
||||||
|
private static final String DEFAULT_NAMESPACE = "http://some.crazy.domain/individual/";
|
||||||
|
private static final String CONFIG_PROPERTIES = "#mock config properties file\n";
|
||||||
|
private static File tempDir;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// framework
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use a mock {@link InitialContext} to create an empty
|
||||||
|
* {@link ConfigurationProperties} object. Each test can use
|
||||||
|
* {@link #setConfigurationProperties(String, String)} to populate it as
|
||||||
|
* they choose.
|
||||||
|
*/
|
||||||
|
@BeforeClass
|
||||||
|
public static void createConfigurationProperties() throws Exception {
|
||||||
|
tempDir = createTempDirectory("FileServingHelperTest");
|
||||||
|
|
||||||
|
File propsFile = createFile(tempDir, "config.properties",
|
||||||
|
CONFIG_PROPERTIES);
|
||||||
|
|
||||||
|
System.setProperty(InitialContext.INITIAL_CONTEXT_FACTORY,
|
||||||
|
InitialContextFactoryStub.class.getName());
|
||||||
|
InitialContextStub.reset();
|
||||||
|
new InitialContext().bind("java:comp/env/path.configuration", propsFile
|
||||||
|
.getPath());
|
||||||
|
|
||||||
|
setConfigurationProperties(DEFAULT_NAMESPACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void cleanup() {
|
||||||
|
purgeDirectoryRecursively(tempDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// tests
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nullUri() {
|
||||||
|
assertCorrectUrl(null, "somefilename.ext", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nullFilename() {
|
||||||
|
assertCorrectUrl("http://some.crazy.domain/individual/n4324", null,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notInDefaultNamespace() {
|
||||||
|
setLoggerLevel(FileServingHelper.class, Level.ERROR);
|
||||||
|
assertCorrectUrl("notInTheNamespace",
|
||||||
|
"somefilename.ext", "notInTheNamespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void inDefaultNamespaceNoTrailingSlash() {
|
||||||
|
assertCorrectUrl("http://some.crazy.domain/individual/n4324",
|
||||||
|
"somefilename.ext", "/file/n4324/somefilename.ext");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void inDefaultNamespaceTrailingSlash() {
|
||||||
|
assertCorrectUrl("http://some.crazy.domain/individual/n4324/",
|
||||||
|
"somefilename.ext", "/file/n4324/somefilename.ext");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Helper methods
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static void setConfigurationProperties(String defaultNamespace) {
|
||||||
|
Map<String, String> map = new HashMap<String, String>();
|
||||||
|
map.put(FileStorageSetup.PROPERTY_DEFAULT_NAMESPACE, defaultNamespace);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field f = ConfigurationProperties.class.getDeclaredField("theMap");
|
||||||
|
f.setAccessible(true);
|
||||||
|
f.set(null, map);
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("Exception while setting config properties: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertCorrectUrl(String uri, String filename, String expected) {
|
||||||
|
String actual = FileServingHelper.getBytestreamAliasUrl(uri, filename);
|
||||||
|
assertEquals("url", expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -103,17 +103,5 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr class="editformcell" id='thumbnailFilenameTr'>
|
|
||||||
<td id="thumbnailTd" valign="bottom" colspan="1">
|
|
||||||
<b>Thumbnail Filename</b> <em>Optional and usually more convenient to upload from previous screen</em><br/>
|
|
||||||
<input type="text" name="ImageThumb" value="<form:value name="ImageThumb"/>" maxlength="255" />
|
|
||||||
<p class="error"><form:error name="ImageThumb"/></p>
|
|
||||||
</td>
|
|
||||||
<td id="optionalImageTd" valign="bottom" colspan="1">
|
|
||||||
<b>Optional Larger Image</b> <em>(filename or full path)</em>
|
|
||||||
<input type="text" name="ImageFile" value="<form:value name="ImageFile"/>" maxlength="255" />
|
|
||||||
<p class="error"><form:error name="ImageFile"/></p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -90,18 +90,6 @@
|
||||||
<input type="text" name="Timekey" value="<form:value name="Timekey"/>" size="19" maxlength="19"><br>
|
<input type="text" name="Timekey" value="<form:value name="Timekey"/>" size="19" maxlength="19"><br>
|
||||||
<font size="2" color="red"><form:error name="Timekey"/></font>
|
<font size="2" color="red"><form:error name="Timekey"/></font>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
|
||||||
<tr class="editformcell" id='thumbnailFilenameTr'>
|
|
||||||
<td id="thumbnailTd" valign="bottom" colspan="1">
|
|
||||||
<b>Thumbnail Filename</b> <em>Optional and usually more convenient to upload from previous screen</em>
|
|
||||||
<input type="text" name="ThumbnailFilename" value="<form:value name="ThumbnailFilename"/>" size="60" maxlength="255" />
|
|
||||||
<font size="2" color="red"><form:error name="ThumbnailFilename"/></font>
|
|
||||||
</td>
|
|
||||||
<td id="optionalImageTd" valign="bottom" colspan="1">
|
|
||||||
<b>Optional Larger Image</b> <em>(filename or full path)</em>
|
|
||||||
<input type="text" name="LargerImage" value="<form:value name="LargerImage"/>" size="60" maxlength="255" />
|
|
||||||
<font size="2" color="red"><form:error name="LargerImage"/></font>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="editformcell" id="portalFlagsTr">
|
<tr class="editformcell" id="portalFlagsTr">
|
||||||
<td id="portalFlagsTd" valign="bottom" colspan="3">
|
<td id="portalFlagsTd" valign="bottom" colspan="3">
|
||||||
|
|
|
@ -63,18 +63,8 @@ if (VitroRequestPrep.isSelfEditing(request) || LoginFormBean.loggedIn(request, L
|
||||||
<c:set var="showEdits" value="${showSelfEdits || showCuratorEdits}" scope="request"/>
|
<c:set var="showEdits" value="${showSelfEdits || showCuratorEdits}" scope="request"/>
|
||||||
<c:set var="editingClass" value="${showEdits ? 'editing' : ''}" scope="request"/>
|
<c:set var="editingClass" value="${showEdits ? 'editing' : ''}" scope="request"/>
|
||||||
|
|
||||||
<c:set var='imageDir' value='images' />
|
|
||||||
<c:set var="themeDir"><c:out value="${portalBean.themeDir}" /></c:set>
|
<c:set var="themeDir"><c:out value="${portalBean.themeDir}" /></c:set>
|
||||||
<%
|
<%
|
||||||
//here we build up the url for the larger image.
|
|
||||||
String imageUrl = null;
|
|
||||||
if (entity.getImageFile() != null &&
|
|
||||||
entity.getImageFile().indexOf("http:")==0) {
|
|
||||||
imageUrl = entity.getImageFile();
|
|
||||||
} else {
|
|
||||||
imageUrl = response.encodeURL( "/images/" + entity.getImageFile() );
|
|
||||||
}
|
|
||||||
|
|
||||||
//anytime we are at an entity page we shouldn't have an editing config or submission
|
//anytime we are at an entity page we shouldn't have an editing config or submission
|
||||||
session.removeAttribute("editjson");
|
session.removeAttribute("editjson");
|
||||||
EditConfiguration.clearAllConfigsInSession(session);
|
EditConfiguration.clearAllConfigsInSession(session);
|
||||||
|
@ -196,28 +186,26 @@ if (VitroRequestPrep.isSelfEditing(request) || LoginFormBean.loggedIn(request, L
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
||||||
<%-- Thumbnail (with citation) --%>
|
<%-- Thumbnail (with citation) --%>
|
||||||
<c:if test="${showEdits || !empty entity.imageThumb}">
|
<c:if test="${showEdits || !empty entity.thumbUrl}">
|
||||||
<div id="dprop-vitro-image" class="propsItem ${editingClass}">
|
<div id="dprop-vitro-image" class="propsItem ${editingClass}">
|
||||||
<c:set var="mayEditThumbnail"><edLnk:editLinks item="<%= VitroVocabulary.IMAGETHUMB %>" icons="false" /></c:set>
|
<c:set var="mayEditImage"><edLnk:editLinks item="<%= VitroVocabulary.IND_MAIN_IMAGE %>" icons="false" /></c:set>
|
||||||
<c:if test="${showEdits and !empty mayEditThumbnail}">
|
<c:if test="${showEdits and !empty mayEditImage}">
|
||||||
<h3 class="propertyName">image</h3>
|
<h3 class="propertyName">image</h3>
|
||||||
<edLnk:editLinks item="<%= VitroVocabulary.IMAGETHUMB %>" icons="false" />
|
<edLnk:editLinks item="<%= VitroVocabulary.IND_MAIN_IMAGE %>" icons="false" />
|
||||||
</c:if>
|
</c:if>
|
||||||
<c:if test="${!empty entity.imageThumb}">
|
<c:if test="${!empty entity.thumbUrl}">
|
||||||
<div class="datatypeProperties">
|
<div class="datatypeProperties">
|
||||||
<div class="datatypePropertyValue">
|
<div class="datatypePropertyValue">
|
||||||
<div class="statementWrap thumbnail">
|
<div class="statementWrap thumbnail">
|
||||||
<c:set var="imageTitle" value="${entity.name}" />
|
<c:set var="imageTitle" value="${entity.name}" />
|
||||||
<c:if test="${!empty entity.imageFile}">
|
<c:if test="${!empty entity.imageUrl}">
|
||||||
<c:url var="imageUrl" value="/${imageDir}/${entity.imageFile}" />
|
<a class="image" href="<c:url value='${entity.imageUrl}'/>">
|
||||||
<a class="image" href="${imageUrl}">
|
|
||||||
<c:set var="imageTitle" value="click to view larger image in new window" />
|
<c:set var="imageTitle" value="click to view larger image in new window" />
|
||||||
</c:if>
|
</c:if>
|
||||||
<c:url var="imageSrc" value='/${imageDir}/${entity.imageThumb}'/>
|
<img src="<c:url value='${entity.thumbUrl}'/>" title="${imageTitle}" alt="" width="150"/>
|
||||||
<img src="<c:out value="${imageSrc}"/>" title="${imageTitle}" alt="" width="150"/>
|
<c:if test="${!empty entity.imageUrl}"></a></c:if>
|
||||||
<c:if test="${!empty entity.imageFile}"></a></c:if>
|
|
||||||
<c:if test="${showEdits}">
|
<c:if test="${showEdits}">
|
||||||
<c:set var="editLinks"><edLnk:editLinks item="<%= VitroVocabulary.IMAGETHUMB %>" data="${entity.imageThumb}" icons="false"/></c:set>
|
<c:set var="editLinks"><edLnk:editLinks item="<%= VitroVocabulary.IND_MAIN_IMAGE %>" data="${entity.thumbUrl}" icons="false"/></c:set>
|
||||||
<c:if test="${!empty editLinks}"><span class="editLinks">${editLinks}</span></c:if>
|
<c:if test="${!empty editLinks}"><span class="editLinks">${editLinks}</span></c:if>
|
||||||
</c:if>
|
</c:if>
|
||||||
</div>
|
</div>
|
||||||
|
@ -282,7 +270,7 @@ if (VitroRequestPrep.isSelfEditing(request) || LoginFormBean.loggedIn(request, L
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
||||||
<%-- Citation, if no thumbnail --%>
|
<%-- Citation, if no thumbnail --%>
|
||||||
<c:if test="${empty entity.imageThumb}">
|
<c:if test="${empty entity.thumbUrl}">
|
||||||
<jsp:include page="entityCitation.jsp" />
|
<jsp:include page="entityCitation.jsp" />
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
<c:out value="${requestScope.columns}" default="8"/>
|
<c:out value="${requestScope.columns}" default="8"/>
|
||||||
</c:set>
|
</c:set>
|
||||||
|
|
||||||
<c:set var='IMG_DIR' value='images/' />
|
|
||||||
<c:set var='IMG_WIDTH' value='100'/>
|
<c:set var='IMG_WIDTH' value='100'/>
|
||||||
|
|
||||||
<table class='tabEntities entityListForGalleryTab'>
|
<table class='tabEntities entityListForGalleryTab'>
|
||||||
|
@ -43,15 +42,14 @@
|
||||||
<c:forEach var='col' begin="1" end="${columns}" step="1">
|
<c:forEach var='col' begin="1" end="${columns}" step="1">
|
||||||
<c:set var='ent' value='${entities[count]}'/>
|
<c:set var='ent' value='${entities[count]}'/>
|
||||||
<c:set var='count' value='${count + 1}'/>
|
<c:set var='count' value='${count + 1}'/>
|
||||||
<c:if test="${ not empty ent and not empty ent.imageThumb}">
|
<c:if test="${ not empty ent and not empty ent.thumbUrl}">
|
||||||
<td>
|
<td>
|
||||||
<c:url var="entityHref" value="/entity">
|
<c:url var="entityHref" value="/entity">
|
||||||
<c:param name="home" value="${portal.portalId}"/>
|
<c:param name="home" value="${portal.portalId}"/>
|
||||||
<c:param name="uri" value="${ent.URI}"/>
|
<c:param name="uri" value="${ent.URI}"/>
|
||||||
</c:url>
|
</c:url>
|
||||||
<a class="image" href="<c:out value="${entityHref}"/>" >
|
<a class="image" href="<c:out value="${entityHref}"/>" >
|
||||||
<c:url var="imageSrc" value="${IMG_DIR}${ent.imageThumb}"/>
|
<img width="${IMG_WIDTH}" src="<c:url value='${ent.thumbUrl}'/>" title="${ent.name}" alt="${ent.name}" />
|
||||||
<img width="${IMG_WIDTH}" src="<c:out value="${imageSrc}"/>" title="${ent.name}" alt="${ent.name}" />
|
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
<c:set var="searchViewPrefix" value="/templates/search/"/>
|
<c:set var="searchViewPrefix" value="/templates/search/"/>
|
||||||
<c:set var='entities' value='${requestScope.entities}' /><%/* just moving this into page scope for easy use */ %>
|
<c:set var='entities' value='${requestScope.entities}' /><%/* just moving this into page scope for easy use */ %>
|
||||||
<c:set var='portal' value='${requestScope.portal}' />
|
<c:set var='portal' value='${requestScope.portal}' />
|
||||||
<c:set var='IMG_DIR' value='images/' />
|
|
||||||
<c:set var='IMG_WIDTH' value='75'/>
|
<c:set var='IMG_WIDTH' value='75'/>
|
||||||
<jsp:include page="/templates/alpha/alphaIndex.jsp"/>
|
<jsp:include page="/templates/alpha/alphaIndex.jsp"/>
|
||||||
<ul class='tabEntities entityListForTab'>
|
<ul class='tabEntities entityListForTab'>
|
||||||
|
@ -76,13 +75,12 @@
|
||||||
</c:if>
|
</c:if>
|
||||||
<c:forEach items='${ent.linksList}' var="entLink"><span class="tab-extLink"> | <c:url var="entLinkUrl" value="${entLink.url}"/><a class="externalLink" href="<c:out value="${entLinkUrl}"/>">${entLink.anchor}</a></span></c:forEach>
|
<c:forEach items='${ent.linksList}' var="entLink"><span class="tab-extLink"> | <c:url var="entLinkUrl" value="${entLink.url}"/><a class="externalLink" href="<c:out value="${entLinkUrl}"/>">${entLink.anchor}</a></span></c:forEach>
|
||||||
<c:choose>
|
<c:choose>
|
||||||
<c:when test='${not empty ent.imageThumb }'>
|
<c:when test='${not empty ent.thumbUrl }'>
|
||||||
<c:url var="imageHref" value="entity">
|
<c:url var="imageHref" value="entity">
|
||||||
<c:param name="home" value="${sessionScope.currentPortalId}"/>
|
<c:param name="home" value="${sessionScope.currentPortalId}"/>
|
||||||
<c:param name="uri" value="${ent.URI}"/>
|
<c:param name="uri" value="${ent.URI}"/>
|
||||||
</c:url>
|
</c:url>
|
||||||
<c:url var="imageSrc" value="${IMG_DIR}${ent.imageThumb}"/>
|
<div class="tab-image"><a class="image" href="<c:out value="${imageHref}"/>"><img width="${IMG_WIDTH}" src="<c:url value='${ent.thumbUrl}'/>" title="${ent.name}" alt="" /></a></div>
|
||||||
<div class="tab-image"><a class="image" href="<c:out value="${imageHref}"/>"><img width="${IMG_WIDTH}" src="<c:out value="${imageSrc}"/>" title="${ent.name}" alt="" /></a></div>
|
|
||||||
<c:if test="${not empty ent.blurb}"><div class='blurb'>${ent.blurb}</div></c:if>
|
<c:if test="${not empty ent.blurb}"><div class='blurb'>${ent.blurb}</div></c:if>
|
||||||
</c:when>
|
</c:when>
|
||||||
<c:otherwise>
|
<c:otherwise>
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
throw new JspException(e);
|
throw new JspException(e);
|
||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
<c:set var='imageDir' value='images' />
|
|
||||||
<c:set var='entities' value='${requestScope.entities}'/><%/* just moving this into page scope for easy use */ %>
|
<c:set var='entities' value='${requestScope.entities}'/><%/* just moving this into page scope for easy use */ %>
|
||||||
<c:set var='classgroup' value='${requestScope.classgroup}'/>
|
<c:set var='classgroup' value='${requestScope.classgroup}'/>
|
||||||
|
|
||||||
|
@ -40,11 +39,11 @@
|
||||||
<c:forEach items="${entity.linksList}" var='link'>
|
<c:forEach items="${entity.linksList}" var='link'>
|
||||||
| <a href='<c:url value="${link.url}"/>'${link.anchor}</a>
|
| <a href='<c:url value="${link.url}"/>'${link.anchor}</a>
|
||||||
</c:forEach>
|
</c:forEach>
|
||||||
<c:if test="${!empty entity.imageThumb}">
|
<c:if test="${!empty entity.thumbUrl}">
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
<c:if test="${!empty entity.imageFile}"><a target="_new" href="<c:url value='${imageDir}/${entity.imageFile}'/>"></c:if>
|
<c:if test="${!empty entity.imageUrl}"><a target="_new" href="<c:url value='${entity.imageUrl}'/>"></c:if>
|
||||||
<img src="<c:url value='${imageDir}/${entity.imageThumb}'/>" title="click to view larger image in new window" width="150">
|
<img src="<c:url value='${entity.thumbUrl}'/>" title="click to view larger image in new window" width="150">
|
||||||
<c:if test="${!empty entity.imageFile}"></a></c:if>
|
<c:if test="${!empty entity.imageUrl}"></a></c:if>
|
||||||
</div>
|
</div>
|
||||||
</c:if>
|
</c:if>
|
||||||
<c:import url="${entityPropsListJsp}" /><%/* here we import the properties for the entity */ %>
|
<c:import url="${entityPropsListJsp}" /><%/* here we import the properties for the entity */ %>
|
||||||
|
|
|
@ -19,16 +19,13 @@
|
||||||
<c:set var='dashboardPropsListJsp' value='/dashboardPropList'/>
|
<c:set var='dashboardPropsListJsp' value='/dashboardPropList'/>
|
||||||
<c:set var='portal' value='${currentPortalId}'/>
|
<c:set var='portal' value='${currentPortalId}'/>
|
||||||
<c:set var='portalBean' value='${currentPortal}'/>
|
<c:set var='portalBean' value='${currentPortal}'/>
|
||||||
<c:set var='imageDir' value='images' />
|
|
||||||
<div id="dashboard"<c:if test="${showCuratorEdits}"> class="loggedIn"</c:if>>
|
<div id="dashboard"<c:if test="${showCuratorEdits}"> class="loggedIn"</c:if>>
|
||||||
<c:if test="${!empty entity.imageThumb}">
|
<c:if test="${!empty entity.thumbUrl}">
|
||||||
<c:if test="${!empty entity.imageFile}">
|
<c:if test="${!empty entity.imageUrl}">
|
||||||
<c:url var="imageUrl" value="${imageDir}/${entity.imageFile}" />
|
<a class="image" href="<c:url value='${entity.imageUrl}'/>">
|
||||||
<a class="image" href="${imageUrl}">
|
|
||||||
</c:if>
|
</c:if>
|
||||||
<c:url var="imageSrc" value='${imageDir}/${entity.imageThumb}'/>
|
<img class="headshot" src="<c:url value='${entity.thumbUrl}'/>" title="click to view larger image in new window" alt="" width="150"/>
|
||||||
<img class="headshot" src="<c:out value="${imageSrc}"/>" title="click to view larger image in new window" alt="" width="150"/>
|
<c:if test="${!empty entity.imageUrl}"></a></c:if>
|
||||||
<c:if test="${!empty entity.imageFile}"></a></c:if>
|
|
||||||
<c:if test="${!empty entity.citation}"><div class="citation">${entity.citation}</div></c:if>
|
<c:if test="${!empty entity.citation}"><div class="citation">${entity.citation}</div></c:if>
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
||||||
|
|
|
@ -19,16 +19,13 @@
|
||||||
<c:set var='dashboardPropsListJsp' value='/dashboardPropList'/>
|
<c:set var='dashboardPropsListJsp' value='/dashboardPropList'/>
|
||||||
<c:set var='portal' value='${currentPortalId}'/>
|
<c:set var='portal' value='${currentPortalId}'/>
|
||||||
<c:set var='portalBean' value='${currentPortal}'/>
|
<c:set var='portalBean' value='${currentPortal}'/>
|
||||||
<c:set var='imageDir' value='images' />
|
|
||||||
<div id="dashboard"<c:if test="${showCuratorEdits}"> class="loggedIn"</c:if>>
|
<div id="dashboard"<c:if test="${showCuratorEdits}"> class="loggedIn"</c:if>>
|
||||||
<c:if test="${!empty entity.imageThumb}">
|
<c:if test="${!empty entity.thumbUrl}">
|
||||||
<c:if test="${!empty entity.imageFile}">
|
<c:if test="${!empty entity.imageUrl}">
|
||||||
<c:url var="imageUrl" value="${imageDir}/${entity.imageFile}" />
|
<a class="image" href="<c:url value='${entity.imageUrl}'/>">
|
||||||
<a class="image" href="${imageUrl}">
|
|
||||||
</c:if>
|
</c:if>
|
||||||
<c:url var="imageSrc" value='${imageDir}/${entity.imageThumb}'/>
|
<img class="headshot" src="<c:url value='${entity.thumbUrl}'/>" title="click to view larger image in new window" alt="" width="150"/>
|
||||||
<img class="headshot" src="<c:out value="${imageSrc}"/>" title="click to view larger image in new window" alt="" width="150"/>
|
<c:if test="${!empty entity.imageUrl}"></a></c:if>
|
||||||
<c:if test="${!empty entity.imageFile}"></a></c:if>
|
|
||||||
<c:if test="${!empty entity.citation}"><div class="citation">${entity.citation}</div></c:if>
|
<c:if test="${!empty entity.citation}"><div class="citation">${entity.citation}</div></c:if>
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue