Skip to content

Commit

Permalink
LSNBLDR-758; fix mime type determination for uploaded files (sakaipro…
Browse files Browse the repository at this point in the history
…ject#3674)

* LSNBLDR-758; fix mime type determination for uploaded files
  • Loading branch information
clhedrick authored Dec 14, 2016
1 parent bd0b91b commit d1d6350
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.content.api.*;
import org.sakaiproject.content.api.GroupAwareEntity.AccessMode;
import org.sakaiproject.content.cover.ContentTypeImageService;
import org.sakaiproject.db.cover.SqlService;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.entity.api.ResourceProperties;
Expand Down Expand Up @@ -521,6 +522,19 @@ public Collection<BltiTool> getBltiTools() {
imageTypes.add("tif");
}

private static final String DEFAULT_HTML_TYPES = "html,xhtml,htm,xht";
private static String[] htmlTypes = null;

static {
String mmTypes = ServerConfigurationService.getString("lessonbuilder.html.types", DEFAULT_HTML_TYPES);
htmlTypes = mmTypes.split(",");
for (int i = 0; i < htmlTypes.length; i++) {
htmlTypes[i] = htmlTypes[i].trim().toLowerCase();
}
Arrays.sort(htmlTypes);
}


// Spring Injection

private SessionManager sessionManager;
Expand Down Expand Up @@ -3518,6 +3532,52 @@ public String getReleaseString(SimplePageItem i, Locale locale) {

}

// only makes sense for SimplePageItem.RESOURCE or .MULTIMEDIA
public String getContentType(SimplePageItem item) {
String mimeType = item.getHtml();
// for files the code no longer stores the mimetype in lessons
// so if lessons doesn't have one, get it from the kernel

// old code put URLs in html field. no legit types start with http
if (mimeType != null && (mimeType.startsWith("http")))
mimeType = null;

if (mimeType == null || mimeType.equals("")) {
String mmDisplayType = item.getAttribute("multimediaDisplayType");
// 2 is the generic "use old display" so treat it as null
// only do this for type 2, since that's where there's an actual file
if (mmDisplayType == null || "".equals(mmDisplayType) || "2".equals(mmDisplayType)) {
try {
ContentResource res = contentHostingService.getResource(item.getSakaiId());
mimeType = res.getContentType();
} catch (Exception ignore) {
}
}
}
if("application/octet-stream".equals(mimeType)) {
// OS X reports octet stream for things like MS Excel documents.
// Force a mimeType lookup so we get a decent icon.
// Probably not needed for normal files, as kernel should sniff
// correctly, but we may need it for URLs
mimeType = null;
}

if (mimeType == null || mimeType.equals("")) {
String s = item.getSakaiId();
int j = s.lastIndexOf(".");
if (j >= 0)
s = s.substring(j+1);
mimeType = ContentTypeImageService.getContentType(s);
// log.info("type " + s + ">" + mimeType);
}

// if still nothing, call it octet-stream just so we don't return null
if (mimeType == null || mimeType.equals(""))
mimeType = "application/octet-stream";

return mimeType;
}

// obviously this function must be called right after getResourceGroups
private boolean inherited = false;
public boolean getInherited() {
Expand Down Expand Up @@ -4415,35 +4475,39 @@ public void recomputeGradebookEntries(Long pageId, String newPoints) {
gradebookIfc.updateExternalAssessmentScores(getCurrentSiteId(), "lesson-builder:" + pageId, userMap);
}

public boolean isImageType(SimplePageItem item) {
// if mime type is defined use it
String mimeType = item.getHtml();
if (mimeType != null && (mimeType.startsWith("http") || mimeType.equals("")))
mimeType = null;

if (mimeType != null && mimeType.length() > 0) {
return mimeType.toLowerCase().startsWith("image/");
}

// else use extension

String name = item.getSakaiId();
// there's one of these in Validator, but it isn't quite right, because it doesn't look at /
// return lowercase version, since we want uppercase versiosns to match
public static String getExtension(String name) {

// starts after last /
int i = name.lastIndexOf("/");
if (i >= 0)
name = name.substring(i+1);

String extension = null;
String extension = "";
i = name.lastIndexOf(".");
if (i > 0)
extension = name.substring(i+1);

if (extension == null)
return false;
extension = name.substring(i+1);

extension = extension.trim();
extension = extension.toLowerCase();

return extension;
}

public boolean isImageType(SimplePageItem item) {

String mimeType = getContentType(item);

if (mimeType != null && mimeType.length() > 0 && !mimeType.equals("application/octet-stream")) {
return mimeType.toLowerCase().startsWith("image/");
}

// no usable type found
// getContentType already checked extensions. But we have our own idea of image types, which is site-configurable
// to check for them

String extension = getExtension(item.getSakaiId());

if (imageTypes.contains(extension)) {
return true;
Expand All @@ -4452,6 +4516,28 @@ public boolean isImageType(SimplePageItem item) {
}
}

public boolean isHtmlType(SimplePageItem item) {

String mimeType = getContentType(item);

if (mimeType != null && mimeType.length() > 0 && !mimeType.equals("application/octet-stream")) {
return mimeType.equals("text/html") || mimeType.equals("application/xhtml+xml");
}

// no usable type found
// getContentType already checked extensions. But we have our own idea of html types, which is site-configurable
// to check for them

String extension = getExtension(item.getSakaiId());

if (Arrays.binarySearch(htmlTypes, extension) >= 0) {
return true;
} else {
return false;
}

}

public void setOrder(String order) {
this.order = order;
}
Expand Down Expand Up @@ -6038,23 +6124,31 @@ public void addMultimediaFile(MultipartFile file, boolean first, String name){
}
if (isCaption)
res.setContentType("text/vtt");
// octet-stream is probably bogus. let content hosting try to guess
else if (!"application/octet-stream".equals(mimeType))
res.setContentType(mimeType);
// don't use mime type, to give kernel a chance to look at the contents
//else if (!"application/octet-stream".equals(mimeType))
// res.setContentType(mimeType);
res.setContent(file.getInputStream());
try {
contentHostingService.commitResource(res, NotificationService.NOTI_NONE);
// reset mime type. kernel may have improved it if it was null
String newMimeType = res.getContentType();
if (newMimeType != null && !newMimeType.equals(""))
mimeType = newMimeType;
if ((newMimeType == null || newMimeType.equals("") || newMimeType.equals("application/octet-stream")) &&
mimeType != null && !mimeType.equals("")) {
// kernel didn't find anything useful. If browser sent something, use it
res = contentHostingService.editResource(res.getId());
res.setContentType(mimeType);
contentHostingService.commitResource(res, NotificationService.NOTI_NONE);
}
// note that we don't save the mime type in the lessons item anymore
// display code will use the item type from resources
// there's a bug in the kernel that can cause
// a null pointer if it can't determine the encoding
// type. Since we want this code to work on old
// systems, work around it.
} catch (java.lang.NullPointerException e) {
setErrMessage(messageLocator.getMessage("simplepage.resourcepossibleerror"));
}
mimeType = null; // display code will use type from the object
sakaiId = res.getId();

if(("application/zip".equals(mimeType) || "application/x-zip-compressed".equals(mimeType)) && isWebsite) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,9 +505,10 @@ else if (summaryPage && entry.itemId != null) {
UIOutput.make(itemListItem,"item-icon")
.decorate(getImageSourceDecorator(pageItem));

if (pageItem.isPrerequisite()) {
itemListItem.decorate(new UIFreeAttributeDecorator("class", "disabled-text-item"));
}
// interesting idea, but makes it invisible
//if (pageItem.isPrerequisite()) {
// itemListItem.decorate(new UIFreeAttributeDecorator("class", "disabled-text-item"));
//}

if(SimplePageItem.TEXT == pageItem.getType()) {
UIOutput.make(itemListItem, "name", messageLocator.getMessage("simplepage.chooser.textitemplaceholder"))
Expand Down Expand Up @@ -642,23 +643,13 @@ private UIStyleDecorator getImageSourceDecorator(SimplePageItem pageItem) {

private UIStyleDecorator getImageSourceDecoratorFromMimeType(SimplePageItem pageItem) {

String mimeType = pageItem.getHtml();
String mimeType;

if(SimplePageItem.TEXT == pageItem.getType()) {
mimeType = "text/html";
} else if("application/octet-stream".equals(mimeType)) {
// OS X reports octet stream for things like MS Excel documents.
// Force a mimeType lookup so we get a decent icon.
mimeType = null;
}

if (mimeType == null || mimeType.equals("")) {
String s = pageItem.getSakaiId();
int j = s.lastIndexOf(".");
if (j >= 0)
s = s.substring(j+1);
mimeType = ContentTypeImageService.getContentType(s);
}
} else {
mimeType = simplePageBean.getContentType(pageItem);
}

String src = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
import org.sakaiproject.event.cover.UsageSessionService;
import org.sakaiproject.authz.api.AuthzGroupService;
import org.sakaiproject.authz.api.Member;
import org.sakaiproject.util.Validator;
import org.sakaiproject.lessonbuildertool.SimplePage;
import org.sakaiproject.lessonbuildertool.SimplePageComment;
import org.sakaiproject.lessonbuildertool.SimplePageItem;
Expand Down Expand Up @@ -206,8 +207,6 @@ public void setImageToMimeMap(Map<String,String> map) {
public MessageLocator messageLocator;
private LocaleGetter localegetter;
public static final String VIEW_ID = "ShowPage";
private static final String DEFAULT_HTML_TYPES = "html,xhtml,htm,xht";
private static String[] htmlTypes = null;
// mp4 means it plays with the flash player if HTML5 doesn't work.
// flv is also played with the flash player, but it doesn't get a backup <OBJECT> inside the player
// Strobe claims to handle MOV files as well, but I feel safer passing them to quicktime, though that requires Quicktime installation
Expand Down Expand Up @@ -526,15 +525,6 @@ public void fillComponents(UIContainer tofill, ViewParameters viewParams, Compon
}
}

if (htmlTypes == null) {
String mmTypes = ServerConfigurationService.getString("lessonbuilder.html.types", DEFAULT_HTML_TYPES);
htmlTypes = mmTypes.split(",");
for (int i = 0; i < htmlTypes.length; i++) {
htmlTypes[i] = htmlTypes[i].trim().toLowerCase();
}
Arrays.sort(htmlTypes);
}

if (mp4Types == null) {
String m4Types = ServerConfigurationService.getString("lessonbuilder.mp4.types", DEFAULT_MP4_TYPES);
mp4Types = m4Types.split(",");
Expand Down Expand Up @@ -1359,22 +1349,7 @@ public int compare(SimpleStudentPage o1, SimpleStudentPage o2) {
itemicon.decorate(new UIStyleDecorator("fa-folder-open-o"));
break;
case SimplePageItem.RESOURCE:
String mimeType = i.getHtml();

if("application/octet-stream".equals(mimeType)) {
// OS X reports octet stream for things like MS Excel documents.
// Force a mimeType lookup so we get a decent icon.
mimeType = null;
}

if (mimeType == null || mimeType.equals("")) {
String s = i.getSakaiId();
int j = s.lastIndexOf(".");
if (j >= 0)
s = s.substring(j+1);
mimeType = ContentTypeImageService.getContentType(s);
// log.info("type " + s + ">" + mimeType);
}
String mimeType = simplePageBean.getContentType(i);

String src = null;
//if (!useSakaiIcons)
Expand Down Expand Up @@ -1755,13 +1730,7 @@ else if (lessonEntity.notPublished())
// over time as we get more experience with different
// object types and browsers.

StringTokenizer token = new StringTokenizer(i.getSakaiId(), ".");

String extension = "";

while (token.hasMoreTokens()) {
extension = token.nextToken().toLowerCase();
}
// String extension = Validator.getFileExtension(i.getSakaiId());

// the extension is almost never used. Normally we have
// the MIME type and use it. Extension is used only if
Expand All @@ -1787,14 +1756,8 @@ else if (lessonEntity.notPublished())
height = new Length(i.getHeight());
}

// Get the MIME type. For multimedia types is should be in
// the html field.
// The old code saved the URL there. So if it looks like a
// URL ignore it.
String mimeType = i.getHtml();
if (mimeType != null && (mimeType.startsWith("http") || mimeType.equals(""))) {
mimeType = null;
}
// Get the MIME type.
String mimeType = simplePageBean.getContentType(i);

// here goes. dispatch on the type and produce the right tag
// type,
Expand Down Expand Up @@ -1920,10 +1883,8 @@ else if (lessonEntity.notPublished())
// use EMBED. OBJECT does work with Flash.
// application/xhtml+xml is XHTML.

} else if (mmDisplayType == null &&
((mimeType != null && !mimeType.equals("text/html") && !mimeType.equals("application/xhtml+xml")) ||
// ((mimeType != null && (mimeType.startsWith("audio/") || mimeType.startsWith("video/"))) ||
(mimeType == null && !(Arrays.binarySearch(htmlTypes, extension) >= 0)))) {
// mimeType is
} else if (mmDisplayType == null && !simplePageBean.isHtmlType(i)) {

// except where explicit display is set,
// this code is used for everything that isn't an image,
Expand All @@ -1937,10 +1898,6 @@ else if (lessonEntity.notPublished())
// html and random stuff without a defined mime type
// random stuff with mime type is displayed with object

if (mimeType == null) {
mimeType = "";
}

String oMimeType = mimeType; // in case we change it for
// FLV or others

Expand Down
9 changes: 5 additions & 4 deletions lessonbuilder/tool/src/webapp/WEB-INF/applicationContext.xml
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,11 @@ simplePageBean.addForumSummary
<entry key="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" value="fa-file-excel-o"/>
<entry key="application/pdf" value="fa-file-pdf-o"/>
<entry key="application/x-pdf" value="fa-file-pdf-o"/>
<entry key="application/x-shockwave-flash" value="fa-video-o"/>
<entry key="video/x-flv" value="fa-video-o"/>
<entry key="video/mp4" value="fa-video-o"/>
<entry key="video/x-mp4" value="fa-video-o"/>
<entry key="application/x-shockwave-flash" value="fa-video-camera"/>
<entry key="video/x-flv" value="fa-video-camera"/>
<entry key="video/mp4" value="fa-video-camera"/>
<entry key="video/x-mp4" value="fa-video-camera"/>
<entry key="video/x-m4v" value="fa-video-camera"/>
<entry key="text/plain" value="fa-file-text-o"/>
<entry key="text/rtf" value="fa-file-text-o"/>
<entry key="LBWEBSITE" value="fa-external-link"/>
Expand Down

0 comments on commit d1d6350

Please sign in to comment.