• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IQSS / dataverse / #23051

03 Sep 2024 05:54PM UTC coverage: 20.761% (+0.002%) from 20.759%
#23051

Pull #10820

github

web-flow
Merge 55f43e0d3 into 025b55d3a
Pull Request #10820: A Fix for the Dataset Thumbnail Bug on Publish, 10819

3 of 38 new or added lines in 4 files covered. (7.89%)

1 existing line in 1 file now uncovered.

17527 of 84424 relevant lines covered (20.76%)

0.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

24.66
/src/main/java/edu/harvard/iq/dataverse/dataset/DatasetUtil.java
1
package edu.harvard.iq.dataverse.dataset;
2

3
import edu.harvard.iq.dataverse.*;
4
import edu.harvard.iq.dataverse.authorization.groups.impl.ipaddress.ip.IpAddress;
5
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
6
import edu.harvard.iq.dataverse.dataaccess.DataAccess;
7

8
import static edu.harvard.iq.dataverse.api.ApiConstants.DS_VERSION_DRAFT;
9
import static edu.harvard.iq.dataverse.dataaccess.DataAccess.getStorageIO;
10

11
import edu.harvard.iq.dataverse.dataaccess.InputStreamIO;
12
import edu.harvard.iq.dataverse.dataaccess.StorageIO;
13
import edu.harvard.iq.dataverse.dataaccess.ImageThumbConverter;
14
import edu.harvard.iq.dataverse.util.BundleUtil;
15
import edu.harvard.iq.dataverse.util.FileUtil;
16
import java.awt.image.BufferedImage;
17
import java.io.ByteArrayInputStream;
18
import java.io.File;
19
import java.io.FileInputStream;
20
import java.io.FileOutputStream;
21
import java.io.IOException;
22
import java.io.InputStream;
23
import java.nio.channels.FileChannel;
24
import java.nio.charset.StandardCharsets;
25
import java.nio.file.Files;
26
import java.nio.file.Paths;
27
import java.util.*;
28
import java.util.logging.Logger;
29
import javax.imageio.ImageIO;
30

31
import jakarta.enterprise.inject.spi.CDI;
32
import org.apache.commons.io.IOUtils;
33
import edu.harvard.iq.dataverse.datasetutility.FileSizeChecker;
34
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
35
import edu.harvard.iq.dataverse.license.License;
36
import edu.harvard.iq.dataverse.util.StringUtil;
37
import static edu.harvard.iq.dataverse.util.json.JsonPrinter.json;
38

39
import org.apache.commons.io.FileUtils;
40
import org.apache.commons.lang3.EnumUtils;
41

