/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/common/trunk/import-handlers/content-handlers/src/java/org/sakaiproject/importer/impl/handlers/ResourcesHandler.java $
* $Id: ResourcesHandler.java 106351 2012-03-28 20:21:21Z matthew@longsight.com $
***********************************************************************************
*
* Copyright (c) 2006, 2007, 2008 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.importer.impl.handlers;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.sakaiproject.exception.IdInvalidException;
import org.sakaiproject.exception.IdUsedException;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.InUseException;
import org.sakaiproject.exception.InconsistentException;
import org.sakaiproject.exception.OverQuotaException;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.exception.ServerOverloadException;
import org.sakaiproject.exception.TypeException;
import org.sakaiproject.importer.api.HandlesImportable;
import org.sakaiproject.importer.api.Importable;
import org.sakaiproject.importer.impl.importables.FileResource;
import org.sakaiproject.importer.impl.importables.Folder;
import org.sakaiproject.importer.impl.importables.WebLink;
import org.sakaiproject.importer.impl.importables.HtmlDocument;
import org.sakaiproject.importer.impl.importables.TextDocument;
import org.sakaiproject.tool.api.SessionManager;
import org.sakaiproject.authz.api.SecurityAdvisor;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.content.api.ContentResource;
import org.sakaiproject.content.api.ContentResourceEdit;
import org.sakaiproject.content.api.ResourceType;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.entity.api.ResourcePropertiesEdit;
import org.sakaiproject.event.api.NotificationService;
import org.sakaiproject.util.Validator;
import javax.activation.MimetypesFileTypeMap;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
public class ResourcesHandler implements HandlesImportable {
private static final String COPYRIGHT = "(c) 2007";
private final int BUFFER = 2048;
private ContentHostingService contentHostingService;
private SessionManager sessionManager;
private SecurityService securityService;
private ServerConfigurationService serverConfigurationService;
private Log m_log = LogFactory.getLog(org.sakaiproject.importer.impl.handlers.ResourcesHandler.class);
public boolean canHandleType(String typeName) {
return (("sakai-file-resource".equals(typeName) || ("sakai-folder".equals(typeName)) ||
("sakai-text-document".equals(typeName)) || ("sakai-html-document".equals(typeName)) ||
("sakai-web-link".equals(typeName)) || ("sakai-learning-module".equals(typeName))));
}
public void handle(Importable thing, String siteId) {
if(canHandleType(thing.getTypeName())){
final String currentUser = sessionManager.getCurrentSessionUserId();
securityService.pushAdvisor(new SecurityAdvisor() {
public SecurityAdvice isAllowed(String userId, String function,
String reference) {
if ((userId != null) && (userId.equals(currentUser)) &&
(("content.new".equals(function))
|| ("content.read".equals(function)))){
return SecurityAdvice.ALLOWED;
}
return SecurityAdvice.PASS;
}
});
String id = null;
String contentType = null;
int notifyOption = NotificationService.NOTI_NONE;
String title = null;
String description = null;
Map resourceProps = new HashMap();
InputStream contents = null;
if ("sakai-file-resource".equals(thing.getTypeName())) {
//title = ((FileResource)thing).getTitle();
description = ((FileResource)thing).getDescription();
String fileName = ((FileResource)thing).getFileName();
id = contentHostingService.getSiteCollection(siteId);
String contextPath = ((FileResource)thing).getDestinationResourcePath();
if (contextPath != null && (contextPath.length() + id.length()) > 255) {
// leave at least 14 characters at end for uniqueness
contextPath = contextPath.substring(0, (255 - 14 - id.length()));
// add a timestamp to differentiate it (+14 chars)
Format f= new SimpleDateFormat("yyyyMMddHHmmss");
contextPath += f.format(new Date());
// total new length of 32 chars
}
id = id + contextPath;
contentType = new MimetypesFileTypeMap().getContentType(fileName);
contents = ((FileResource)thing).getInputStream();
// if((title == null) || (title.equals(""))) {
// title = fileName;
// }
title = fileName;
resourceProps.put(ResourceProperties.PROP_DESCRIPTION, description);
if (title.toLowerCase().endsWith(".zip")) {
//create a folder with the name of the zip, minus the .zip
String container = title.substring(0, title.length() - 4);
resourceProps.put(ResourceProperties.PROP_DISPLAY_NAME, container);
//get the full path to the current folder
String path = id.substring(0, id.length() - title.length());
addContentCollection(path + container, resourceProps);
addAllResources(contents, path + container, notifyOption);
}
else {
if(m_log.isDebugEnabled()) {
m_log.debug("import ResourcesHandler about to add file entitled '" + title + "'");
}
resourceProps.put(ResourceProperties.PROP_DISPLAY_NAME, title);
addContentResource(id, contentType, contents, resourceProps, notifyOption);
}
} else if ("sakai-web-link".equals(thing.getTypeName())) {
title = ((WebLink)thing).getTitle();
description = ((WebLink)thing).getDescription();
id = contentHostingService.getSiteCollection(siteId) + thing.getContextPath();
contentType = ResourceProperties.TYPE_URL;
String absoluteUrl = "";
if (((WebLink)thing).isAbsolute()) {
absoluteUrl = ((WebLink)thing).getUrl();
} else {
absoluteUrl = serverConfigurationService.getServerUrl() + "/access/content" +
contentHostingService.getSiteCollection(siteId) + ((WebLink)thing).getUrl();
}
contents = new ByteArrayInputStream(absoluteUrl.getBytes());
if((title == null) || (title.equals(""))) {
title = ((WebLink)thing).getUrl();
}
resourceProps.put(ResourceProperties.PROP_DISPLAY_NAME, title);
resourceProps.put(ResourceProperties.PROP_DESCRIPTION, description);
resourceProps.put(ResourceProperties.PROP_HAS_CUSTOM_SORT, Boolean.TRUE.toString());
resourceProps.put(ResourceProperties.PROP_CONTENT_PRIORITY, Integer.toString(((WebLink)thing).getSequenceNum()));
if(m_log.isDebugEnabled()){
m_log.debug("import ResourcesHandler about to add web link entitled '" + title + "'");
}
ContentResource contentResource = addContentResource(id, contentType, contents, resourceProps, notifyOption);
if (contentResource != null) {
try {
ContentResourceEdit cre = contentHostingService.editResource(contentResource.getId());
cre.setResourceType(ResourceType.TYPE_URL);
contentHostingService.commitResource(cre, notifyOption);
} catch (Exception e1) {
m_log.error("import ResourcesHandler tried to set Resource Type of web link and failed", e1);
}
}
} else if ("sakai-html-document".equals(thing.getTypeName())) {
title = ((HtmlDocument)thing).getTitle();
contents = new ByteArrayInputStream(((HtmlDocument)thing).getContent().getBytes());
id = contentHostingService.getSiteCollection(siteId) + thing.getContextPath();
contentType = "text/html";
resourceProps.put(ResourceProperties.PROP_DISPLAY_NAME, title);
if(m_log.isDebugEnabled()){
m_log.debug("import ResourcesHandler about to add html document entitled '" + title + "'");
}
addContentResource(id, contentType, contents, resourceProps, notifyOption);
} else if ("sakai-text-document".equals(thing.getTypeName())) {
title = ((TextDocument)thing).getTitle();
contents = new ByteArrayInputStream(((TextDocument)thing).getContent().getBytes());
id = contentHostingService.getSiteCollection(siteId) + thing.getContextPath();
contentType = "text/plain";
resourceProps.put(ResourceProperties.PROP_DISPLAY_NAME, title);
if(m_log.isDebugEnabled()){
m_log.debug("import ResourcesHandler about to add text document entitled '" + title + "'");
}
addContentResource(id, contentType, contents, resourceProps, notifyOption);
}
else if ("sakai-folder".equals(thing.getTypeName())) {
title = ((Folder)thing).getTitle();
description = ((Folder)thing).getDescription();
resourceProps.put(ResourceProperties.PROP_DISPLAY_NAME, title);
resourceProps.put(ResourceProperties.PROP_DESCRIPTION, description);
resourceProps.put(ResourceProperties.PROP_COPYRIGHT, COPYRIGHT);
/*
* Added title to the end of the path. Otherwise, we're setting the props on the
* containing folder rather than the folder itself.
*/
String path = contentHostingService.getSiteCollection(siteId) + ((Folder)thing).getPath();
addContentCollection(path,resourceProps);
}
securityService.popAdvisor();
}
}
protected void addAllResources(InputStream archive, String path, int notifyOption) {
ZipInputStream zipStream = new ZipInputStream(archive);
ZipEntry entry;
String contentType;
if (path.charAt(0) == '/') {
path = path.substring(1);
}
if (!path.endsWith("/")) {
path = path + "/";
}
try {
while((entry = zipStream.getNextEntry()) != null) {
Map resourceProps = new HashMap();
contentType = new MimetypesFileTypeMap().getContentType(entry.getName());
String title = entry.getName();
if (title.lastIndexOf("/") > 0) {
title = title.substring(title.lastIndexOf("/") + 1);
}
resourceProps.put(ResourceProperties.PROP_DISPLAY_NAME, title);
resourceProps.put(ResourceProperties.PROP_COPYRIGHT, COPYRIGHT);
if(m_log.isDebugEnabled()) {
m_log.debug("import ResourcesHandler about to add file entitled '" + title + "'");
}
int count;
ByteArrayOutputStream contents = new ByteArrayOutputStream();
byte[] data = new byte[BUFFER];
while ((count = zipStream.read(data, 0, BUFFER)) != -1) {
contents.write(data, 0, count);
}
if (entry.isDirectory()) {
addContentCollection(path + entry.getName(), resourceProps);
addAllResources(new ByteArrayInputStream(contents.toByteArray()), path + entry.getName(), notifyOption);
}
else {
addContentResource(path + entry.getName(), contentType, new ByteArrayInputStream(contents.toByteArray()), resourceProps, notifyOption);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
protected ContentResource addContentResource(String id, String contentType, InputStream contents, Map properties, int notifyOption) {
try {
id = makeIdCleanAndLengthCompliant(id);
ResourcePropertiesEdit resourceProps = contentHostingService.newResourceProperties();
Set keys = properties.keySet();
for (Iterator i = keys.iterator();i.hasNext();) {
String key = (String)i.next();
String value = (String)properties.get(key);
resourceProps.addProperty(key, value);
}
String enclosingDirectory = id.substring(0, id.lastIndexOf('/', id.length() - 2) + 1);
if(existsDirectory(enclosingDirectory)) {
contentHostingService.addProperty(enclosingDirectory, ResourceProperties.PROP_HAS_CUSTOM_SORT, Boolean.TRUE.toString());
}
return contentHostingService.addResource(id, contentType, contents, resourceProps, notifyOption);
} catch (PermissionException e) {
m_log.error("ResourcesHandler.addContentResource: " + e.toString());
} catch (IdUsedException e) {
m_log.warn("ResourcesHandler.addContentResource IdUsedException: " + e.toString());
} catch (IdInvalidException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InconsistentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (OverQuotaException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ServerOverloadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IdUnusedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TypeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InUseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
protected boolean existsDirectory(String path) {
try {
contentHostingService.getCollection(path);
} catch (IdUnusedException e) {
return false;
} catch (TypeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (PermissionException e) {
m_log.error("ResourcesHandler.existsDirectory: " + e.toString());
}
return true;
}
protected void addContentCollection(String path, Map properties) {
path = makeIdCleanAndLengthCompliant(path);
ResourcePropertiesEdit resourceProps = contentHostingService.newResourceProperties();
Set keys = properties.keySet();
for (Iterator i = keys.iterator();i.hasNext();) {
String key = (String)i.next();
String value = (String)properties.get(key);
resourceProps.addProperty(key, value);
}
// ContentCollectionEdit coll = null;
try {
// String enclosingDirectory = path.substring(0, path.lastIndexOf('/', path.length() - 2) + 1);
// if(!existsDirectory(enclosingDirectory)) {
// ResourcePropertiesEdit enclosingProps = ContentHostingService.newResourceProperties();
// enclosingProps.addProperty(ResourceProperties.PROP_DISPLAY_NAME, );
// ContentHostingService.addCollection(enclosingDirectory, enclosingProps);
//
// }
contentHostingService.addCollection(path, resourceProps);
//coll = ContentHostingService.addCollection(path);
//ContentHostingService.commitCollection(coll);
} catch (IdUsedException e) {
// if this thing already exists (which it probably does),
// we'll do an update on the properties rather than creating the folder
// try {
// ContentHostingService.addProperty
// (path, ResourceProperties.PROP_DISPLAY_NAME, (String)properties.get(ResourceProperties.PROP_DISPLAY_NAME));
// ContentHostingService.addProperty
// (path, ResourceProperties.PROP_COPYRIGHT, (String)properties.get(ResourceProperties.PROP_COPYRIGHT));
// ContentHostingService.addProperty
// (path, ResourceProperties.PROP_DESCRIPTION, (String)properties.get(ResourceProperties.PROP_DESCRIPTION));
// } catch (PermissionException e1) {
// m_log.error("ResourcesHandler.addContentCollection: " + e.toString());
// } catch (IdUnusedException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// } catch (TypeException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// } catch (InUseException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// } catch (ServerOverloadException e1) {
// // TODO Auto-generated catch block
// e1.printStackTrace();
// }
} catch (IdInvalidException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (PermissionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InconsistentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String makeIdCleanAndLengthCompliant (String path) {
String [] parts = path.split("/");
StringBuilder rv = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
if (parts[i].length() > 0) {
rv.append("/" + Validator.escapeResourceName(parts[i]));
}
}
// SAK-18833, the content resource must be less than 255 chars
if (rv.length() > (255 - 5)) {
// leave at least 14 characters at end for uniqueness
// leave an additional 5 characters for an extension like .html
rv.setLength(255 - 14 - 5);
// add a timestamp to differentiate it (+14 chars)
Format f = new SimpleDateFormat("yyyyMMddHHmmss");
rv.append(f.format(new Date()));
if (m_log.isDebugEnabled()) {
m_log.debug("makeIdCleanAndLengthCompliant truncated from " + path + " to " + rv.toString());
}
}
return rv.toString();
}
public ContentHostingService getContentHostingService() {
return contentHostingService;
}
public void setContentHostingService(ContentHostingService chs) {
this.contentHostingService = chs;
}
public SessionManager getSessionManager() {
return sessionManager;
}
public void setSessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
public SecurityService getSecurityService() {
return securityService;
}
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
public ServerConfigurationService getServerConfigurationService() {
return serverConfigurationService;
}
public void setServerConfigurationService(
ServerConfigurationService serverConfigurationService) {
this.serverConfigurationService = serverConfigurationService;
}
}