NIHVIVO-157 manual merge of the changes from branch NIHVIVO-157-file-storage

This commit is contained in:
jeb228 2010-06-20 19:23:19 +00:00
parent 083aa4e530
commit fb7d5bbba9
51 changed files with 3535 additions and 1198 deletions

View file

@ -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());

View file

@ -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

View file

@ -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>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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());

View file

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

View file

@ -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());

View file

@ -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";
} }

View file

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

View file

@ -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;

View file

@ -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() {

View file

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

View file

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

View file

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

View file

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

View file

@ -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;

View file

@ -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

View file

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

View file

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

View file

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

View file

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

View file

@ -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";
}
}

View file

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

View file

@ -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.
}
}

View file

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

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

View file

@ -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";
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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,

View file

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

View file

@ -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");

View file

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

View file

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

View file

@ -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>

View file

@ -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">

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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 */ %>

View file

@ -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>

View file

@ -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>