42
public class DatasetUtil {
×
43

44
    private static final Logger logger = Logger.getLogger(DatasetUtil.class.getCanonicalName());
1✔
45
    public static final String datasetDefaultSummaryFieldNames = "dsDescription,subject,keyword,publication,notesText";
46
    public static String datasetLogoFilenameFinal = "dataset_logo_original";
1✔
47
    public static String datasetLogoThumbnail = "dataset_logo";
1✔
48
    public static String thumbExtension = ".thumb";
1✔
49

50
    public static List<DatasetThumbnail> getThumbnailCandidates(Dataset dataset, boolean considerDatasetLogoAsCandidate, int size) {
51
        List<DatasetThumbnail> thumbnails = new ArrayList<>();
1✔
52
        if (dataset == null) {
1✔
53
            return thumbnails;
1✔
54
        }
55
        if (considerDatasetLogoAsCandidate) {
1✔
56
//            Path path = Paths.get(dataset.getFileSystemDirectory() + File.separator + datasetLogoThumbnail + thumb48addedByImageThumbConverter);
57
//            if (Files.exists(path)) {
58
//                logger.fine("Thumbnail created from dataset logo exists!");
59
//                File file = path.toFile();
60
//                try {
61
//                    byte[] bytes = Files.readAllBytes(file.toPath());
62
            StorageIO<Dataset> dataAccess = null;
×
63

64
            try{
65
                dataAccess = DataAccess.getStorageIO(dataset);
×
66
            }
67
            catch(IOException ioex){
×
68
            }
×
69

70
            InputStream in = null;
×
71
            try {
72
                    in = dataAccess.getAuxFileAsInputStream(datasetLogoThumbnail + thumbExtension + size);
×
73
            } catch (Exception ioex) {
×
74
            }
×
75

76
            if (in != null) {
×
77
                logger.fine("Thumbnail created from dataset logo exists!");
×
78
                try {
79
                    byte[] bytes = IOUtils.toByteArray(in);
×
80
                    String base64image = Base64.getEncoder().encodeToString(bytes);
×
81
                    DatasetThumbnail datasetThumbnail = new DatasetThumbnail(FileUtil.DATA_URI_SCHEME + base64image, null);
×
82
                    thumbnails.add(datasetThumbnail);
×
83
                } catch (IOException ex) {
×
84
                    logger.warning("Unable to rescale image: " + ex);
×
85
                }
×
86
            } else {
87
                logger.fine("There is no thumbnail created from a dataset logo");
×
88
            }
89
            IOUtils.closeQuietly(in);
×
90
        }
91
        for (FileMetadata fileMetadata : dataset.getLatestVersion().getFileMetadatas()) {
1✔
92
            DataFile dataFile = fileMetadata.getDataFile();
1✔
93

94
            if (dataFile != null && FileUtil.isThumbnailSupported(dataFile)
1✔
95
                    && ImageThumbConverter.isThumbnailAvailable(dataFile)
1✔
96
                    && !dataFile.isRestricted()
×
97
                    && !FileUtil.isActivelyEmbargoed(dataFile)) {
×
98
                String imageSourceBase64 = null;
×
99
                imageSourceBase64 = ImageThumbConverter.getImageThumbnailAsBase64(dataFile, size);
×
100

101
                if (imageSourceBase64 != null) {
×
102
                    DatasetThumbnail datasetThumbnail = new DatasetThumbnail(imageSourceBase64, dataFile);
×
103
                    thumbnails.add(datasetThumbnail);
×
104
                }
105
            }
106
        }
1✔
107
        return thumbnails;
1✔
108
    }
109

110
    /**
111
     * Note "datasetVersionId" can be null. If needed, it helps the "efficiency"
112
     * of "attemptToAutomaticallySelectThumbnailFromDataFiles"
113
     *
114
     * @param dataset
115
     * @param datasetVersion
116
     * @param size of the requested thumbnail
117
     * @return DatasetThumbnail object, or null if not available
118
     */
119
    public static DatasetThumbnail getThumbnail(Dataset dataset, DatasetVersion datasetVersion, int size) {
120
        if (dataset == null) {
1✔
121
            return null;
1✔
122
        }
123

124
        if (size == 0) {
1✔
125
            // Size 0 will fail (and set the failure flag) and should never be sent
NEW
126
            logger.warning("getThumbnail called with size 0");
×
NEW
127
            return null;
×
128
        }        
129
        StorageIO<Dataset> dataAccess = null;
1✔
130
                
131
        try{
132
            dataAccess = DataAccess.getStorageIO(dataset);
1✔
133
        }
134
        catch(IOException ioex){
1✔
135
            logger.warning("getThumbnail(): Failed to initialize dataset StorageIO for " + dataset.getStorageIdentifier() + " (" + ioex.getMessage() + ")");
1✔
136
        }
1✔
137
        
138
        InputStream in = null;
1✔
139
        try {
140
            if (dataAccess == null) {
1✔
141
                logger.warning("getThumbnail(): Failed to initialize dataset StorageIO for " + dataset.getStorageIdentifier());
1✔
142
            } else {
143
                in = dataAccess.getAuxFileAsInputStream(datasetLogoThumbnail + ".thumb" + size);
1✔
144
            }
145
        } catch (IOException ex) {
×
146
            in = null; 
×
147
            logger.fine("Dataset-level thumbnail file does not exist, or failed to open; will try to find an image file that can be used as the thumbnail.");
×
148
        }
1✔
149

150
        
151
        if (in != null) {
1✔
152
            try {
153
                byte[] bytes = IOUtils.toByteArray(in);
×
154
                String base64image = Base64.getEncoder().encodeToString(bytes);
×
155
                DatasetThumbnail datasetThumbnail = new DatasetThumbnail(FileUtil.DATA_URI_SCHEME + base64image, null);
×
156
                logger.fine("will get thumbnail from dataset logo");
×
157
                return datasetThumbnail;
×
158
            } catch (IOException ex) {
×
159
                logger.fine("Unable to read thumbnail image from file: " + ex);
×
160
                return null;
×
161
            } finally
162
            {
163
                    IOUtils.closeQuietly(in);
×
164
            }
165
        } else {
166
            DataFile thumbnailFile = dataset.getThumbnailFile();
1✔
167

168
            if (thumbnailFile !=null && (thumbnailFile.isRestricted() || FileUtil.isActivelyEmbargoed(thumbnailFile))) {
1✔
169
                logger.fine("Dataset (id :" + dataset.getId() + ") has a thumbnail (user selected or automatically chosen) but the file must have later been restricted or embargoed. Returning null.");
1✔
170
                thumbnailFile= null;
1✔
171
            }
172
            if (thumbnailFile == null) {
1✔
173
                if (dataset.isUseGenericThumbnail()) {
1✔
174
                    logger.fine("Dataset (id :" + dataset.getId() + ") does not have a thumbnail and is 'Use Generic'.");
1✔
175
                    return null;
1✔
176
                } else {
177
                    thumbnailFile = attemptToAutomaticallySelectThumbnailFromDataFiles(dataset, datasetVersion);
1✔
178
                    if (thumbnailFile == null) {
1✔
179
                        logger.fine("Dataset (id :" + dataset.getId() + ") does not have a thumbnail available that could be selected automatically.");
1✔
180
                        return null;
1✔
181
                    } else {
182
                        String imageSourceBase64 = ImageThumbConverter.getImageThumbnailAsBase64(thumbnailFile, size);
×
183
                        DatasetThumbnail defaultDatasetThumbnail = new DatasetThumbnail(imageSourceBase64, thumbnailFile);
×
184
                        logger.fine("thumbnailFile (id :" + thumbnailFile.getId() + ") will get thumbnail through automatic selection from DataFile id " + thumbnailFile.getId());
×
185
                        return defaultDatasetThumbnail;
×
186
                    }
187
                }
188
            } else {
189
                String imageSourceBase64 = ImageThumbConverter.getImageThumbnailAsBase64(thumbnailFile, size);
×
190
                DatasetThumbnail userSpecifiedDatasetThumbnail = new DatasetThumbnail(imageSourceBase64, thumbnailFile);
×
191
                logger.fine("Dataset (id :" + dataset.getId() + ")  will get thumbnail the user specified from DataFile id " + thumbnailFile.getId());
×
192
                return userSpecifiedDatasetThumbnail;
×
193

194
            }
195
        }
196
    }
197

198
    public static DatasetThumbnail getThumbnail(Dataset dataset, int size) {
199
        if (dataset == null) {
1✔
200
            return null;
1✔
201
        }
202
        return getThumbnail(dataset, null, size);
1✔
203
    }
204

205
    public static boolean deleteDatasetLogo(Dataset dataset) {
206
        if (dataset == null) {
1✔
207
            return false;
1✔
208
        }
209
        try {
210
            StorageIO<Dataset> storageIO = getStorageIO(dataset);
×
211

212
            if (storageIO == null) {
×
213
                logger.warning("Null storageIO in deleteDatasetLogo()");
×
214
                return false;
×
215
            }
216

217
            storageIO.deleteAuxObject(datasetLogoFilenameFinal);
×
218
            storageIO.deleteAuxObject(datasetLogoThumbnail + thumbExtension + ImageThumbConverter.DEFAULT_DATASETLOGO_SIZE);
×
219
            storageIO.deleteAuxObject(datasetLogoThumbnail + thumbExtension + ImageThumbConverter.DEFAULT_CARDIMAGE_SIZE);
×
220

221
        } catch (IOException ex) {
1✔
222
            logger.fine("Failed to delete dataset logo: " + ex.getMessage() + 
1✔
223
                    " (this is most likely harmless; this method is often called without checking if the custom dataset logo was in fact present)");
224
            return false;
1✔
225
        }
×
226
        return true;
×
227
                
228
        //TODO: Is this required? 
229
//        File originalFile = new File(dataset.getFileSystemDirectory().toString(), datasetLogoFilenameFinal);
230
//        boolean originalFileDeleted = originalFile.delete();
231
//        File thumb48 = new File(dataset.getFileSystemDirectory().toString(), File.separator + datasetLogoThumbnail + thumb48addedByImageThumbConverter);
232
//        boolean thumb48Deleted = thumb48.delete();
233
//        if (originalFileDeleted && thumb48Deleted) {
234
//            return true;
235
//        } else {
236
//            logger.info("One of the files wasn't deleted. Original deleted: " + originalFileDeleted + ". thumb48 deleted: " + thumb48Deleted + ".");
237
//            return false;
238
//        }
239
    }
240

241
    /**
242
     * Pass an optional datasetVersion in case the file system is checked
243
     *
244
     * @param dataset
245
     * @param datasetVersion
246
     * @return
247
     */
248
    public static DataFile attemptToAutomaticallySelectThumbnailFromDataFiles(Dataset dataset, DatasetVersion datasetVersion) {
249
        if (dataset == null) {
1✔
250
            return null;
1✔
251
        }
252

253
        if (dataset.isUseGenericThumbnail()) {
1✔
254
            logger.fine("Bypassing logic to find a thumbnail because a generic icon for the dataset is desired.");
×
255
            return null;
×
256
        }
257

258
        if (datasetVersion == null) {
1✔
259
            logger.fine("getting a published version of the dataset");
1✔
260
            // We want to use published files only when automatically selecting 
261
            // dataset thumbnails.
262
            datasetVersion = dataset.getReleasedVersion(); 
1✔
263
        }
264
        
265
        // No published version? - No [auto-selected] thumbnail for you.
266
        if (datasetVersion == null) {
1✔
267
            return null; 
1✔
268
        }
269

270
        for (FileMetadata fmd : datasetVersion.getFileMetadatas()) {
×
271
            DataFile testFile = fmd.getDataFile();
×
272
            // We don't want to use a restricted image file as the dedicated thumbnail:
273
            if (!testFile.isRestricted() && !FileUtil.isActivelyEmbargoed(testFile) && FileUtil.isThumbnailSupported(testFile) && ImageThumbConverter.isThumbnailAvailable(testFile, ImageThumbConverter.DEFAULT_DATASETLOGO_SIZE)) {
×
274
                return testFile;
×
275
            }
276
        }
×
277
        logger.fine("In attemptToAutomaticallySelectThumbnailFromDataFiles and interated through all the files but couldn't find a thumbnail.");
×
278
        return null;
×
279
    }
280

281
    public static Dataset persistDatasetLogoToStorageAndCreateThumbnails(Dataset dataset, InputStream inputStream) {
282
        if (dataset == null) {
1✔
283
            return null;
1✔
284
        }
285
        File tmpFile = null;
×
286
        try {
287
            tmpFile = FileUtil.inputStreamToFile(inputStream);
×
288
        } catch (IOException ex) {
×
289
                logger.severe("FileUtil.inputStreamToFile failed for tmpFile: " + ex.getMessage());
×
290
        }
×
291

292
        StorageIO<Dataset> dataAccess = null;
×
293
                
294
        try{
295
             dataAccess = DataAccess.getStorageIO(dataset);
×
296
        }
297
        catch(IOException ioex){
×
298
            //TODO: Add a suitable warning message
299
            logger.warning("Failed to save the file, storage id " + dataset.getStorageIdentifier() + " (" + ioex.getMessage() + ")");
×
300
        }
×
301
        
302
        //File originalFile = new File(datasetDirectory.toString(), datasetLogoFilenameFinal);
303
        try {
304
            //this goes through Swift API/local storage/s3 to write the dataset thumbnail into a container
305
            dataAccess.savePathAsAux(tmpFile.toPath(), datasetLogoFilenameFinal);
×
306
        } catch (IOException ex) {
×
307
            logger.severe("Failed to move original file from " + tmpFile.getAbsolutePath() + " to its DataAccess location" + ": " + ex);
×
308
        }
×
309

310
        BufferedImage fullSizeImage = null;
×
311
        try {
312
            fullSizeImage = ImageIO.read(tmpFile);
×
313
        } catch (IOException ex) {
×
314
                IOUtils.closeQuietly(inputStream);
×
315
            logger.severe("ImageIO.read failed for tmpFile: " + ex.getMessage());
×
316
            return null;
×
317
        }
×
318
        if (fullSizeImage == null) {
×
319
            logger.fine("fullSizeImage was null!");
×
320
            IOUtils.closeQuietly(inputStream);
×
321
            return null;
×
322
        }
323
        int width = fullSizeImage.getWidth();
×
324
        int height = fullSizeImage.getHeight();
×
325
        FileChannel src = null;
×
326
        FileChannel dest = null;
×
327
        try (FileInputStream fis = new FileInputStream(tmpFile); FileOutputStream fos = new FileOutputStream(tmpFile)) {
×
328
            src = fis.getChannel();
×
329
            dest = fos.getChannel();
×
330
            dest.transferFrom(src, 0, src.size());
×
331
        } catch (IOException ex) {
×
332
                IOUtils.closeQuietly(inputStream);
×
333
            logger.severe("Error occurred during transfer using FileChannels: " + ex.getMessage());
×
334
            return null;
×
335
        }
×
336
        File tmpFileForResize = null;
×
337
        try {
338
                //The stream was used around line 274 above, so this creates an empty file (OK since all it is used for is getting a path, but not reusing it here would make it easier to close it above.)
339
            tmpFileForResize = FileUtil.inputStreamToFile(inputStream);
×
340
        } catch (IOException ex) {
×
341
            logger.severe("FileUtil.inputStreamToFile failed for tmpFileForResize: " + ex.getMessage());
×
342
            return null;
×
343
        } finally {
344
                IOUtils.closeQuietly(inputStream);
×
345
        }
346
        // We'll try to pre-generate the rescaled versions in both the 
347
        // DEFAULT_DATASET_LOGO (currently 140) and DEFAULT_CARDIMAGE_SIZE (48)
348
        String thumbFileLocation = ImageThumbConverter.rescaleImage(fullSizeImage, width, height, ImageThumbConverter.DEFAULT_DATASETLOGO_SIZE, tmpFileForResize.toPath().toString());
×
349
        if (thumbFileLocation == null) {
×
350
            logger.warning("Rescale Thumbnail Image to logo failed");
×
351
            dataset.setPreviewImageAvailable(false);
×
352
            dataset.setUseGenericThumbnail(true);
×
353
        } else {
354
            logger.fine("thumbFileLocation = " + thumbFileLocation);
×
355
            logger.fine("tmpFileLocation=" + tmpFileForResize.toPath().toString());
×
356
            //now we must save the updated thumbnail
357
            try {
358
                dataAccess.savePathAsAux(Paths.get(thumbFileLocation), datasetLogoThumbnail + thumbExtension + ImageThumbConverter.DEFAULT_DATASETLOGO_SIZE);
×
359
            } catch (IOException ex) {
×
360
                logger.severe("Failed to move updated thumbnail file from " + tmpFile.getAbsolutePath() + " to its DataAccess location" + ": " + ex);
×
361
            }
×
362
        }
363
        
364
        thumbFileLocation = ImageThumbConverter.rescaleImage(fullSizeImage, width, height, ImageThumbConverter.DEFAULT_CARDIMAGE_SIZE, tmpFileForResize.toPath().toString());
×
365
        if (thumbFileLocation == null) {
×
366
            logger.warning("Rescale Thumbnail Image to card failed");
×
367
            dataset.setPreviewImageAvailable(false);
×
368
            dataset.setUseGenericThumbnail(true);
×
369
        } else {
370
            logger.fine("thumbFileLocation = " + thumbFileLocation);
×
371
            logger.fine("tmpFileLocation=" + tmpFileForResize.toPath().toString());
×
372
            //now we must save the updated thumbnail
373
            try {
374
                dataAccess.savePathAsAux(Paths.get(thumbFileLocation), datasetLogoThumbnail + thumbExtension + ImageThumbConverter.DEFAULT_CARDIMAGE_SIZE);
×
375
            } catch (IOException ex) {
×
376
                logger.severe("Failed to move updated thumbnail file from " + tmpFile.getAbsolutePath() + " to its DataAccess location" + ": " + ex);
×
377
            }
×
378
        }
379
        
380
        //This deletes the tempfiles created for rescaling and encoding
381
        boolean tmpFileWasDeleted = tmpFile.delete();
×
382
        boolean originalTempFileWasDeleted = tmpFileForResize.delete();
×
383
        try {
384
            if (thumbFileLocation != null) {
×
385
                Files.delete(Paths.get(thumbFileLocation));
×
386
            }
387
        } catch (IOException ioex) {
×
388
            logger.fine("Failed to delete temporary thumbnail file");
×
389
        }
×
390
        
391
        logger.fine("Thumbnail saved to " + thumbFileLocation + ". Temporary file deleted : " + tmpFileWasDeleted + ". Original file deleted : " + originalTempFileWasDeleted);
×
392
        return dataset;
×
393
    }
394

395
    public static InputStream getThumbnailAsInputStream(Dataset dataset, int size) {
396
        if (dataset == null) {
1✔
397
            return null;
1✔
398
        }
399
        DatasetThumbnail datasetThumbnail = dataset.getDatasetThumbnail(size);
×
400
        if (datasetThumbnail == null) {
×
401
            return null;
×
402
        } else {
403
            String base64Image = datasetThumbnail.getBase64image();
×
404
            String leadingStringToRemove = FileUtil.DATA_URI_SCHEME;
×
405
            String encodedImg = base64Image.substring(leadingStringToRemove.length());
×
406
            byte[] decodedImg = Base64.getDecoder().decode(encodedImg.getBytes(StandardCharsets.UTF_8));
×
407
            logger.fine("returning this many bytes for  " + "dataset id: " + dataset.getId() + ", persistentId: " + dataset.getIdentifier() + " :" + decodedImg.length);
×
408
            ByteArrayInputStream nonDefaultDatasetThumbnail = new ByteArrayInputStream(decodedImg);
×
409
            logger.fine("For dataset id " + dataset.getId() + " a thumbnail was found and is being returned.");
×
410
            return nonDefaultDatasetThumbnail;
×
411
        }
412
    }
413
    
414
    public static InputStream getLogoAsInputStream(Dataset dataset) {
415
        if (dataset == null) {
×
416
            return null;
×
417
        }
418
        StorageIO<Dataset> dataAccess = null;
×
419

420
        try {
421
            dataAccess = DataAccess.getStorageIO(dataset);
×
422
        } catch (IOException ioex) {
×
423
            logger.warning("getLogo(): Failed to initialize dataset StorageIO for " + dataset.getStorageIdentifier()
×
424
                    + " (" + ioex.getMessage() + ")");
×
425
        }
×
426

427
        InputStream in = null;
×
428
        try {
429
            if (dataAccess == null) {
×
430
                logger.warning(
×
431
                        "getLogo(): Failed to initialize dataset StorageIO for " + dataset.getStorageIdentifier());
×
432
            } else {
433
                in = dataAccess.getAuxFileAsInputStream(datasetLogoFilenameFinal);
×
434
            }
435
        } catch (IOException ex) {
×
436
            logger.fine(
×
437
                    "Dataset-level thumbnail file does not exist, or failed to open; will try to find an image file that can be used as the thumbnail.");
438
        }
×
439

440
        if (in == null) {
×
441
            DataFile thumbnailFile = dataset.getThumbnailFile();
×
442

443
            if (thumbnailFile == null) {
×
444
                if (dataset.isUseGenericThumbnail()) {
×
445
                    logger.fine("Dataset (id :" + dataset.getId() + ") does not have a logo and is 'Use Generic'.");
×
446
                    return null;
×
447
                } else {
448
                    thumbnailFile = attemptToAutomaticallySelectThumbnailFromDataFiles(dataset, null);
×
449
                    if (thumbnailFile == null) {
×
450
                        logger.fine("Dataset (id :" + dataset.getId()
×
451
                                + ") does not have a logo available that could be selected automatically.");
452
                        return null;
×
453
                    } else {
454

455
                    }
456
                }
457
            }
458
            if (thumbnailFile.isRestricted()) {
×
459
                logger.fine("Dataset (id :" + dataset.getId()
×
460
                        + ") has a logo the user selected but the file must have later been restricted. Returning null.");
461
                return null;
×
462
            }
463

464
            try {
465

466
                boolean origImageFailed = thumbnailFile.isPreviewImageFail();
×
467
                InputStreamIO isIO = ImageThumbConverter.getImageThumbnailAsInputStream(thumbnailFile.getStorageIO(),
×
468
                        ImageThumbConverter.DEFAULT_DATASETLOGO_SIZE);
469
                if (!origImageFailed && thumbnailFile.isPreviewImageFail()) {
×
470
                    // We found an older 0 length thumbnail. Newer image uploads will not have this issue.
471
                    // Once cleaned up, this thumbnail will no longer have this issue
472
                    // ImageThumbConverter fixed the DataFile
473
                    // Now we need to update dataset since this is a bad logo
474
                    DatasetServiceBean datasetService = CDI.current().select(DatasetServiceBean.class).get();
×
475
                    datasetService.clearDatasetLevelThumbnail(dataset);
×
476
                }
477
                in = isIO != null ? isIO.getInputStream() : null;
×
478
            } catch (IOException ioex) {
×
479
                logger.warning("getLogo(): Failed to get logo from DataFile for " + dataset.getStorageIdentifier()
×
480
                        + " (" + ioex.getMessage() + ")");
×
481
                ioex.printStackTrace();
×
482
            }
×
483

484
        }
485
        return in;
×
486
    }
487

488
    /**
489
     * The dataset logo is the file that a user uploads which is *not* one of
490
     * the data files. Compare to the datavese logo. We do not save the original
491
     * file that is uploaded. Rather, we delete it after first creating at least
492
     * one thumbnail from it. 
493
     */
494
    public static boolean isDatasetLogoPresent(Dataset dataset, int size) {
495
        if (dataset == null) {
1✔
496
            return false;
×
497
        }
498

499
        StorageIO<Dataset> dataAccess = null;
1✔
500

501
        try {
502
            dataAccess = DataAccess.getStorageIO(dataset);
×
503
            return dataAccess.isAuxObjectCached(datasetLogoThumbnail + thumbExtension + size);
×
504
        } catch (IOException ioex) {
1✔
505
        }
506
        return false;
1✔
507
    }
508

509
    public static List<DatasetField> getDatasetSummaryFields(DatasetVersion datasetVersion, String customFieldNames) {
510
        Map<String, DatasetField> datasetFieldsSet = new HashMap<>();
1✔
511
        for (DatasetField dsf : datasetVersion.getFlatDatasetFields()) {
1✔
512
            datasetFieldsSet.put(dsf.getDatasetFieldType().getName(), dsf);
1✔
513
        }
1✔
514
        String[] summaryFieldNames = getDatasetSummaryFieldNames(customFieldNames);
1✔
515
        List<DatasetField> datasetSummaryFields = new ArrayList<>();
1✔
516
        for (String summaryFieldName : summaryFieldNames) {
1✔
517
            DatasetField df = datasetFieldsSet.get(summaryFieldName);
1✔
518
            if (df != null) {
1✔
519
                datasetSummaryFields.add(df);
1✔
520
            }
521
        }
522
        return datasetSummaryFields;
1✔
523
    }
524

525
    public static String[] getDatasetSummaryFieldNames(String customFieldNames) {
526
        String summaryFieldNames;
527
        // If the custom fields are empty, go with the default fields.
528
        if(customFieldNames == null || customFieldNames.isEmpty()){
1✔
529
            summaryFieldNames = datasetDefaultSummaryFieldNames;
1✔
530
        } else {
531
            summaryFieldNames = customFieldNames;
1✔
532
        }
533
        return summaryFieldNames.split(",");
1✔
534
    }
535

536
    public static boolean isRsyncAppropriateStorageDriver(Dataset dataset){
537
        // ToDo - rsync was written before multiple store support and currently is hardcoded to use the DataAccess.S3 store.
538
        // When those restrictions are lifted/rsync can be configured per store, this test should check that setting
539
        // instead of testing for the 's3" store,
540
        //This method is used by both the dataset and edit files page so one change here
541
        //will fix both
542
       return dataset.getEffectiveStorageDriverId().equals(DataAccess.S3);
×
543
    }
544
    
545
    /**
546
     * Given a dataset version, return it's size in human readable units such as
547
     * 42.9 MB.There is a GetDatasetStorageSizeCommand but it's overly complex
548
     * for the use case.
549
     *
550
     * @param original Use the original file size rather than the archival file
551
     * size for tabular files.
552
     */
553
    public static String getDownloadSize(DatasetVersion dsv, boolean original) {
554
        return FileSizeChecker.bytesToHumanReadable(getDownloadSizeNumeric(dsv, original));
×
555
    }
556

557
    public static Long getDownloadSizeNumeric(DatasetVersion dsv, boolean original) {
558
        return getDownloadSizeNumericBySelectedFiles(dsv.getFileMetadatas(), original);
×
559
    }
560

561
    public static Long getDownloadSizeNumericBySelectedFiles(List<FileMetadata> fileMetadatas, boolean original) {
562
        long bytes = 0l;
×
563
        for (FileMetadata fileMetadata : fileMetadatas) {
×
564
            DataFile dataFile = fileMetadata.getDataFile();
×
565
            if (original && dataFile.isTabularData()) {
×
566
                bytes += dataFile.getOriginalFileSize() == null ? 0 : dataFile.getOriginalFileSize();
×
567
            } else {
568
                bytes += dataFile.getFilesize();
×
569
            }
570
        }
×
571
        return bytes;
×
572
    }
573
    
574
    public static boolean validateDatasetMetadataExternally(Dataset ds, String executable, DataverseRequest request) {
575
        String sourceAddressLabel = "0.0.0.0"; 
×
576
        String userIdentifier = "guest";
×
577
        
578
        if (request != null) {
×
579
            IpAddress sourceAddress = request.getSourceAddress();
×
580
            if (sourceAddress != null) {
×
581
                sourceAddressLabel = sourceAddress.toString();
×
582
            }
583
            
584
            AuthenticatedUser user = request.getAuthenticatedUser();
×
585
            
586
            if (user != null) {
×
587
                userIdentifier = user.getUserIdentifier();
×
588
            }
589
        }
590
        
591
        String jsonMetadata; 
592
        
593
        // We are sending the dataset metadata encoded in our standard json 
594
        // format, with a couple of extra elements added, such as the ids of 
595
        // the home collection and the user, in order to make it easier 
596
        // for the filter to whitelist by these attributes. 
597
        
598
        try {
599
            jsonMetadata = json(ds).add("datasetVersion", json(ds.getLatestVersion(), true))
×
600
                    .add("sourceAddress", sourceAddressLabel)
×
601
                    .add("userIdentifier", userIdentifier)
×
602
                    .add("parentAlias", ds.getOwner().getAlias())
×
603
                    .build().toString();
×
604
        } catch (Exception ex) {
×
605
            logger.warning("Failed to export dataset metadata as json; "+ex.getMessage() == null ? "" : ex.getMessage());
×
606
            return false; 
×
607
        }
×
608
        
609
        if (StringUtil.isEmpty(jsonMetadata)) {
×
610
            logger.warning("Failed to export dataset metadata as json.");
×
611
            return false; 
×
612
        }
613
       
614
        // save the metadata in a temp file: 
615
        
616
        try {
617
            File tempFile = File.createTempFile("datasetMetadataCheck", ".tmp");
×
618
            FileUtils.writeStringToFile(tempFile, jsonMetadata, StandardCharsets.UTF_8);
×
619
            
620
            // run the external executable: 
621
            String[] params = { executable, tempFile.getAbsolutePath() };
×
622
            Process p = Runtime.getRuntime().exec(params);
×
623
            p.waitFor(); 
×
624
            
625
            return p.exitValue() == 0;
×
626
 
627
        } catch (IOException | InterruptedException ex) {
×
628
            logger.warning("Failed run the external executable.");
×
629
            return false; 
×
630
        }
631
        
632
    }
633

634
    public static License getLicense(DatasetVersion dsv) {
635
        License license = null;
1✔
636
        TermsOfUseAndAccess tua = dsv.getTermsOfUseAndAccess();
1✔
637
        if(tua!=null) {
1✔
638
            license = tua.getLicense();
1✔
639
        }
640
        return license;
1✔
641
    }
642

643
    public static String getLicenseName(DatasetVersion dsv) {
644

645
        DatasetVersionServiceBean datasetVersionService = CDI.current().select(DatasetVersionServiceBean.class).get();
×
646
        /*
647
        Special case where there are default custom terms indicating that no actual choice has been made...
648
         */
649
        if (datasetVersionService.isVersionDefaultCustomTerms(dsv)) {
×
650
            return BundleUtil.getStringFromBundle("license.none.chosen");
×
651
        }
652

653
        License license = DatasetUtil.getLicense(dsv);
×
654
        return getLocalizedLicenseName(license);
×
655
    }
656
    
657
    public static String getLocalizedLicenseName(License license) {
658
        return license != null ? getLocalizedLicenseDetails(license,"NAME")
×
659
                : BundleUtil.getStringFromBundle("license.custom");
×
660
    }
661

662
    public static String getLicenseURI(DatasetVersion dsv) {
663
        License license = DatasetUtil.getLicense(dsv);
1✔
664
        // Return the URI
665
        // For standard licenses, just return the stored URI
666
        return (license != null) ? license.getUri().toString()
1✔
667
                // For custom terms, construct a URI with draft version constant or the version number in the URI
668
                : (dsv.getVersionState().name().equals("DRAFT")
1✔
669
                        ? dsv.getDataverseSiteUrl()
×
670
                                + "/api/datasets/:persistentId/versions/" + DS_VERSION_DRAFT + "/customlicense?persistentId="
671
                                + dsv.getDataset().getGlobalId().asString()
×
672
                        : dsv.getDataverseSiteUrl() + "/api/datasets/:persistentId/versions/" + dsv.getVersionNumber()
1✔
673
                                + "." + dsv.getMinorVersionNumber() + "/customlicense?persistentId="
1✔
674
                                + dsv.getDataset().getGlobalId().asString());
1✔
675
    }
676

677
    public static String getLicenseIcon(DatasetVersion dsv) {
678
        License license = DatasetUtil.getLicense(dsv);
×
679
        return license != null && license.getIconUrl() != null ? license.getIconUrl().toString() : null;
×
680
    }
681

682
    public static String getLicenseDescription(DatasetVersion dsv) {
683
        
684
        DatasetVersionServiceBean datasetVersionService = CDI.current().select(DatasetVersionServiceBean.class).get();
×
685
        /*
686
        Special case where there are default custom terms indicating that no actual choice has been made...
687
         */
688
        if (datasetVersionService.isVersionDefaultCustomTerms(dsv)) {
×
689
            return BundleUtil.getStringFromBundle("license.none.chosen.description");
×
690
        }
691
        License license = DatasetUtil.getLicense(dsv);
×
692
        
693
        return license != null ? getLocalizedLicenseDetails(license,"DESCRIPTION") : BundleUtil.getStringFromBundle("license.custom.description");
×
694
    }
695

696
    public enum LicenseOption {
×
697
        NAME, DESCRIPTION
×
698
    };
699

700
    public static String getLocalizedLicenseDetails(License license,String keyPart) {
701
        String licenseName = license.getName();
×
702
        String localizedLicenseValue =  "" ;
×
703
        try {
704
            if (EnumUtils.isValidEnum(LicenseOption.class, keyPart ) ){
×
705
                String key = "license." + licenseName.toLowerCase().replace(" ", "_") + "." + keyPart.toLowerCase();
×
706
                localizedLicenseValue = BundleUtil.getStringFromPropertyFile(key, "License");
×
707
            }
708
        }
709
        catch (Exception e) {
×
710
            localizedLicenseValue = licenseName;
×
711
        }
×
712

713
        if (localizedLicenseValue == null) {
×
714
            localizedLicenseValue = licenseName ;
×
715
        }
716
        return localizedLicenseValue;
×
717
    }
718

719
    public static String getLocaleExternalStatus(String status) {
720
        String localizedName =  "" ;
×
721
        try {
722
            localizedName = BundleUtil.getStringFromPropertyFile(status.toLowerCase().replace(" ", "_"), "CurationLabels");
×
723
        }
724
        catch (Exception e) {
×
725
            localizedName = status;
×
726
        }
×
727

728
        if (localizedName == null) {
×
729
            localizedName = status ;
×
730
        }
731
        return localizedName;
×
732
    }
733
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc