Java tutorial
/********************************************************************************** * $URL$ * $Id$ *********************************************************************************** * * Copyright (c) 2007, 2008 The Regents of the University of Michigan & Foothill College, ETUDES Project * * Licensed under the Apache 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.apache.org/licenses/LICENSE-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.muse.mneme.impl; import java.awt.Container; import java.awt.Graphics2D; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Toolkit; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.muse.ambrosia.util.EscapeRefUrl; import org.muse.mneme.api.Attachment; import org.muse.mneme.api.AttachmentService; import org.muse.mneme.api.MnemeService; import org.muse.mneme.api.SecurityService; import org.muse.mneme.api.Submission; import org.muse.mneme.api.SubmissionService; import org.muse.mneme.api.Translation; import org.sakaiproject.authz.api.SecurityAdvisor; import org.sakaiproject.component.api.ServerConfigurationService; import org.sakaiproject.content.api.ContentCollection; import org.sakaiproject.content.api.ContentCollectionEdit; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.content.api.ContentResourceEdit; import org.sakaiproject.entity.api.Entity; import org.sakaiproject.entity.api.EntityAccessOverloadException; import org.sakaiproject.entity.api.EntityCopyrightException; import org.sakaiproject.entity.api.EntityManager; import org.sakaiproject.entity.api.EntityNotDefinedException; import org.sakaiproject.entity.api.EntityPermissionException; import org.sakaiproject.entity.api.EntityProducer; import org.sakaiproject.entity.api.HttpAccess; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.entity.api.ResourcePropertiesEdit; import org.sakaiproject.exception.IdInvalidException; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.IdUsedException; 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.id.api.IdManager; import org.sakaiproject.tool.api.SessionManager; import org.sakaiproject.util.StringUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGEncodeParam; import com.sun.image.codec.jpeg.JPEGImageEncoder; /** * AttachmentServiceImpl implements AttachmentService. */ public class AttachmentServiceImpl implements AttachmentService, EntityProducer { /** Our logger. */ private static Log M_log = LogFactory.getLog(AttachmentServiceImpl.class); protected final static String PROP_THUMB = "attachment:thumb"; protected final static String PROP_UNIQUE_HOLDER = "attachment:unique"; /** The chunk size used when streaming (100k). */ protected static final int STREAM_BUFFER_SIZE = 102400; /** Dependency: ContentHostingService */ protected ContentHostingService contentHostingService = null; /** Dependency: EntityManager */ protected EntityManager entityManager = null; /** Dependency: IdManager. */ protected IdManager idManager = null; /** Configuration: to make thumbs for images or not. */ protected boolean makeThumbs = true; /** Dependency: SecurityService */ protected SecurityService securityService = null; /** Dependency: SecurityService */ protected org.sakaiproject.authz.api.SecurityService securityServiceSakai = null; /** Dependency: ServerConfigurationService */ protected ServerConfigurationService serverConfigurationService = null; /** Dependency: SessionManager */ protected SessionManager sessionManager = null; /** Dependency: SubmissionService */ protected SubmissionService submissionService = null; /** * {@inheritDoc} */ public Reference addAttachment(String application, String context, String prefix, boolean uniqueHolder, FileItem file) { pushAdvisor(); try { String name = file.getName(); if (name != null) { name = massageName(name); } String type = file.getContentType(); // TODO: change to file.getInputStream() for after Sakai 2.3 more efficient support // InputStream body = file.getInputStream(); byte[] body = file.get(); long size = file.getSize(); // detect no file selected if ((name == null) || (type == null) || (body == null) || (size == 0)) { // TODO: if using input stream, close it // if (body != null) body.close(); return null; } Reference rv = doAdd(contentHostingId(name, application, context, prefix, uniqueHolder), name, type, body, size, false); // if this failed, and we are not using a uniqueHolder, try it with a uniqueHolder if ((rv == null) && !uniqueHolder) { rv = doAdd(contentHostingId(name, application, context, prefix, true), name, type, body, size, false); } // TODO: we might not want a thumb (such as for submission uploads to essay/task // if we added one if (rv != null) { // if it is an image if (type.toLowerCase().startsWith("image/")) { addThumb(rv, name, body); } } return rv; } finally { popAdvisor(); } } /** * {@inheritDoc} */ public Reference addAttachment(String application, String context, String prefix, boolean uniqueHolder, Reference resourceRef) { // make sure we can read! pushAdvisor(); try { // if from our docs, convert into a content hosting ref if (resourceRef.getType().equals(APPLICATION_ID)) { resourceRef = entityManager.newReference(resourceRef.getId()); } // make sure we can read! ContentResource resource = this.contentHostingService.getResource(resourceRef.getId()); String type = resource.getContentType(); long size = resource.getContentLength(); byte[] body = resource.getContent(); String name = resource.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME); Reference rv = doAdd(contentHostingId(name, application, context, prefix, uniqueHolder), name, type, body, size, false); // if this failed, and we are not using a uniqueHolder, try it with a uniqueHolder if ((rv == null) && !uniqueHolder) { rv = doAdd(contentHostingId(name, application, context, prefix, true), name, type, body, size, false); } // if we added one if (rv != null) { // if it is an image if (type.toLowerCase().startsWith("image/")) { addThumb(rv, name, body); } } return rv; } catch (PermissionException e) { M_log.warn("addAttachment: " + e.toString()); } catch (IdUnusedException e) { M_log.warn("addAttachment: " + e.toString()); } catch (TypeException e) { M_log.warn("addAttachment: " + e.toString()); } catch (ServerOverloadException e) { M_log.warn("addAttachment: " + e.toString()); } finally { // clear the security advisor popAdvisor(); } return null; } /** * {@inheritDoc} */ public Reference addAttachment(String application, String context, String prefix, boolean uniqueHolder, String name, byte[] body, String type) { pushAdvisor(); try { if (name != null) { name = massageName(name); } long size = body.length; // detect no file selected if ((name == null) || (type == null) || (body == null) || (size == 0)) { return null; } Reference rv = doAdd(contentHostingId(name, application, context, prefix, uniqueHolder), name, type, body, size, false); // if this failed, and we are not using a uniqueHolder, try it with a uniqueHolder if ((rv == null) && !uniqueHolder) { rv = doAdd(contentHostingId(name, application, context, prefix, true), name, type, body, size, false); } // TODO: we might not want a thumb (such as for submission uploads to essay/task // if we added one if (rv != null) { // if it is an image if (type.toLowerCase().startsWith("image/")) { addThumb(rv, name, body); } } return rv; } finally { popAdvisor(); } } /** * {@inheritDoc} */ public String archive(String siteId, Document doc, Stack stack, String archivePath, List attachments) { return null; } /** * Returns to uninitialized state. */ public void destroy() { M_log.info("destroy()"); } /** * {@inheritDoc} */ public List<Attachment> findFiles(String application, String context, String prefix) { return findTypes(application, context, prefix, null); } /** * {@inheritDoc} */ public List<Attachment> findImages(String application, String context, String prefix) { return findTypes(application, context, prefix, "image/"); } /** * {@inheritDoc} */ public Entity getEntity(Reference ref) { // decide on security if (!checkSecurity(ref)) return null; // isolate the ContentHosting reference Reference contentHostingRef = entityManager.newReference(ref.getId()); // setup a security advisor pushAdvisor(); try { // make sure we have a valid ContentHosting reference with an entity producer we can talk to EntityProducer service = contentHostingRef.getEntityProducer(); if (service == null) return null; // pass on the request return service.getEntity(contentHostingRef); } finally { // clear the security advisor popAdvisor(); } } /** * {@inheritDoc} */ public Collection getEntityAuthzGroups(Reference ref, String userId) { // Since we handle security ourself, we won't support anyone else asking return null; } /** * {@inheritDoc} */ public String getEntityDescription(Reference ref) { // decide on security if (!checkSecurity(ref)) return null; // isolate the ContentHosting reference Reference contentHostingRef = entityManager.newReference(ref.getId()); // setup a security advisor pushAdvisor(); try { // make sure we have a valid ContentHosting reference with an entity producer we can talk to EntityProducer service = contentHostingRef.getEntityProducer(); if (service == null) return null; // pass on the request return service.getEntityDescription(contentHostingRef); } finally { // clear the security advisor popAdvisor(); } } /** * {@inheritDoc} */ public ResourceProperties getEntityResourceProperties(Reference ref) { // decide on security if (!checkSecurity(ref)) return null; // isolate the ContentHosting reference Reference contentHostingRef = entityManager.newReference(ref.getId()); // setup a security advisor pushAdvisor(); try { // make sure we have a valid ContentHosting reference with an entity producer we can talk to EntityProducer service = contentHostingRef.getEntityProducer(); if (service == null) return null; // pass on the request return service.getEntityResourceProperties(contentHostingRef); } finally { // clear the security advisor popAdvisor(); } } /** * {@inheritDoc} */ public String getEntityUrl(Reference ref) { return serverConfigurationService.getAccessUrl() + ref.getReference(); } /** * {@inheritDoc} */ public HttpAccess getHttpAccess() { return new HttpAccess() { public void handleAccess(HttpServletRequest req, HttpServletResponse res, Reference ref, Collection copyrightAcceptedRefs) throws EntityPermissionException, EntityNotDefinedException, EntityAccessOverloadException, EntityCopyrightException { // decide on security if (!checkSecurity(ref)) { throw new EntityPermissionException(sessionManager.getCurrentSessionUserId(), "sampleAccess", ref.getReference()); } // isolate the ContentHosting reference Reference contentHostingRef = entityManager.newReference(ref.getId()); // setup a security advisor pushAdvisor(); try { // make sure we have a valid ContentHosting reference with an entity producer we can talk to EntityProducer service = contentHostingRef.getEntityProducer(); if (service == null) throw new EntityNotDefinedException(ref.getReference()); // get the producer's HttpAccess helper, it might not support one HttpAccess access = service.getHttpAccess(); if (access == null) throw new EntityNotDefinedException(ref.getReference()); // let the helper do the work access.handleAccess(req, res, contentHostingRef, copyrightAcceptedRefs); } finally { // clear the security advisor popAdvisor(); } } }; } /** * {@inheritDoc} */ public String getLabel() { return null; } /** * {@inheritDoc} */ public Reference getReference(String refString) { Reference ref = this.entityManager.newReference(refString); return ref; } /** * {@inheritDoc} */ public Set<String> harvestAttachmentsReferenced(String data, boolean normalize) { Set<String> rv = new HashSet<String>(); if (data == null) return rv; // pattern to find any src= or href= text // groups: 0: the whole matching text 1: src|href 2: the string in the quotes Pattern p = Pattern.compile("(src|href)[\\s]*=[\\s]*\"([^\"]*)\""); Matcher m = p.matcher(data); while (m.find()) { if (m.groupCount() == 2) { String ref = m.group(2); // harvest any content hosting reference int index = ref.indexOf("/access/content/"); if (index != -1) { // except for any in /user/ or /public/ if (ref.indexOf("/access/content/user/") != -1) { index = -1; } else if (ref.indexOf("/access/content/public/") != -1) { index = -1; } } // harvest also the mneme docs references if (index == -1) index = ref.indexOf("/access/mneme/content/"); // TODO: further filter to docs root and context (optional) if (index != -1) { // save just the reference part (i.e. after the /access); String refString = ref.substring(index + 7); // deal with %20 and other encoded URL stuff if (normalize) { try { refString = URLDecoder.decode(refString, "UTF-8"); } catch (UnsupportedEncodingException e) { M_log.warn("harvestAttachmentsReferenced: " + e); } } rv.add(refString); } } } return rv; } /** * Final initialization, once all dependencies are set. */ public void init() { try { // register as an entity producer entityManager.registerEntityProducer(this, REFERENCE_ROOT); M_log.info("init(): thumbs: " + this.makeThumbs); } catch (Throwable t) { M_log.warn("init(): ", t); } } /** * {@inheritDoc} */ public String merge(String siteId, Element root, String archivePath, String fromSiteId, Map attachmentNames, Map userIdTrans, Set userListAllowImport) { return null; } /** * {@inheritDoc} */ public boolean parseEntityReference(String reference, Reference ref) { if (reference.startsWith(REFERENCE_ROOT)) { // we will get null, sampleAccess, content, private, sampleAccess, <context>, test.txt // we will store the context, and the ContentHosting reference in our id field. String id = null; String context = null; String[] parts = StringUtil.split(reference, Entity.SEPARATOR); if (parts.length > 5) { context = parts[5]; id = "/" + StringUtil.unsplit(parts, 2, parts.length - 2, "/"); } ref.set(APPLICATION_ID, null, id, null, context); return true; } return false; } /** * {@inheritDoc} */ public void removeAttachment(Reference ref) { pushAdvisor(); try { String id = entityManager.newReference(ref.getId()).getId(); // check if this has a unique containing collection ContentResource resource = this.contentHostingService.getResource(id); ContentCollection collection = resource.getContainingCollection(); // remove the resource this.contentHostingService.removeResource(id); // if the collection was made just to hold the attachment, remove it as well if (collection.getProperties().getProperty(PROP_UNIQUE_HOLDER) != null) { this.contentHostingService.removeCollection(collection.getId()); } } catch (PermissionException e) { M_log.warn("removeAttachment: " + e.toString()); } catch (ServerOverloadException e) { M_log.warn("removeAttachment: " + e.toString()); } catch (InUseException e) { M_log.warn("removeAttachment: " + e.toString()); } catch (IdUnusedException e) { M_log.warn("removeAttachment: " + e.toString()); } catch (TypeException e) { M_log.warn("removeAttachment: " + e.toString()); } finally { popAdvisor(); } } /** * Dependency: ContentHostingService. * * @param service * The ContentHostingService. */ public void setContentHostingService(ContentHostingService service) { contentHostingService = service; } /** * Dependency: EntityManager. * * @param service * The EntityManager. */ public void setEntityManager(EntityManager service) { entityManager = service; } /** * Set the IdManager * * @param IdManager * The IdManager */ public void setIdManager(IdManager idManager) { this.idManager = idManager; } /** * Set the make thumbs setting * * @param value * the string of the boolean for the make thumbs setting. */ public void setMakeThumbs(String value) { this.makeThumbs = Boolean.valueOf(value); } /** * Dependency: SecurityService. * * @param service * The SecurityService. */ public void setSecurityService(SecurityService service) { this.securityService = service; } /** * Dependency: SecurityService. * * @param service * The SecurityService. */ public void setSecurityServiceSakai(org.sakaiproject.authz.api.SecurityService service) { this.securityServiceSakai = service; } /** * Dependency: ServerConfigurationService. * * @param service * The ServerConfigurationService. */ public void setServerConfigurationService(ServerConfigurationService service) { this.serverConfigurationService = service; } /** * Dependency: SessionManager. * * @param service * The SessionManager. */ public void setSessionManager(SessionManager service) { this.sessionManager = service; } /** * Dependency: SubmissionService. * * @param service * The SubmissionService. */ public void setSubmissionService(SubmissionService service) { this.submissionService = service; } /** * {@inheritDoc} */ public String translateEmbeddedReferences(String data, List<Translation> translations) { if (data == null) return data; if (translations == null) return data; // pattern to find any src= or href= text // groups: 0: the whole matching text 1: src|href 2: the string in the quotes Pattern p = Pattern.compile("(src|href)[\\s]*=[\\s]*\"([^\"]*)\""); Matcher m = p.matcher(data); StringBuffer sb = new StringBuffer(); // process each "harvested" string (avoiding like strings that are not in src= or href= patterns) while (m.find()) { if (m.groupCount() == 2) { String ref = m.group(2); // harvest any content hosting reference int index = ref.indexOf("/access/content/"); if (index != -1) { // except for any in /user/ or /public/ if (ref.indexOf("/access/content/user/") != -1) { index = -1; } else if (ref.indexOf("/access/content/public/") != -1) { index = -1; } } // harvest also the mneme docs references if (index == -1) index = ref.indexOf("/access/mneme/content/"); if (index != -1) { // save just the reference part (i.e. after the /access); String normal = ref.substring(index + 7); // deal with %20 and other encoded URL stuff try { normal = URLDecoder.decode(normal, "UTF-8"); } catch (UnsupportedEncodingException e) { M_log.warn("harvestAttachmentsReferenced: " + e); } // translate the normal form String translated = normal; for (Translation translation : translations) { translated = translation.translate(translated); } // URL encode translated String escaped = EscapeRefUrl.escapeUrl(translated); // if changed, replace if (!normal.equals(translated)) { m.appendReplacement(sb, Matcher.quoteReplacement( m.group(1) + "=\"" + ref.substring(0, index + 7) + escaped + "\"")); } } } } m.appendTail(sb); return sb.toString(); } /** * {@inheritDoc} */ public boolean willArchiveMerge() { return false; } /** * Add a thumbnail for the image at this reference with this name and contents. * * @param resource * The image resource reference. * @param name * The original file name. * @param body * The image bytes. * @return A reference to the thumbnail, or null if not made. */ protected Reference addThumb(Reference resource, String name, byte[] body) { // if disabled if (!this.makeThumbs) return null; Reference ref = this.getReference(resource.getId()); String thumbId = ref.getId() + THUMB_SUFFIX; String thumbName = name + THUMB_SUFFIX; try { byte[] thumb = makeThumb(body, 80, 80, 0.75f); Reference thumbRef = doAdd(thumbId, thumbName, "image/jpeg", thumb, thumb.length, true); return thumbRef; } catch (IOException e) { M_log.warn("addAttachment: thumbing: " + e.toString()); } catch (InterruptedException e) { M_log.warn("addAttachment: thumbing: " + e.toString()); } return null; } /** * Assure that a collection with this name exists in the container collection: create it if it is missing. * * @param container * The full path of the container collection. * @param name * The collection name to check and create (no trailing slash needed). * @param uniqueHolder * true if the folder is being created solely to hold the attachment uniquely. */ protected void assureCollection(String container, String name, boolean uniqueHolder) { try { contentHostingService.getCollection(container + name + "/"); } catch (IdUnusedException e) { try { ContentCollectionEdit edit = contentHostingService.addCollection(container + name + "/"); ResourcePropertiesEdit props = edit.getPropertiesEdit(); // set the alternate reference root so we get all requests props.addProperty(ContentHostingService.PROP_ALTERNATE_REFERENCE, AttachmentService.REFERENCE_ROOT); props.addProperty(ResourceProperties.PROP_DISPLAY_NAME, name); // mark it if it is a unique holder if (uniqueHolder) { props.addProperty(PROP_UNIQUE_HOLDER, PROP_UNIQUE_HOLDER); } contentHostingService.commitCollection(edit); } catch (IdUsedException e2) { // M_log.warn("init: creating our root collection: " + e2.toString()); } catch (IdInvalidException e2) { M_log.warn("assureCollection: " + e2.toString()); } catch (PermissionException e2) { M_log.warn("assureCollection: " + e2.toString()); } catch (InconsistentException e2) { M_log.warn("assureCollection: " + e2.toString()); } } catch (TypeException e) { M_log.warn("assureCollection(2): " + e.toString()); } catch (PermissionException e) { M_log.warn("assureCollection(2): " + e.toString()); } } /** * Check security for this entity. * * @param ref * The Reference to the entity. * @return true if allowed, false if not. */ protected boolean checkSecurity(Reference ref) { String userId = this.sessionManager.getCurrentSessionUserId(); String context = ref.getContext(); String[] parts = StringUtil.split(ref.getId(), "/"); if ((parts.length == 9) && (SUBMISSIONS_AREA.equals(parts[5]))) { // manage or grade permission for the context is still a winner if (this.securityService.checkSecurity(userId, MnemeService.MANAGE_PERMISSION, context)) return true; if (this.securityService.checkSecurity(userId, MnemeService.GRADE_PERMISSION, context)) return true; // otherwise, user must be submission user and have submit permission if (this.securityService.checkSecurity(userId, MnemeService.SUBMIT_PERMISSION, context)) { Submission submission = this.submissionService.getSubmission(parts[6]); if (submission != null) { if (submission.getUserId().equals(userId)) { return true; } } } return false; } // this is how we used to do it, without a submission id in the ref if (this.securityService.checkSecurity(userId, MnemeService.MANAGE_PERMISSION, context)) return true; if (this.securityService.checkSecurity(userId, MnemeService.SUBMIT_PERMISSION, context)) return true; return false; } /** * Compute the content hosting id. * * @param name * The file name. * @param application * The application prefix for the collection in private. * @param context * The context associated with the attachment. * @param prefix * Any prefix path for within the context are of the application in private. * @param uniqueHolder * If true, a uniquely named folder is created to hold the resource. * @return */ protected String contentHostingId(String name, String application, String context, String prefix, boolean uniqueHolder) { // form the content hosting path, and make sure all the folders exist String contentPath = "/private/"; assureCollection(contentPath, application, false); contentPath += application + "/"; assureCollection(contentPath, context, false); contentPath += context + "/"; if ((prefix != null) && (prefix.length() > 0)) { // allow multi-part prefix if (prefix.indexOf('/') != -1) { String[] prefixes = StringUtil.split(prefix, "/"); for (String pre : prefixes) { assureCollection(contentPath, pre, false); contentPath += pre + "/"; } } else { assureCollection(contentPath, prefix, false); contentPath += prefix + "/"; } } if (uniqueHolder) { String uuid = this.idManager.createUuid(); assureCollection(contentPath, uuid, true); contentPath += uuid + "/"; } contentPath += name; return contentPath; } /** * Perform the add. * * @param id * The content hosting id. * @param name * The simple file name. * @param type * The mime type. * @param body * The body bytes. * @param size * The body size. * @param application * The application prefix for the collection in private. * @param context * The context associated with the attachment. * @param prefix * Any prefix path for within the context are of the application in private. * @param uniqueHolder * If true, a uniquely named folder is created to hold the resource. * @return The Reference to the added attachment. */ protected Reference doAdd(String id, String name, String type, byte[] body, long size, boolean thumb) { try { ContentResourceEdit edit = this.contentHostingService.addResource(id); edit.setContent(body); edit.setContentType(type); ResourcePropertiesEdit props = edit.getPropertiesEdit(); // set the alternate reference root so we get all requests props.addProperty(ContentHostingService.PROP_ALTERNATE_REFERENCE, AttachmentService.REFERENCE_ROOT); props.addProperty(ResourceProperties.PROP_DISPLAY_NAME, name); if (thumb) { props.addProperty(PROP_THUMB, PROP_THUMB); } this.contentHostingService.commitResource(edit); String ref = edit.getReference(ContentHostingService.PROP_ALTERNATE_REFERENCE); Reference reference = entityManager.newReference(ref); return reference; } catch (PermissionException e2) { M_log.warn("addAttachment: creating our content: " + e2.toString()); } catch (IdUsedException e2) { // M_log.warn("addAttachment: creating our content: " + e2.toString()); } catch (IdInvalidException e2) { M_log.warn("addAttachment: creating our content: " + e2.toString()); } catch (InconsistentException e2) { M_log.warn("addAttachment: creating our content: " + e2.toString()); } catch (ServerOverloadException e2) { M_log.warn("addAttachment: creating our content: " + e2.toString()); } catch (OverQuotaException e2) { M_log.warn("addAttachment: creating our content: " + e2.toString()); } finally { // try // { // // TODO: if using input stream // if (body != null) body.close(); // } // catch (IOException e) // { // } } return null; } /** * If the document matches typePrefix and not a thumbnail, add it to the attachments. * * @param attachments * The list of attachments. * @param doc * The resource. * @param typePrefix * if null, match any type, else match only the types that match this prefix. */ protected void filterNonThumbTypes(List<Attachment> attachments, ContentResource doc, String typePrefix) { // only matching types if ((typePrefix == null) || (doc.getContentType().toLowerCase().startsWith(typePrefix.toLowerCase()))) { // not thumbs if (doc.getProperties().getProperty(this.PROP_THUMB) == null) { String ref = doc.getReference(ContentHostingService.PROP_ALTERNATE_REFERENCE); String url = doc.getUrl(ContentHostingService.PROP_ALTERNATE_REFERENCE); String escapedUrl = EscapeRefUrl.escapeRefUrl(ref, url); Attachment a = new AttachmentImpl( doc.getProperties().getProperty(ResourceProperties.PROP_DISPLAY_NAME), ref, escapedUrl, doc.getContentType()); attachments.add(a); } } } /** * Find all the attachments in the docs area of the application for this context. Skip image thumbs. Select only those matching type. * * @param application * The application prefix for the collection in private. * @param context * The context associated with the attachment. * @param prefix * Any prefix path for within the context are of the application in private. * @param typePrefix * if null, all but the thumbs. Otherwise only those matching the prefix in mime type. * @return A List of Attachments to the attachments. */ protected List<Attachment> findTypes(String application, String context, String prefix, String typePrefix) { // permission pushAdvisor(); List<Attachment> rv = new ArrayList<Attachment>(); try { // form the content hosting path to the docs collection String docsCollection = "/private/"; docsCollection += application + "/"; docsCollection += context + "/"; if ((prefix != null) && (prefix.length() > 0)) { docsCollection += prefix + "/"; } // get the members of this collection ContentCollection docs = contentHostingService.getCollection(docsCollection); List members = docs.getMemberResources(); for (Object m : members) { if (m instanceof ContentCollection) { // get the member within ContentCollection holder = (ContentCollection) m; List innerMembers = holder.getMemberResources(); for (Object mm : innerMembers) { if (mm instanceof ContentResource) { filterNonThumbTypes(rv, (ContentResource) mm, typePrefix); } } } else if (m instanceof ContentResource) { filterNonThumbTypes(rv, (ContentResource) m, typePrefix); } } } catch (IdUnusedException e) { } catch (TypeException e) { } catch (PermissionException e) { } finally { popAdvisor(); } return rv; } /** * Create a thumbnail image from the full image in the byte[], of the desired width and height and quality, preserving aspect ratio. * * @param full * The full image bytes. * @param width * The desired max width (pixels). * @param height * The desired max height (pixels). * @param quality * The JPEG quality (0 - 1). * @return The thumbnail JPEG as a byte[]. * @throws IOException * @throws InterruptedException */ protected byte[] makeThumb(byte[] full, int width, int height, float quality) throws IOException, InterruptedException { // read the image from the byte array, waiting till it's processed Image fullImage = Toolkit.getDefaultToolkit().createImage(full); MediaTracker tracker = new MediaTracker(new Container()); tracker.addImage(fullImage, 0); tracker.waitForID(0); // get the full image dimensions int fullWidth = fullImage.getWidth(null); int fullHeight = fullImage.getHeight(null); // preserve the aspect of the full image, not exceeding the thumb dimensions if (fullWidth > fullHeight) { // full width will take the full desired width, set the appropriate height height = (int) ((((float) width) / ((float) fullWidth)) * ((float) fullHeight)); } else { // full height will take the full desired height, set the appropriate width width = (int) ((((float) height) / ((float) fullHeight)) * ((float) fullWidth)); } // draw the scaled thumb BufferedImage thumbImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g2D = thumbImage.createGraphics(); g2D.drawImage(fullImage, 0, 0, width, height, null); // encode as jpeg to a byte array ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); BufferedOutputStream out = new BufferedOutputStream(byteStream); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(thumbImage); param.setQuality(quality, false); encoder.setJPEGEncodeParam(param); encoder.encode(thumbImage); out.close(); byte[] thumb = byteStream.toByteArray(); return thumb; } /** * Trim the name to only the characters after the last slash of either kind.<br /> * Remove junk from uploaded file names. * * @param name * The string to trim. * @return The trimmed string. */ protected String massageName(String name) { // if there are any slashes, forward or back, take from the last one found to the right as the name int pos = -1; for (int i = name.length() - 1; i >= 0; i--) { char c = name.charAt(i); if ((c == '/') || (c == '\\')) { pos = i + 1; break; } } if (pos != -1) { name = name.substring(pos); } return name; } /** * Remove our security advisor. */ protected void popAdvisor() { securityServiceSakai.popAdvisor(); } /** * Setup a security advisor. */ protected void pushAdvisor() { // setup a security advisor securityServiceSakai.pushAdvisor(new SecurityAdvisor() { public SecurityAdvice isAllowed(String userId, String function, String reference) { return SecurityAdvice.ALLOWED; } }); } }