Skip to content

Commit

Permalink
SAK-41272 Code improvements for Zip download in Resources. (sakaiproj…
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaMiller-Which authored and Miguel Pellicer committed Mar 12, 2019
1 parent 83c69ae commit b91cec4
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 209 deletions.
3 changes: 1 addition & 2 deletions content/content-bundles/resources/types.properties
Original file line number Diff line number Diff line change
Expand Up @@ -599,9 +599,8 @@ zipdownload.sure=Are you sure to download a zip file with these resources?
zipdownload.show=Download item(s) confirmation...
zipdownload.table=Table holds information about materials selected for downloading. Column headers contain descriptions of contents and links for sorting.
zipdownload.show1=Download
zipdownload.maxIndividualSize=File ''{0}'' exceeds the allowed size for a single file ({1}).
zipdownload.maxIndividualSizeInFolder=The file wth path ''{0}'' exceeds the allowed size for a single file ({1}) that can be put in the Zip.
zipdownload.maxTotalSize=The files to add to the Zip exceed the maximum allowed size ({0}).
zipdownload.maxTotalSize=The files to add to the Zip exceed the maximum allowed size for the Zip ({0}).

#SAK-30924
label.overwrite.warning=Warning: Existing files will be overwritten.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@
import org.sakaiproject.content.api.providers.SiteContentAdvisorProvider;
import org.sakaiproject.content.copyright.api.CopyrightInfo;
import org.sakaiproject.content.copyright.api.CopyrightItem;
import org.sakaiproject.content.exception.ZipMaxSingleFileSizeException;
import org.sakaiproject.content.exception.ZipMaxTotalSizeException;
import org.sakaiproject.content.util.ZipContentUtil;
import org.sakaiproject.entity.api.Entity;
Expand All @@ -135,6 +134,7 @@
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.exception.ServerOverloadException;
import org.sakaiproject.exception.TypeException;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.Group;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SitePage;
Expand Down Expand Up @@ -788,6 +788,9 @@ public void setParent(String parent)
/** the site title */
private static final String STATE_SITE_TITLE = PREFIX + REQUEST + "site_title";

/** the site ID */
private static final String STATE_SITE_ID = PREFIX + REQUEST + "site_id";

/** The sort ascending or decending */
private static final String STATE_SORT_ASC = PREFIX + REQUEST + "sort_asc";

Expand Down Expand Up @@ -869,7 +872,6 @@ public void setParent(String parent)

/** the interval (in days) the the soft-deleted content will be automatically permanently removed **/
public static final String STATE_CLEANUP_DELETED_CONTENT_INTERVAL= "state_cleanup_deleted_content_interval";

// may need to distinguish permission on entity vs permission on its containing collection
static
{
Expand Down Expand Up @@ -8508,6 +8510,19 @@ else if(RESOURCES_MODE_DROPBOX.equals(resources_mode))
state.setAttribute(STATE_SITE_TITLE, title);
}

if (state.getAttribute(STATE_SITE_ID) == null)
{
String id = "";
try
{
id = ((Site) siteService.getSite(toolManager.getCurrentPlacement().getContext())).getId();
}
catch (IdUnusedException e)
{ // ignore
}
state.setAttribute(STATE_SITE_ID, id);
}

getExpandedCollections(state).clear();
state.setAttribute(STATE_EXPANDED_FOLDER_SORT_MAP, new HashMap());

Expand Down Expand Up @@ -10377,6 +10392,8 @@ public void doZipDownloadconfirm(RunData data)
protected void zipDownloadItems(SessionState state, Set<String> zipDownloadIdSet)
{
List<ListItem> zipDownloadItems = new ArrayList<>();
// Set to hold the names of files that exceed that maximum size for zipping.
Set<String> zipSingleFileSizeExceeded = new HashSet<>();

long zipMaxIndividualFileSize = Long.parseLong(ServerConfigurationService.getString("content.zip.download.maxindividualfilesize","0"));
long zipMaxTotalSize = Long.parseLong(ServerConfigurationService.getString("content.zip.download.maxtotalsize","0"));
Expand All @@ -10385,70 +10402,57 @@ protected void zipDownloadItems(SessionState state, Set<String> zipDownloadIdSet

ContentHostingService contentService = contentHostingService;

for(String showId : zipDownloadIdSet)
{
for (String showId : zipDownloadIdSet) {
ContentEntity entity = null;
try
{
if(contentService.isCollection(showId))
{
try {
if (contentService.isCollection(showId)) {
if (contentService.allowGetCollection(showId)) {
entity = contentService.getCollection(showId);
currentEntitySize = getCollectionRecursiveSize((ContentCollection) entity, zipMaxIndividualFileSize, zipMaxTotalSize);
currentEntitySize = getCollectionRecursiveSize((ContentCollection) entity, zipMaxIndividualFileSize, zipMaxTotalSize, zipSingleFileSizeExceeded);
}
}
else if(contentService.allowGetResource(showId))
{
} else if (contentService.allowGetResource(showId)) {
entity = contentService.getResource(showId);
currentEntitySize = ((ContentResource)entity).getContentLength();
currentEntitySize = ((ContentResource) entity).getContentLength();
if (currentEntitySize > zipMaxIndividualFileSize) {
// Work out the file path without the site ID.
String filePath = entity.getId().replace("/group/" + toolManager.getCurrentPlacement().getContext(), "");
zipSingleFileSizeExceeded.add(filePath);
}
}

accumulatedSize = accumulatedSize + currentEntitySize;

if (accumulatedSize > zipMaxTotalSize)
{
if (accumulatedSize > zipMaxTotalSize) {
throw new ZipMaxTotalSizeException();
}

ListItem item = new ListItem(entity);
if(item.isCollection() && contentService.allowGetCollection(showId))
{
if (item.isCollection() && contentService.allowGetCollection(showId)) {
item.setSize(ResourcesAction.getFileSizeString(currentEntitySize, rb));
zipDownloadItems.add(item);
}
else if(!item.isCollection() && contentService.allowGetResource(showId))
{
} else if (!item.isCollection() && contentService.allowGetResource(showId)) {
zipDownloadItems.add(item);
}
}
catch (ZipMaxSingleFileSizeException sfe)
{
for (String maxSingleFileSize: sfe.getResourceIds()) {
addAlert(state, trb.getFormattedMessage("zipdownload.maxIndividualSizeInFolder", maxSingleFileSize, getFileSizeString(zipMaxIndividualFileSize, rb)));
}
state.setAttribute(STATE_MODE, MODE_LIST);
}
catch (ZipMaxTotalSizeException tse)
{
} catch (ZipMaxTotalSizeException tse) {
addAlert(state, trb.getFormattedMessage("zipdownload.maxTotalSize", getFileSizeString(zipMaxTotalSize, rb)));
state.setAttribute(STATE_MODE, MODE_LIST);
// abort loop so alert not repeated.
break;
}
catch (IdUnusedException ide)
{
} catch (IdUnusedException ide) {
log.warn("IdUnusedException", ide);
}
catch (TypeException te)
{
} catch (TypeException te) {
log.warn("TypeException", te);
}
catch (PermissionException pe)
{
} catch (PermissionException pe) {
log.warn("PermissionException", pe);
}
}

if (!zipSingleFileSizeExceeded.isEmpty()) {
// We want to alert about all files that exceed the individual zip size so need to process the whole zipDownloadIdSet before alerting.
for (String zipSingleFile : zipSingleFileSizeExceeded) {
addAlert(state, trb.getFormattedMessage("zipdownload.maxIndividualSizeInFolder", zipSingleFile, getFileSizeString(zipMaxIndividualFileSize, rb)));
}
state.setAttribute(STATE_MODE, MODE_LIST);
}
state.setAttribute (STATE_ZIPDOWNLOAD_SET, zipDownloadItems);
}

Expand Down Expand Up @@ -10492,114 +10496,16 @@ public void doFinalizeZipDownload(RunData data)
}
}

// Use the site title for the zip name, replace spaces with hyphens though.
// Use the site title for the zip name, remove spaces though.
String siteTitle = (String) state.getAttribute(STATE_SITE_TITLE);
siteTitle = siteTitle.replace(' ', '-');
new ZipContentUtil().compressSelectedResources(siteTitle, selectedFolderIds, selectedFiles, response);
}

protected void compressResource(ZipOutputStream zipOut, String collectionId, String rootFolderName, String resourceId) throws Exception
{
if (contentHostingService.isCollection(resourceId))
{
try
{
ContentCollection collection = contentHostingService.getCollection(resourceId);
List<String> children = collection.getMembers();
if(children != null)
{
for(int i = children.size() - 1; i >= 0; i--)
{
String child = children.get(i);
compressResource(zipOut,collectionId,rootFolderName,child);
}
}
}
catch (PermissionException e)
{
//Ignore
}
}
else
{
try
{
ContentResource resource = contentHostingService.getResource(resourceId);
String displayName = isolateName(resource.getId());
displayName = escapeInvalidCharsEntry(displayName);

InputStream content = resource.streamContent();
byte data[] = new byte[1024 * 10];
BufferedInputStream bContent = null;

try
{
bContent = new BufferedInputStream(content, data.length);

String entryName = (resource.getContainingCollection().getId() + displayName);
entryName=entryName.replace(collectionId,rootFolderName+"/");
entryName = escapeInvalidCharsEntry(entryName);

ZipEntry resourceEntry = new ZipEntry(entryName);
zipOut.putNextEntry(resourceEntry); //A duplicate entry throw ZipException here.
int bCount;
while ((bCount = bContent.read(data, 0, data.length)) != -1)
{
zipOut.write(data, 0, bCount);
}

try
{
zipOut.closeEntry();
}
catch (IOException ioException)
{
log.error("IOException when closing zip file entry",ioException);
}
}
catch (IllegalArgumentException iException)
{
log.error("IllegalArgumentException while creating zip file",iException);
}
catch (java.util.zip.ZipException e)
{
//Duplicate entry: ignore and continue.
try
{
zipOut.closeEntry();
}
catch (IOException ioException)
{
log.error("IOException when closing zip file entry",ioException);
}
}
finally
{
if (bContent != null)
{
try
{
bContent.close();
}
catch (IOException ioException)
{
log.error("IOException when closing zip file",ioException);
}
}
}
}
catch (PermissionException e)
{
//Ignore
}
}
siteTitle = siteTitle.replace(" ", "");
new ZipContentUtil().compressSelectedResources((String)state.getAttribute(STATE_SITE_ID), siteTitle, selectedFolderIds, selectedFiles, response);
}

private long getCollectionRecursiveSize(ContentCollection currentCollection, long maxIndividualFileSize, long zipMaxTotalSize) throws ZipMaxSingleFileSizeException, ZipMaxTotalSizeException
private long getCollectionRecursiveSize(ContentCollection currentCollection, long maxIndividualFileSize, long zipMaxTotalSize, Set<String> zipSingleFileSizeExceeded) throws ZipMaxTotalSizeException
{
long total=0;
Set<String> maxSingleFileSizeSet = new HashSet<>();


List items = currentCollection.getMemberResources();
Iterator it = items.iterator();
while(it.hasNext())
Expand All @@ -10611,35 +10517,22 @@ private long getCollectionRecursiveSize(ContentCollection currentCollection, lon
if (tempSize > maxIndividualFileSize) {
// Work out the file path without the site ID.
String filePath = myElement.getId().replace("/group/" + toolManager.getCurrentPlacement().getContext(), "");
maxSingleFileSizeSet.add(filePath);

zipSingleFileSizeExceeded.add(filePath);
}
else {total=total+tempSize;}
}
else if (myElement.isCollection())
{
long tempSize = getCollectionRecursiveSize((ContentCollection)myElement, maxIndividualFileSize, zipMaxTotalSize);
long tempSize = getCollectionRecursiveSize((ContentCollection)myElement, maxIndividualFileSize, zipMaxTotalSize, zipSingleFileSizeExceeded);

if (tempSize > zipMaxTotalSize) {
throw new ZipMaxTotalSizeException();
}
else {total=total+tempSize;}
}
}
if (!maxSingleFileSizeSet.isEmpty()) {
throw new ZipMaxSingleFileSizeException(maxSingleFileSizeSet);
}
return total;
}

private String removeRootCollectionId(String resource)
{
for (int i=0;i<3;i++) {resource=resource.substring(resource.indexOf('/')+1,resource.length());}
return resource;
}

private String escapeInvalidCharsEntry(String accentedString)
{
String decomposed = Normalizer.normalize(accentedString, Normalizer.Form.NFD);
String cleanString = decomposed.replaceAll( "\\p{InCombiningDiacriticalMarks}+", "");
return cleanString;
}
} // ResourcesAction
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- sakai_resources_zipDownloadFinish.vm, version: $Revision: $, use with org.sakaiproject.tool.content.ResourcesAction.java -->
<script src="/library/js/spinner.js" type="text/javascript"></script>
<div class="portletBody">
<div class="instruction">$tlang.getString("zipdownload.sure")</div><div style="display:block;clear:both" ></div>
<div class="instruction">$tlang.getString("zipdownload.sure")</div>
#if ($alertMessage)<div class="alertMessage">$tlang.getString("label.alert") $validator.escapeHtml($alertMessage)</div><div style="display:block;clear:both" ></div>#end

<form action="#toolForm("ResourcesAction")" id="zipDownloadFileForm" name="zipDownloadFileForm" method="post" >
Expand Down
Loading

0 comments on commit b91cec4

Please sign in to comment.