diff --git a/src/main/java/Bundle.properties b/src/main/java/Bundle.properties index d9b8ce2ecc2..51c80a47c35 100755 --- a/src/main/java/Bundle.properties +++ b/src/main/java/Bundle.properties @@ -1490,6 +1490,7 @@ file.lastupdated.label=Last Updated file.metadataTab.fileMetadata.header=File Metadata file.metadataTab.fileMetadata.persistentid.label=Data File Persistent ID +file.metadataTab.fileMetadata.downloadUrl.label=Download URL file.metadataTab.fileMetadata.unf.label=UNF file.metadataTab.fileMetadata.size.label=Size file.metadataTab.fileMetadata.type.label=Type diff --git a/src/main/java/edu/harvard/iq/dataverse/Dataset.java b/src/main/java/edu/harvard/iq/dataverse/Dataset.java index 5b0dc443942..678ecd6f2fb 100644 --- a/src/main/java/edu/harvard/iq/dataverse/Dataset.java +++ b/src/main/java/edu/harvard/iq/dataverse/Dataset.java @@ -328,10 +328,14 @@ public DatasetVersion getEditVersion(Template template) { } } - public DatasetVersion getCreateVersion() { + /** + * @todo Investigate if this method should be deprecated in favor of + * createNewDatasetVersion. + */ + public DatasetVersion getCreateVersion() { DatasetVersion dsv = new DatasetVersion(); dsv.setVersionState(DatasetVersion.VersionState.DRAFT); - dsv.setDataset(this); + dsv.setDataset(this); dsv.initDefaultValues(); this.setVersions(new ArrayList()); getVersions().add(0, dsv); diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java b/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java index 4662c797a1c..12482c54b2a 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java @@ -3311,7 +3311,7 @@ public boolean isFileAccessRequestMultiSignUpButtonEnabled(){ } public boolean isDownloadPopupRequired() { - return fileDownloadService.isDownloadPopupRequired(workingVersion); + return FileUtil.isDownloadPopupRequired(workingVersion); } public String requestAccessMultipleFiles(String fileIdString) { diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java b/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java index 437c42ba8b3..8e091420428 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java @@ -131,6 +131,11 @@ public void setVersion(Long version) { @Column(length = VERSION_NOTE_MAX_LENGTH) private String versionNote; + /** + * @todo versionState should never be null so when we are ready, uncomment + * the `nullable = false` below. + */ +// @Column(nullable = false) @Enumerated(EnumType.STRING) private VersionState versionState; diff --git a/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java index c78445d61aa..2de075eb835 100644 --- a/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/FileDownloadServiceBean.java @@ -120,42 +120,15 @@ public void callDownloadServlet(String multiFileString, Boolean gbRecordsWritten //return fileDownloadUrl; } - - //private String callDownloadServlet( String downloadType, Long fileId){ - public void callDownloadServlet( String downloadType, Long fileId, Boolean gbRecordsWritten){ - - String fileDownloadUrl = "/api/access/datafile/" + fileId; - - if (downloadType != null && downloadType.equals("bundle")){ - fileDownloadUrl = "/api/access/datafile/bundle/" + fileId; - } - if (downloadType != null && downloadType.equals("original")){ - fileDownloadUrl = "/api/access/datafile/" + fileId + "?format=original"; - } - if (downloadType != null && downloadType.equals("RData")){ - fileDownloadUrl = "/api/access/datafile/" + fileId + "?format=RData"; - } - if (downloadType != null && downloadType.equals("var")){ - fileDownloadUrl = "/api/meta/datafile/" + fileId; - } - if (downloadType != null && downloadType.equals("tab")){ - fileDownloadUrl = "/api/access/datafile/" + fileId+ "?format=tab"; - } - if (gbRecordsWritten){ - if(downloadType != null && ( downloadType.equals("original") || downloadType.equals("RData") || downloadType.equals("tab")) ){ - fileDownloadUrl += "&gbrecs=true"; - } else { - fileDownloadUrl += "?gbrecs=true"; - } - - } - logger.fine("Returning file download url: " + fileDownloadUrl); + + public void callDownloadServlet(String downloadType, Long fileId, boolean gbRecordsWritten) { + String fileDownloadUrl = FileUtil.getFileDownloadUrlPath(downloadType, fileId, gbRecordsWritten); + logger.fine("Redirecting to file download url: " + fileDownloadUrl); try { FacesContext.getCurrentInstance().getExternalContext().redirect(fileDownloadUrl); } catch (IOException ex) { - logger.info("Failed to issue a redirect to file download url."); + logger.info("Failed to issue a redirect to file download url (" + fileDownloadUrl + "): " + ex); } - //return fileDownloadUrl; } //public String startFileDownload(FileMetadata fileMetadata, String format) { @@ -229,39 +202,7 @@ public String startWorldMapDownloadLink(GuestbookResponse guestbookResponse, Fil } return retVal; } - - public boolean isDownloadPopupRequired(DatasetVersion datasetVersion) { - // Each of these conditions is sufficient reason to have to - // present the user with the popup: - if (datasetVersion == null){ - return false; - } - //0. if version is draft then Popup "not required" - if (!datasetVersion.isReleased()){ - return false; - } - // 1. License and Terms of Use: - if (datasetVersion.getTermsOfUseAndAccess() != null) { - if (!TermsOfUseAndAccess.License.CC0.equals(datasetVersion.getTermsOfUseAndAccess().getLicense()) - && !(datasetVersion.getTermsOfUseAndAccess().getTermsOfUse() == null - || datasetVersion.getTermsOfUseAndAccess().getTermsOfUse().equals(""))) { - return true; - } - - // 2. Terms of Access: - if (!(datasetVersion.getTermsOfUseAndAccess().getTermsOfAccess() == null) && !datasetVersion.getTermsOfUseAndAccess().getTermsOfAccess().equals("")) { - return true; - } - } - - // 3. Guest Book: - if (datasetVersion.getDataset().getGuestbook() != null && datasetVersion.getDataset().getGuestbook().isEnabled() && datasetVersion.getDataset().getGuestbook().getDataverse() != null ) { - return true; - } - return false; - } - public Boolean canSeeTwoRavensExploreButton(){ return false; } diff --git a/src/main/java/edu/harvard/iq/dataverse/FilePage.java b/src/main/java/edu/harvard/iq/dataverse/FilePage.java index 9225805eba1..2850ec2f603 100644 --- a/src/main/java/edu/harvard/iq/dataverse/FilePage.java +++ b/src/main/java/edu/harvard/iq/dataverse/FilePage.java @@ -19,6 +19,7 @@ import edu.harvard.iq.dataverse.export.ExportService; import edu.harvard.iq.dataverse.export.spi.Exporter; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; +import edu.harvard.iq.dataverse.util.FileUtil; import edu.harvard.iq.dataverse.util.JsfHelper; import static edu.harvard.iq.dataverse.util.JsfHelper.JH; import edu.harvard.iq.dataverse.util.SystemConfig; @@ -91,6 +92,8 @@ public class FilePage implements java.io.Serializable { TwoRavensHelper twoRavensHelper; @Inject WorldMapPermissionHelper worldMapPermissionHelper; + private static final Logger logger = Logger.getLogger(FilePage.class.getCanonicalName()); + public String init() { @@ -157,7 +160,7 @@ public boolean isDownloadPopupRequired() { if(fileMetadata.getId() == null || fileMetadata.getDatasetVersion().getId() == null ){ return false; } - return fileDownloadService.isDownloadPopupRequired(fileMetadata.getDatasetVersion()); + return FileUtil.isDownloadPopupRequired(fileMetadata.getDatasetVersion()); } @@ -448,5 +451,13 @@ public boolean isReplacementFile(){ return this.datafileService.isReplacementFile(this.getFile()); } - + + public boolean isPubliclyDownloadable() { + return FileUtil.isPubliclyDownloadable(fileMetadata); + } + + public String getPublicDownloadUrl() { + return FileUtil.getPublicDownloadUrl(systemConfig.getDataverseSiteUrl(), fileId); + } + } diff --git a/src/main/java/edu/harvard/iq/dataverse/util/FileUtil.java b/src/main/java/edu/harvard/iq/dataverse/util/FileUtil.java index 73e7ef8c715..f3a9825f72e 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/FileUtil.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/FileUtil.java @@ -20,18 +20,16 @@ package edu.harvard.iq.dataverse.util; -import edu.emory.mathcs.backport.java.util.Collections; import edu.harvard.iq.dataverse.DataFile; import edu.harvard.iq.dataverse.DataFile.ChecksumType; import edu.harvard.iq.dataverse.DatasetVersion; import edu.harvard.iq.dataverse.FileMetadata; +import edu.harvard.iq.dataverse.TermsOfUseAndAccess; import edu.harvard.iq.dataverse.datasetutility.FileExceedsMaxSizeException; import edu.harvard.iq.dataverse.ingest.IngestReport; -import edu.harvard.iq.dataverse.ingest.IngestUtil; import edu.harvard.iq.dataverse.ingest.IngestServiceShapefileHelper; import edu.harvard.iq.dataverse.ingest.IngestableDataChecker; import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -56,11 +54,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -1182,4 +1177,128 @@ public static String getCiteDataFileFilename(FileMetadata fileMetadata, FileCita } } + /** + * @todo Consider returning not only the boolean but the human readable + * reason why the popup is required, which could be used in the GUI to + * elaborate on the text "This file cannot be downloaded publicly." + */ + public static boolean isDownloadPopupRequired(DatasetVersion datasetVersion) { + // Each of these conditions is sufficient reason to have to + // present the user with the popup: + if (datasetVersion == null) { + logger.fine("Download popup required because datasetVersion is null."); + return false; + } + //0. if version is draft then Popup "not required" + if (!datasetVersion.isReleased()) { + logger.fine("Download popup required because datasetVersion has not been released."); + return false; + } + // 1. License and Terms of Use: + if (datasetVersion.getTermsOfUseAndAccess() != null) { + if (!TermsOfUseAndAccess.License.CC0.equals(datasetVersion.getTermsOfUseAndAccess().getLicense()) + && !(datasetVersion.getTermsOfUseAndAccess().getTermsOfUse() == null + || datasetVersion.getTermsOfUseAndAccess().getTermsOfUse().equals(""))) { + logger.fine("Download popup required because of license or terms of use."); + return true; + } + + // 2. Terms of Access: + if (!(datasetVersion.getTermsOfUseAndAccess().getTermsOfAccess() == null) && !datasetVersion.getTermsOfUseAndAccess().getTermsOfAccess().equals("")) { + logger.fine("Download popup required because of terms of access."); + return true; + } + } + + // 3. Guest Book: + if (datasetVersion.getDataset() != null && datasetVersion.getDataset().getGuestbook() != null && datasetVersion.getDataset().getGuestbook().isEnabled() && datasetVersion.getDataset().getGuestbook().getDataverse() != null) { + logger.fine("Download popup required because of guestbook."); + return true; + } + + logger.fine("Download popup is not required."); + return false; + } + + /** + * Provide download URL if no Terms of Use, no guestbook, and not + * restricted. + */ + public static boolean isPubliclyDownloadable(FileMetadata fileMetadata) { + if (fileMetadata == null) { + return false; + } + if (fileMetadata.isRestricted()) { + String msg = "Not publicly downloadable because the file is restricted."; + logger.fine(msg); + return false; + } + boolean popupReasons = isDownloadPopupRequired(fileMetadata.getDatasetVersion()); + if (popupReasons == true) { + /** + * @todo The user clicking publish may have a bad "Dude, where did + * the file Download URL go" experience in the following scenario: + * + * - The user creates a dataset and uploads a file. + * + * - The user sets Terms of Use, which means a Download URL should + * not be displayed. + * + * - While the dataset is in draft, the Download URL is displayed + * due to the rule "Download popup required because datasetVersion + * has not been released." + * + * - Once the dataset is published the Download URL disappears due + * to the rule "Download popup required because of license or terms + * of use." + * + * In short, the Download URL disappears on publish in the scenario + * above, which is weird. We should probably attempt to see into the + * future to when the dataset is published to see if the file will + * be publicly downloadable or not. + */ + return false; + } + return true; + } + + public static String getFileDownloadUrlPath(String downloadType, Long fileId, boolean gbRecordsWritten) { + String fileDownloadUrl = "/api/access/datafile/" + fileId; + if (downloadType != null && downloadType.equals("bundle")) { + fileDownloadUrl = "/api/access/datafile/bundle/" + fileId; + } + if (downloadType != null && downloadType.equals("original")) { + fileDownloadUrl = "/api/access/datafile/" + fileId + "?format=original"; + } + if (downloadType != null && downloadType.equals("RData")) { + fileDownloadUrl = "/api/access/datafile/" + fileId + "?format=RData"; + } + if (downloadType != null && downloadType.equals("var")) { + fileDownloadUrl = "/api/meta/datafile/" + fileId; + } + if (downloadType != null && downloadType.equals("tab")) { + fileDownloadUrl = "/api/access/datafile/" + fileId + "?format=tab"; + } + if (gbRecordsWritten) { + if (downloadType != null && (downloadType.equals("original") || downloadType.equals("RData") || downloadType.equals("tab"))) { + fileDownloadUrl += "&gbrecs=true"; + } else { + fileDownloadUrl += "?gbrecs=true"; + } + } + logger.fine("Returning file download url: " + fileDownloadUrl); + return fileDownloadUrl; + } + + public static String getPublicDownloadUrl(String dataverseSiteUrl, Long fileId) { + if (fileId == null) { + logger.info("In getPublicDownloadUrl but fileId is null!"); + return null; + } + String downloadType = null; + boolean gbRecordsWritten = false; + String path = getFileDownloadUrlPath(downloadType, fileId, gbRecordsWritten); + return dataverseSiteUrl + path; + } + } diff --git a/src/main/webapp/file.xhtml b/src/main/webapp/file.xhtml index a462b10e843..88ac46537e0 100644 --- a/src/main/webapp/file.xhtml +++ b/src/main/webapp/file.xhtml @@ -222,11 +222,7 @@ - - - -
@@ -236,14 +232,6 @@
- - - -
- This is a Replacement File -
-
@@ -306,6 +294,16 @@
+
+ +
+ + + +
+
- -
- -
- -
- - diff --git a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java index 75ea64f17da..c21e05c9cf0 100644 --- a/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java +++ b/src/test/java/edu/harvard/iq/dataverse/api/FilesIT.java @@ -469,7 +469,7 @@ public void test_006_ReplaceFileGoodTabular() throws InterruptedException { .statusCode(OK.getStatusCode()); // give file time to ingest - sleep(1000); + sleep(10000); Response publishDatasetResp = UtilIT.publishDatasetViaNativeApi(datasetId, "major", apiToken); publishDatasetResp.prettyPrint(); diff --git a/src/test/java/edu/harvard/iq/dataverse/util/FileUtilTest.java b/src/test/java/edu/harvard/iq/dataverse/util/FileUtilTest.java index 0884b812290..d980c47d5a8 100644 --- a/src/test/java/edu/harvard/iq/dataverse/util/FileUtilTest.java +++ b/src/test/java/edu/harvard/iq/dataverse/util/FileUtilTest.java @@ -1,6 +1,11 @@ package edu.harvard.iq.dataverse.util; +import edu.harvard.iq.dataverse.Dataset; +import edu.harvard.iq.dataverse.DatasetVersion; +import edu.harvard.iq.dataverse.Dataverse; import edu.harvard.iq.dataverse.FileMetadata; +import edu.harvard.iq.dataverse.Guestbook; +import edu.harvard.iq.dataverse.TermsOfUseAndAccess; import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -25,4 +30,144 @@ public void testGetCiteDataFileFilename() { assertEquals("50by1000.bib", FileUtil.getCiteDataFileFilename(tabular, FileUtil.FileCitationExtension.BIBTEX)); } + @Test + public void testIsDownloadPopupRequiredNull() { + assertEquals(false, FileUtil.isDownloadPopupRequired(null)); + } + + @Test + public void testIsDownloadPopupRequiredDraft() { + Dataset dataset = new Dataset(); + DatasetVersion dsv1 = dataset.getEditVersion(); + assertEquals(DatasetVersion.VersionState.DRAFT, dsv1.getVersionState()); + assertEquals(false, FileUtil.isDownloadPopupRequired(dsv1)); + } + + @Test + public void testIsDownloadPopupRequiredLicenseCC0() { + DatasetVersion dsv1 = new DatasetVersion(); + dsv1.setVersionState(DatasetVersion.VersionState.RELEASED); + TermsOfUseAndAccess termsOfUseAndAccess = new TermsOfUseAndAccess(); + termsOfUseAndAccess.setLicense(TermsOfUseAndAccess.License.CC0); + dsv1.setTermsOfUseAndAccess(termsOfUseAndAccess); + assertEquals(false, FileUtil.isDownloadPopupRequired(dsv1)); + } + + @Test + public void testIsDownloadPopupRequiredHasTermsOfUseAndCc0License() { + DatasetVersion dsv1 = new DatasetVersion(); + dsv1.setVersionState(DatasetVersion.VersionState.RELEASED); + TermsOfUseAndAccess termsOfUseAndAccess = new TermsOfUseAndAccess(); + /** + * @todo Ask if setting the license to CC0 should be enough to not show + * the popup when the are Terms of Use. This feels like a bug since the + * Terms of Use should probably be shown. + */ + termsOfUseAndAccess.setLicense(TermsOfUseAndAccess.License.CC0); + termsOfUseAndAccess.setTermsOfUse("be excellent to each other"); + dsv1.setTermsOfUseAndAccess(termsOfUseAndAccess); + assertEquals(false, FileUtil.isDownloadPopupRequired(dsv1)); + } + + @Test + public void testIsDownloadPopupRequiredHasTermsOfUseAndNoneLicense() { + DatasetVersion dsv1 = new DatasetVersion(); + dsv1.setVersionState(DatasetVersion.VersionState.RELEASED); + TermsOfUseAndAccess termsOfUseAndAccess = new TermsOfUseAndAccess(); + termsOfUseAndAccess.setLicense(TermsOfUseAndAccess.License.NONE); + termsOfUseAndAccess.setTermsOfUse("be excellent to each other"); + dsv1.setTermsOfUseAndAccess(termsOfUseAndAccess); + assertEquals(true, FileUtil.isDownloadPopupRequired(dsv1)); + } + + @Test + public void testIsDownloadPopupRequiredHasTermsOfAccess() { + DatasetVersion dsv1 = new DatasetVersion(); + dsv1.setVersionState(DatasetVersion.VersionState.RELEASED); + TermsOfUseAndAccess termsOfUseAndAccess = new TermsOfUseAndAccess(); + termsOfUseAndAccess.setTermsOfAccess("Terms of *Access* is different than Terms of Use"); + dsv1.setTermsOfUseAndAccess(termsOfUseAndAccess); + assertEquals(true, FileUtil.isDownloadPopupRequired(dsv1)); + } + + @Test + public void testIsDownloadPopupRequiredHasGuestBook() { + DatasetVersion datasetVersion = new DatasetVersion(); + datasetVersion.setVersionState(DatasetVersion.VersionState.RELEASED); + Dataset dataset = new Dataset(); + datasetVersion.setDataset(dataset); + Guestbook guestbook = new Guestbook(); + guestbook.setEnabled(true); + dataset.setGuestbook(guestbook); + Dataverse dataverse = new Dataverse(); + guestbook.setDataverse(dataverse); + assertEquals(true, FileUtil.isDownloadPopupRequired(datasetVersion)); + } + + @Test + public void testIsPubliclyDownloadable() { + assertEquals(false, FileUtil.isPubliclyDownloadable(null)); + + FileMetadata restrictedFileMetadata = new FileMetadata(); + restrictedFileMetadata.setRestricted(true); + assertEquals(false, FileUtil.isPubliclyDownloadable(restrictedFileMetadata)); + + FileMetadata nonRestrictedFileMetadata = new FileMetadata(); + DatasetVersion dsv = new DatasetVersion(); + dsv.setVersionState(DatasetVersion.VersionState.RELEASED); + nonRestrictedFileMetadata.setDatasetVersion(dsv); + Dataset dataset = new Dataset(); + dsv.setDataset(dataset); + nonRestrictedFileMetadata.setRestricted(false); + assertEquals(true, FileUtil.isPubliclyDownloadable(nonRestrictedFileMetadata)); + } + + @Test + public void testIsPubliclyDownloadable2() { + + FileMetadata nonRestrictedFileMetadata = new FileMetadata(); + DatasetVersion dsv = new DatasetVersion(); + TermsOfUseAndAccess termsOfUseAndAccess = new TermsOfUseAndAccess(); + termsOfUseAndAccess.setTermsOfUse("be excellent to each other"); + dsv.setTermsOfUseAndAccess(termsOfUseAndAccess); + dsv.setVersionState(DatasetVersion.VersionState.RELEASED); + nonRestrictedFileMetadata.setDatasetVersion(dsv); + Dataset dataset = new Dataset(); + dsv.setDataset(dataset); + nonRestrictedFileMetadata.setRestricted(false); + assertEquals(false, FileUtil.isPubliclyDownloadable(nonRestrictedFileMetadata)); + } + + @Test + public void testgetFileDownloadUrl() { + Long fileId = 42l; + assertEquals("/api/access/datafile/42", FileUtil.getFileDownloadUrlPath(null, fileId, false)); + assertEquals("/api/access/datafile/42", FileUtil.getFileDownloadUrlPath("", fileId, false)); + assertEquals("/api/access/datafile/bundle/42", FileUtil.getFileDownloadUrlPath("bundle", fileId, false)); + assertEquals("/api/access/datafile/42?format=original", FileUtil.getFileDownloadUrlPath("original", fileId, false)); + assertEquals("/api/access/datafile/42?format=RData", FileUtil.getFileDownloadUrlPath("RData", fileId, false)); + assertEquals("/api/meta/datafile/42", FileUtil.getFileDownloadUrlPath("var", fileId, false)); + assertEquals("/api/access/datafile/42?format=tab", FileUtil.getFileDownloadUrlPath("tab", fileId, false)); + assertEquals("/api/access/datafile/42?format=tab&gbrecs=true", FileUtil.getFileDownloadUrlPath("tab", fileId, true)); + assertEquals("/api/access/datafile/42?gbrecs=true", FileUtil.getFileDownloadUrlPath(null, fileId, true)); + } + + @Test + public void testGetPublicDownloadUrl() { + assertEquals(null, FileUtil.getPublicDownloadUrl(null, null)); + assertEquals("https://demo.dataverse.org/api/access/datafile/42", FileUtil.getPublicDownloadUrl("https://demo.dataverse.org", 42l)); + } + + @Test + public void testGenerateOriginalExtension() { + assertEquals("", FileUtil.generateOriginalExtension("foo")); + // uh-oh, NullPointerException +// assertEquals("", FileUtil.generateOriginalExtension(null)); + assertEquals(".sav", FileUtil.generateOriginalExtension("application/x-spss-sav")); + assertEquals(".por", FileUtil.generateOriginalExtension("application/x-spss-por")); + assertEquals(".dta", FileUtil.generateOriginalExtension("application/x-stata")); + assertEquals(".RData", FileUtil.generateOriginalExtension("application/x-rlang-transport")); + assertEquals(".csv", FileUtil.generateOriginalExtension("text/csv")); + assertEquals(".xlsx", FileUtil.generateOriginalExtension("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")); + } }