Index: content/content-api/api/src/java/org/sakaiproject/content/api/ResourceToolAction.java =================================================================== --- content/content-api/api/src/java/org/sakaiproject/content/api/ResourceToolAction.java (revisión: 45398) +++ content/content-api/api/src/java/org/sakaiproject/content/api/ResourceToolAction.java (copia de trabajo) @@ -173,8 +173,17 @@ * must implement the CustomToolAction interface to provide Resources tool with a way to * determine permissions, as well as either InteractionAction or ServiceLevelAction. */ - CUSTOM_TOOL_ACTION - + CUSTOM_TOOL_ACTION, + + /** + * Compress a selected folder to a zip archive with the same name. + */ + COMPRESS_ZIP_FOLDER, + + /** + * Expands a zip file into serveral folders and archives + */ + EXPAND_ZIP_ARCHIVE } public static final String CREATE = "create"; @@ -193,6 +202,8 @@ public static final String REORDER = "revise_order"; public static final String EXPAND = "expand"; public static final String COLLAPSE = "collapse"; + public static final String COMPRESS_ZIP_FOLDER = "compress_zip_folder"; + public static final String EXPAND_ZIP_ARCHIVE = "expand_zip_archive"; public static final String ACTION_DELIMITER = ":"; Index: content/content-tool/tool/src/java/org/sakaiproject/content/tool/ResourcesAction.java =================================================================== --- content/content-tool/tool/src/java/org/sakaiproject/content/tool/ResourcesAction.java (revisión: 45398) +++ content/content-tool/tool/src/java/org/sakaiproject/content/tool/ResourcesAction.java (copia de trabajo) @@ -846,6 +846,8 @@ CONTENT_MODIFY_ACTIONS.add(ActionType.REVISE_CONTENT); CONTENT_MODIFY_ACTIONS.add(ActionType.REPLACE_CONTENT); CONTENT_MODIFY_ACTIONS.add(ActionType.REVISE_ORDER); + CONTENT_MODIFY_ACTIONS.add(ActionType.COMPRESS_ZIP_FOLDER); + CONTENT_MODIFY_ACTIONS.add(ActionType.EXPAND_ZIP_ARCHIVE); CONTENT_DELETE_ACTIONS.add(ActionType.MOVE); CONTENT_DELETE_ACTIONS.add(ActionType.DELETE); Index: content/content-bundles/types.properties =================================================================== --- content/content-bundles/types.properties (revisión: 45398) +++ content/content-bundles/types.properties (copia de trabajo) @@ -40,7 +40,9 @@ action.reorder = Reorder action.replace = Upload New Version action.revise = Edit Content -action.select = - Select Action - +action.select = - Select Action - +action.compresszipfolder = Compress to ZIP Archive +action.expandziparchive = Expand ZIP Archive alert.exists = The folder ''{0}'' already exists in this folder. alert.noperm = You do not have permission to change this item. alert.nofldr = Please provide the folder name. Index: content/content-impl/.classpath =================================================================== --- content/content-impl/.classpath (revisión: 45398) +++ content/content-impl/.classpath (copia de trabajo) @@ -31,5 +31,7 @@ + + Index: content/content-impl/impl/src/java/org/sakaiproject/content/impl/util/ZipContentUtil.java =================================================================== --- content/content-impl/impl/src/java/org/sakaiproject/content/impl/util/ZipContentUtil.java (revisión: 0) +++ content/content-impl/impl/src/java/org/sakaiproject/content/impl/util/ZipContentUtil.java (revisión: 0) @@ -0,0 +1,220 @@ +package org.sakaiproject.content.impl.util; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.Enumeration; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +import javax.activation.MimetypesFileTypeMap; + +import org.apache.commons.io.IOUtils; +import org.sakaiproject.content.api.ContentCollection; +import org.sakaiproject.content.api.ContentCollectionEdit; +import org.sakaiproject.content.api.ContentResource; +import org.sakaiproject.content.api.ContentResourceEdit; +import org.sakaiproject.content.cover.ContentHostingService; +import org.sakaiproject.entity.api.Entity; +import org.sakaiproject.entity.api.Reference; +import org.sakaiproject.entity.api.ResourcePropertiesEdit; + +public class ZipContentUtil { + + private static final String ZIP_EXTENSION = ".zip"; + private static final int BUFFER_SIZE = 8192; + private static final MimetypesFileTypeMap mime = new MimetypesFileTypeMap(); + + /** + * Compresses a ContentCollection to a new zip archive with the same folder name + * + * @param reference + * @throws Exception + */ + public void compressFolder(Reference reference) throws Exception { + File temp = null; + try { + // Create the compressed archive in the filesystem + temp = File.createTempFile("sakai_content-", ".tmp"); + temp.deleteOnExit(); + ContentCollection collection = ContentHostingService.getCollection(reference.getId()); + ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(temp),BUFFER_SIZE)); + storeContentCollection(reference.getId(),collection,out); + out.close(); + + // Store the compressed archive in the repository + String resourceId = reference.getId().substring(0,reference.getId().lastIndexOf(Entity.SEPARATOR))+ZIP_EXTENSION; + String resourceName = extractName(resourceId); + ContentResourceEdit resourceEdit = ContentHostingService.addResource(resourceId); + resourceEdit.setContent(new FileInputStream(temp)); + resourceEdit.setContentType(mime.getContentType(resourceId)); + ResourcePropertiesEdit props = resourceEdit.getPropertiesEdit(); + props.addProperty(ResourcePropertiesEdit.PROP_DISPLAY_NAME, resourceName); + ContentHostingService.commitResource(resourceEdit); + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + temp.delete(); + } + } + + /** + * Extracts a compressed (zip) ContentResource to a new folder with the same name. + * + * @param reference + * @throws Exception + */ + public void extractArchive(Reference reference) throws Exception { + ContentResource resource = ContentHostingService.getResource(reference.getId()); + String rootCollectionId = extractZipCollectionPrefix(resource); + + // Prepare Collection + ContentCollectionEdit rootCollection = ContentHostingService.addCollection(rootCollectionId); + ResourcePropertiesEdit prop = rootCollection.getPropertiesEdit(); + prop.addProperty(ResourcePropertiesEdit.PROP_DISPLAY_NAME, extractZipCollectionName(resource)); + ContentHostingService.commitCollection(rootCollection); + + // Extract Zip File + File temp = null; + try { + temp = exportResourceToFile(resource); + ZipFile zipFile = new ZipFile(temp,ZipFile.OPEN_READ); + Enumeration entries = zipFile.entries(); + + while (entries.hasMoreElements()) { + ZipEntry nextElement = entries.nextElement(); + if (nextElement.isDirectory()) { + createContentCollection(rootCollectionId, nextElement); + } + else { + createContentResource(rootCollectionId, nextElement, zipFile); + } + } + zipFile.close(); + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + temp.delete(); + } + + } + + /** + * Creates a new ContentResource extracted from ZipFile + * + * @param rootCollectionId + * @param nextElement + * @param zipFile + * @throws Exception + */ + private void createContentResource(String rootCollectionId, + ZipEntry nextElement, ZipFile zipFile) throws Exception { + String resourceId = rootCollectionId + nextElement.getName(); + String resourceName = extractName(nextElement.getName()); + ContentResourceEdit resourceEdit = ContentHostingService.addResource(resourceId); + resourceEdit.setContent(zipFile.getInputStream(nextElement)); + resourceEdit.setContentType(mime.getContentType(resourceName)); + ResourcePropertiesEdit props = resourceEdit.getPropertiesEdit(); + props.addProperty(ResourcePropertiesEdit.PROP_DISPLAY_NAME, resourceName); + ContentHostingService.commitResource(resourceEdit); + } + + /** + * Creates a new ContentCollection in the rootCollectionId with the element.getName() + * + * @param rootCollectionId + * @param element + * @throws Exception + */ + private void createContentCollection(String rootCollectionId, + ZipEntry element) throws Exception { + String resourceId = rootCollectionId + element.getName(); + String resourceName = extractName(element.getName()); + ContentCollectionEdit collection = ContentHostingService.addCollection(resourceId); + ResourcePropertiesEdit props = collection.getPropertiesEdit(); + props.addProperty(ResourcePropertiesEdit.PROP_DISPLAY_NAME, resourceName); + ContentHostingService.commitCollection(collection); + } + + /** + * Exports a the ContentResource zip file to the operating system + * + * @param resource + * @return + * @throws Exception + */ + private File exportResourceToFile(ContentResource resource) throws Exception { + File temp = File.createTempFile("sakai_content-", ".tmp"); + temp.deleteOnExit(); + + // Write content to file + FileOutputStream out = new FileOutputStream(temp); + IOUtils.copy(resource.streamContent(),out); + out.flush(); + out.close(); + + return temp; + } + + /** + * Iterates the collection.getMembers() and streams content resources recursively to the ZipOutputStream + * + * @param rootId + * @param collection + * @param out + * @throws Exception + */ + private void storeContentCollection(String rootId, ContentCollection collection, ZipOutputStream out) throws Exception { + List members = collection.getMembers(); + for (String memberId: members) { + if (memberId.endsWith(Entity.SEPARATOR)) { + ContentCollection memberCollection = ContentHostingService.getCollection(memberId); + storeContentCollection(rootId,memberCollection,out); + } + else { + ContentResource resource = ContentHostingService.getResource(memberId); + storeContentResource(rootId, resource, out); + } + } + } + + /** + * Streams content resource to the ZipOutputStream + * + * @param rootId + * @param resource + * @param out + * @throws Exception + */ + private void storeContentResource(String rootId, ContentResource resource, ZipOutputStream out) throws Exception { + String filename = resource.getId().substring(rootId.length(),resource.getId().length()); + ZipEntry zipEntry = new ZipEntry(filename); + zipEntry.setSize(resource.getContentLength()); + out.putNextEntry(zipEntry); + IOUtils.copy(resource.streamContent(),out); + } + + private String extractZipCollectionPrefix(ContentResource resource) { + String idPrefix = resource.getContainingCollection().getId() + + extractZipCollectionName(resource) + + Entity.SEPARATOR; + return idPrefix; + } + + private String extractName(String collectionName) { + String[] tmp = collectionName.split(Entity.SEPARATOR); + return tmp[tmp.length-1]; + } + + private String extractZipCollectionName(ContentResource resource) { + String tmp = extractName(resource.getId()); + return tmp.substring(0, tmp.lastIndexOf(".")); + } +} \ Sin fin-de-línea al final del archivo Cambios de propiedades en content/content-impl/impl/src/java/org/sakaiproject/content/impl/util/ZipContentUtil.java ___________________________________________________________________ Nombre: svn:keywords + Date Revision Author HeadURL Id Nombre: svn:eol-style + native Index: content/content-impl/impl/src/java/org/sakaiproject/content/types/FileUploadType.java =================================================================== --- content/content-impl/impl/src/java/org/sakaiproject/content/types/FileUploadType.java (revisión: 45398) +++ content/content-impl/impl/src/java/org/sakaiproject/content/types/FileUploadType.java (copia de trabajo) @@ -43,6 +43,7 @@ import org.sakaiproject.content.api.ServiceLevelAction; import org.sakaiproject.content.api.ResourceToolAction.ActionType; import org.sakaiproject.content.cover.ContentTypeImageService; +import org.sakaiproject.content.impl.util.ZipContentUtil; import org.sakaiproject.content.util.BaseResourceType; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; @@ -77,6 +78,7 @@ actions.put(ResourceToolAction.COPY, new FileUploadCopyAction()); actions.put(ResourceToolAction.MOVE, new FileUploadMoveAction()); actions.put(ResourceToolAction.DELETE, new FileUploadDeleteAction()); + actions.put(ResourceToolAction.EXPAND_ZIP_ARCHIVE, new FileUploadExpandAction()); // initialize actionMap with an empty List for each ActionType for(ResourceToolAction.ActionType type : ResourceToolAction.ActionType.values()) @@ -741,6 +743,55 @@ } } + public class FileUploadExpandAction implements ServiceLevelAction { + + private ZipContentUtil extractZipArchive = new ZipContentUtil(); + + public void cancelAction(Reference reference) { + // TODO Auto-generated method stub + + } + + public void finalizeAction(Reference reference) { + // TODO Auto-generated method stub + + } + + public void initializeAction(Reference reference) { + try { + extractZipArchive.extractArchive(reference); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + public boolean isMultipleItemAction() { + // TODO Auto-generated method stub + return false; + } + + public boolean available(ContentEntity entity) { + return entity.getId().toLowerCase().endsWith(".zip"); + } + + public ActionType getActionType() { + return ResourceToolAction.ActionType.EXPAND_ZIP_ARCHIVE; + } + + public String getId() { + return ResourceToolAction.EXPAND_ZIP_ARCHIVE; + } + + public String getLabel() { + return rb.getString("action.expandziparchive"); + } + + public String getTypeId() { + return typeId; + } + + } + public ResourceToolAction getAction(String actionId) { return (ResourceToolAction) actions.get(actionId); Index: content/content-impl/impl/src/java/org/sakaiproject/content/types/FolderType.java =================================================================== --- content/content-impl/impl/src/java/org/sakaiproject/content/types/FolderType.java (revisión: 45398) +++ content/content-impl/impl/src/java/org/sakaiproject/content/types/FolderType.java (copia de trabajo) @@ -44,6 +44,7 @@ import org.sakaiproject.content.api.ResourceType; import org.sakaiproject.content.api.ServiceLevelAction; import org.sakaiproject.content.api.ResourceToolAction.ActionType; +import org.sakaiproject.content.impl.util.ZipContentUtil; import org.sakaiproject.content.util.BaseResourceType; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; @@ -90,6 +91,7 @@ actions.put(ResourceToolAction.PERMISSIONS, new FolderPermissionsAction()); actions.put(ResourceToolAction.EXPAND, new FolderExpandAction()); actions.put(ResourceToolAction.COLLAPSE, new FolderCollapseAction()); + actions.put(ResourceToolAction.COMPRESS_ZIP_FOLDER, new FolderCompressAction()); // initialize actionMap with an empty List for each ActionType for(ResourceToolAction.ActionType type : ResourceToolAction.ActionType.values()) @@ -1392,6 +1394,52 @@ } + public class FolderCompressAction implements ServiceLevelAction { + + private ZipContentUtil zipUtil = new ZipContentUtil(); + + public void cancelAction(Reference reference) { + // TODO Auto-generated method stub + } + + public void finalizeAction(Reference reference) { + // TODO Auto-generated method stub + } + + public void initializeAction(Reference reference) { + try { + zipUtil.compressFolder(reference); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + } + + public boolean isMultipleItemAction() { + // TODO Auto-generated method stub + return false; + } + + public boolean available(ContentEntity entity) { + return true; + } + + public ActionType getActionType() { + return ResourceToolAction.ActionType.COMPRESS_ZIP_FOLDER; + } + + public String getId() { + return ResourceToolAction.COMPRESS_ZIP_FOLDER; + } + + public String getLabel() { + return rb.getString("action.compresszipfolder"); + } + + public String getTypeId() { + return typeId; + } + } + public ResourceToolAction getAction(String actionId) { return (ResourceToolAction) actions.get(actionId); Index: content/content-impl/impl/pom.xml =================================================================== --- content/content-impl/impl/pom.xml (revisión: 45398) +++ content/content-impl/impl/pom.xml (copia de trabajo) @@ -131,6 +131,16 @@ commons-logging 1.0.4 + + org.apache.commons + commons-io + 1.3.2 + + + javax.activation + activation + 1.0.2 + commons-dbcp