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

CeON / dataverse / 1359

02 Apr 2024 09:47AM UTC coverage: 25.104%. First build
1359

push

jenkins

web-flow
Closes #2440: Improved shapefile handling (#2443)

* Closes #2440: Improved shapefile handler, error handling and simpler api, use commons-compress for extraction supporting unicode extra fields in zip

* review comments

96 of 123 new or added lines in 6 files covered. (78.05%)

17423 of 69404 relevant lines covered (25.1%)

0.25 hits per line

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

0.0
/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/datafile/page/EditDatafilesPage.java
1
package edu.harvard.iq.dataverse.datafile.page;
2

3
import edu.harvard.iq.dataverse.DataFileServiceBean;
4
import edu.harvard.iq.dataverse.DatasetDao;
5
import edu.harvard.iq.dataverse.DataverseRequestServiceBean;
6
import edu.harvard.iq.dataverse.DataverseSession;
7
import edu.harvard.iq.dataverse.PermissionServiceBean;
8
import edu.harvard.iq.dataverse.PermissionsWrapper;
9
import edu.harvard.iq.dataverse.api.AbstractApiBean;
10
import edu.harvard.iq.dataverse.dataaccess.ImageThumbConverter;
11
import edu.harvard.iq.dataverse.datacapturemodule.DataCaptureModuleUtil;
12
import edu.harvard.iq.dataverse.datafile.DataFileCreator;
13
import edu.harvard.iq.dataverse.datafile.FileService;
14
import edu.harvard.iq.dataverse.datafile.pojo.RsyncInfo;
15
import edu.harvard.iq.dataverse.dataset.DatasetService;
16
import edu.harvard.iq.dataverse.dataset.DatasetThumbnail;
17
import edu.harvard.iq.dataverse.dataset.DatasetThumbnailService;
18
import edu.harvard.iq.dataverse.dataset.datasetversion.DatasetVersionServiceBean;
19
import edu.harvard.iq.dataverse.datasetutility.FileExceedsMaxSizeException;
20
import edu.harvard.iq.dataverse.datasetutility.VirusFoundException;
21
import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException;
22
import edu.harvard.iq.dataverse.engine.command.impl.UpdateDatasetVersionCommand;
23
import edu.harvard.iq.dataverse.ingest.IngestServiceBean;
24
import edu.harvard.iq.dataverse.ingest.IngestUtil;
25
import edu.harvard.iq.dataverse.license.TermsOfUseFormMapper;
26
import edu.harvard.iq.dataverse.license.TermsOfUseSelectItemsFactory;
27
import edu.harvard.iq.dataverse.persistence.datafile.DataFile;
28
import edu.harvard.iq.dataverse.persistence.datafile.DataFileTag;
29
import edu.harvard.iq.dataverse.persistence.datafile.FileMetadata;
30
import edu.harvard.iq.dataverse.persistence.datafile.ingest.IngestRequest;
31
import edu.harvard.iq.dataverse.persistence.datafile.license.FileTermsOfUse;
32
import edu.harvard.iq.dataverse.persistence.datafile.license.TermsOfUseForm;
33
import edu.harvard.iq.dataverse.persistence.dataset.Dataset;
34
import edu.harvard.iq.dataverse.persistence.dataset.DatasetLock;
35
import edu.harvard.iq.dataverse.persistence.dataset.DatasetVersion;
36
import edu.harvard.iq.dataverse.persistence.user.AuthenticatedUser;
37
import edu.harvard.iq.dataverse.provenance.ProvPopupFragmentBean;
38
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
39
import edu.harvard.iq.dataverse.settings.SettingsServiceBean.Key;
40
import edu.harvard.iq.dataverse.settings.SettingsWrapper;
41
import edu.harvard.iq.dataverse.util.FileUtil;
42
import edu.harvard.iq.dataverse.util.JsfHelper;
43
import edu.harvard.iq.dataverse.util.SystemConfig;
44
import io.vavr.control.Option;
45
import io.vavr.control.Try;
46
import org.apache.commons.httpclient.HttpClient;
47
import org.apache.commons.httpclient.methods.GetMethod;
48
import org.apache.commons.io.IOUtils;
49
import org.apache.commons.lang3.StringUtils;
50
import org.apache.commons.lang3.math.NumberUtils;
51
import org.omnifaces.cdi.ViewScoped;
52
import org.primefaces.event.FileUploadEvent;
53
import org.primefaces.model.file.UploadedFile;
54

55
import javax.annotation.PreDestroy;
56
import javax.ejb.EJBException;
57
import javax.faces.application.FacesMessage;
58
import javax.faces.context.FacesContext;
59
import javax.faces.event.ActionEvent;
60
import javax.faces.model.SelectItem;
61
import javax.inject.Inject;
62
import javax.inject.Named;
63
import javax.json.Json;
64
import javax.json.JsonArray;
65
import javax.json.JsonObject;
66
import javax.json.JsonReader;
67
import javax.servlet.ServletOutputStream;
68
import javax.servlet.http.HttpServletResponse;
69
import java.io.File;
70
import java.io.FileOutputStream;
71
import java.io.IOException;
72
import java.io.InputStream;
73
import java.io.StringReader;
74
import java.nio.file.Files;
75
import java.nio.file.Path;
76
import java.nio.file.Paths;
77
import java.util.ArrayList;
78
import java.util.Arrays;
79
import java.util.Collection;
80
import java.util.HashMap;
81
import java.util.Iterator;
82
import java.util.List;
83
import java.util.Map;
84
import java.util.ResourceBundle;
85
import java.util.Set;
86
import java.util.logging.Level;
87
import java.util.logging.Logger;
88
import java.util.stream.Collectors;
89

90
import static edu.harvard.iq.dataverse.common.BundleUtil.getStringFromBundle;
91
import static edu.harvard.iq.dataverse.common.FileSizeUtil.bytesToHumanReadable;
92
import static java.util.stream.Collectors.joining;
93
import static java.util.stream.Collectors.toSet;
94

95

96
/**
97
 * @author Leonid Andreev
98
 */
99
@ViewScoped
100
@Named("EditDatafilesPage")
101
public class EditDatafilesPage implements java.io.Serializable {
102

103
    private static final long TEMP_VALID_TIME_MILLIS = 24 * 60 * 60 * 1000;
104

105
        private static final Logger logger = Logger.getLogger(EditDatafilesPage.class.getCanonicalName());
×
106

107
        private static final int NUMBER_OF_SCROLL_ROWS = 25;
108

109
    public enum FileEditMode {
×
110
        EDIT, UPLOAD, CREATE
×
111
    }
112

113
    private DatasetDao datasetDao;
114
    private DataFileServiceBean datafileDao;
115
    private DataFileCreator dataFileCreator;
116
    private PermissionServiceBean permissionService;
117
    private IngestServiceBean ingestService;
118
    private DataverseSession session;
119
    private SettingsServiceBean settingsService;
120
    private SystemConfig systemConfig;
121
    private DataverseRequestServiceBean dvRequestService;
122
    private PermissionsWrapper permissionsWrapper;
123
    private FileDownloadHelper fileDownloadHelper;
124
    private ProvPopupFragmentBean provPopupFragmentBean;
125
    private SettingsWrapper settingsWrapper;
126
    private DatasetVersionServiceBean datasetVersionService;
127
    private TermsOfUseFormMapper termsOfUseFormMapper;
128
    private TermsOfUseSelectItemsFactory termsOfUseSelectItemsFactory;
129
    private DatasetService datasetService;
130
    private FileService fileService;
131
    private DatasetThumbnailService datasetThumbnailService;
132
    private ImageThumbConverter imageThumbConverter;
133
    private DuplicatesService duplicatesService;
134

135
    private Dataset dataset = new Dataset();
×
136

137
    private String selectedFileIdsString = null;
×
138
    private FileEditMode mode = FileEditMode.EDIT;
×
139
    private List<FileMetadata> fileMetadatas = new ArrayList<>();
×
140

141

142
    private Long ownerId;
143
    private Long versionId;
144
    private List<DataFile> newFiles = new ArrayList<>();
×
145
    private List<DataFile> uploadedFiles = new ArrayList<>();
×
146
    private DataFileUploadInfo dataFileUploadInfo = new DataFileUploadInfo();
×
147
    private DatasetVersion workingVersion;
148
    private String dropBoxSelection = "";
×
149

150
    private String persistentId;
151

152
    private String versionString = "";
×
153

154
    private long currentBatchSize = 0L;
×
155

156
    private boolean saveEnabled = false;
×
157

158
    private Long maxFileUploadSizeInBytes = null;
×
159
    private Long multipleUploadFilesLimit = null;
×
160

161
    private List<SelectItem> termsOfUseSelectItems;
162
    private List<FileMetadata> selectedFiles;
163
    private List<DataFile> filesToBeDeleted = new ArrayList<>();
×
164

165
    private Boolean hasRsyncScript = false;
×
166

167
    /** The contents of the script. */
168
    private String rsyncScript = "";
×
169
    private String rsyncScriptFilename;
170
    private String warningMessageForPopUp;
171

172
    private String uploadWarningMessage = null;
×
173
    private String uploadSuccessMessage = null;
×
174
    private String uploadComponentId = null;
×
175

176
    private boolean uploadInProgress = false;
×
177

178
    private Map<String, String> temporaryThumbnailsMap = new HashMap<>();
×
179
    private Set<String> fileLabelsExisting = null;
×
180

181
    private Boolean lockedFromEditsVar;
182

183
    private FileMetadata fileMetadataSelectedForThumbnailPopup = null;
×
184
    private boolean alreadyDesignatedAsDatasetThumbnail = false;
×
185
    private FileMetadata selectedFile = null;
×
186
    private FileMetadata fileMetadataSelectedForIngestOptionsPopup = null;
×
187
    private String ingestLanguageEncoding = null;
×
188
    private String savedLabelsTempFile = null;
×
189

190
    private boolean hasDuplicates;
191
    private List<DuplicatesService.DuplicateGroup> duplicatesList = new ArrayList<>();
×
192
    private List<FileMetadata> filesTableBackup = new ArrayList<>();
×
193

194
    // -------------------- CONSTRUCTORS --------------------
195

196
    public EditDatafilesPage() { }
×
197

198
    @Inject
199
    public EditDatafilesPage(DatasetDao datasetDao, DataFileServiceBean datafileDao,
200
                             DataFileCreator dataFileCreator, PermissionServiceBean permissionService,
201
                             IngestServiceBean ingestService, DataverseSession session,
202
                             SettingsServiceBean settingsService, SystemConfig systemConfig,
203
                             DataverseRequestServiceBean dvRequestService, PermissionsWrapper permissionsWrapper,
204
                             FileDownloadHelper fileDownloadHelper, ProvPopupFragmentBean provPopupFragmentBean,
205
                             SettingsWrapper settingsWrapper, DatasetVersionServiceBean datasetVersionService,
206
                             TermsOfUseFormMapper termsOfUseFormMapper, TermsOfUseSelectItemsFactory termsOfUseSelectItemsFactory,
207
                             DatasetService datasetService, FileService fileService,
208
                             DatasetThumbnailService datasetThumbnailService, ImageThumbConverter imageThumbConverter,
209
                             DuplicatesService duplicatesService) {
×
210
        this.datasetDao = datasetDao;
×
211
        this.datafileDao = datafileDao;
×
212
        this.dataFileCreator = dataFileCreator;
×
213
        this.permissionService = permissionService;
×
214
        this.ingestService = ingestService;
×
215
        this.session = session;
×
216
        this.settingsService = settingsService;
×
217
        this.systemConfig = systemConfig;
×
218
        this.dvRequestService = dvRequestService;
×
219
        this.permissionsWrapper = permissionsWrapper;
×
220
        this.fileDownloadHelper = fileDownloadHelper;
×
221
        this.provPopupFragmentBean = provPopupFragmentBean;
×
222
        this.settingsWrapper = settingsWrapper;
×
223
        this.datasetVersionService = datasetVersionService;
×
224
        this.termsOfUseFormMapper = termsOfUseFormMapper;
×
225
        this.termsOfUseSelectItemsFactory = termsOfUseSelectItemsFactory;
×
226
        this.datasetService = datasetService;
×
227
        this.fileService = fileService;
×
228
        this.datasetThumbnailService = datasetThumbnailService;
×
229
        this.imageThumbConverter = imageThumbConverter;
×
230
        this.duplicatesService = duplicatesService;
×
231
    }
×
232

233
    // -------------------- GETTERS --------------------
234

235
    public String getSelectedFileIds() {
236
        return selectedFileIdsString;
×
237
    }
238

239
    public FileEditMode getMode() {
240
        return mode;
×
241
    }
242

243
    public Long getMaxFileUploadSizeInBytes() {
244
        return maxFileUploadSizeInBytes;
×
245
    }
246

247
    // The number of files the GUI user is allowed to upload in one batch,
248
    // via drag-and-drop, or through the file select dialog. Now configurable
249
    // in the Settings table.
250
    public Long getMaxNumberOfFiles() {
251
        return multipleUploadFilesLimit;
×
252
    }
253

254
    public String getGlobalId() {
255
        return persistentId;
×
256
    }
257

258
    public String getPersistentId() {
259
        return persistentId;
×
260
    }
261

262
    public String getDropBoxSelection() {
263
        return dropBoxSelection;
×
264
    }
265

266
    public Dataset getDataset() {
267
        return dataset;
×
268
    }
269

270
    public void setSelectedFileIds(String selectedFileIds) {
271
        selectedFileIdsString = selectedFileIds;
×
272
    }
×
273

274
    public DatasetVersion getWorkingVersion() {
275
        return workingVersion;
×
276
    }
277

278
    public Long getOwnerId() {
279
        return ownerId;
×
280
    }
281

282
    public Long getVersionId() {
283
        return versionId;
×
284
    }
285

286
    public long getCurrentBatchSize() {
287
        return currentBatchSize;
×
288
    }
289

290
    public List<FileMetadata> getSelectedFiles() {
291
        return selectedFiles;
×
292
    }
293

294
    public String getVersionString() {
295
        return versionString;
×
296
    }
297

298
    public Boolean isHasRsyncScript() {
299
        return hasRsyncScript;
×
300
    }
301

302
    public String getRsyncScript() {
303
        return rsyncScript;
×
304
    }
305

306
    public String getRsyncScriptFilename() {
307
        return rsyncScriptFilename;
×
308
    }
309

310
    public String getWarningMessageForPopUp() {
311
        return warningMessageForPopUp;
×
312
    }
313

314
    public FileMetadata getSelectedFile() {
315
        return selectedFile;
×
316
    }
317

318
    public FileMetadata getFileMetadataSelectedForIngestOptionsPopup() {
319
        return fileMetadataSelectedForIngestOptionsPopup;
×
320
    }
321

322
    public void setMode(FileEditMode mode) {
323
        this.mode = mode;
×
324
    }
×
325

326
    public List<SelectItem> getTermsOfUseSelectItems() {
327
        return termsOfUseSelectItems;
×
328
    }
329

330
    public boolean getHasDuplicates() {
331
        return hasDuplicates;
×
332
    }
333

334
    public List<DuplicatesService.DuplicateGroup> getDuplicatesList() {
335
        return duplicatesList;
×
336
    }
337

338
    // -------------------- LOGIC --------------------
339

340
    public List<FileMetadata> getFileMetadatas() {
341

342
        logger.fine(fileMetadatas != null
×
343
            ? String.format("Returning a list of %d file metadatas.", fileMetadatas.size())
×
344
            : "File metadatas list hasn't been initialized yet.");
345
        return fileMetadatas;
×
346
    }
347

348
    /**
349
     *   The method below is for setting up the p:dataTable component
350
     *   used to display the uploaded files, or the files selected for editing.
351
     *   It supplies the value of the component attribute "scrollable".
352
     *   When we have more than NUMBER_OF_SCROLL_ROWS worth of files (currently
353
     *   set to 25), we will add a scroller to the table, showing NUMBER_OF_SCROLL_ROWS
354
     *   at a time; thus making the page a little bit more usable.
355
     *   When there is fewer rows, however, the attribute needs to be set to
356
     *   false - because otherwise some (idiosyncratic) amount of white space
357
     *   is added to the bottom of the table, making the page look silly.
358
     */
359
    public boolean isScrollable() {
360
        return !(fileMetadatas == null || fileMetadatas.size() <= NUMBER_OF_SCROLL_ROWS + 1);
×
361
    }
362

363
    /**
364
     *  This may be null, signifying unlimited download size.
365
     */
366
    public String getHumanMaxFileUploadSize() {
367
        return getMaxFileUploadSizeInBytes() == null
×
368
                ? StringUtils.EMPTY
369
                : bytesToHumanReadable(getMaxFileUploadSizeInBytes());
×
370
    }
371

372
    public boolean isUnlimitedUploadFileSize() {
373
        return maxFileUploadSizeInBytes == null;
×
374
    }
375

376
    public String getHumanMaxBatchUploadSize() {
377
        Long batchSize = getMaxBatchSize();
×
378
        return batchSize == null || batchSize.equals(0L)
×
379
                ? StringUtils.EMPTY
380
                : bytesToHumanReadable(batchSize);
×
381
    }
382

383
    public String getUploadBatchTooBigMessage() {
384
        return getStringFromBundle("dataset.file.uploadBatchTooBig", getHumanMaxBatchUploadSize());
×
385
    }
386

387
    public void reset() { }
×
388

389
    public String getDropBoxKey() {
390
        // Site-specific DropBox application registration key is configured
391
        // via a JVM option under glassfish.
392
        // if (true) return "some-test-key";  // for debugging
393
        return settingsService.getValueForKey(Key.DropboxKey);
×
394
    }
395

396
    public String initCreateMode(DatasetVersion version, List<DataFile> newFilesList, List<FileMetadata> selectedFileMetadatasList) {
397
        logger.fine("Initializing Edit Files page in CREATE mode;");
×
398
        if (version == null) {
×
399
            return permissionsWrapper.notFound();
×
400
        }
401

402
        maxFileUploadSizeInBytes = settingsService.getValueForKeyAsLong(SettingsServiceBean.Key.MaxFileUploadSizeInBytes);
×
403
        multipleUploadFilesLimit = settingsService.getValueForKeyAsLong(SettingsServiceBean.Key.MultipleUploadFilesLimit);
×
404
        workingVersion = version;
×
405
        dataset = version.getDataset();
×
406
        mode = FileEditMode.CREATE;
×
407
        newFiles = newFilesList;
×
408
        uploadedFiles = new ArrayList<>();
×
409
        selectedFiles = selectedFileMetadatasList;
×
410
        termsOfUseSelectItems = termsOfUseSelectItemsFactory.buildLicenseSelectItems();
×
411
        saveEnabled = true;
×
412
        return null;
×
413
    }
414

415

416
    public String init() {
417
        fileMetadatas = new ArrayList<>();
×
418
        newFiles = new ArrayList<>();
×
419
        uploadedFiles = new ArrayList<>();
×
420
        cleanupTempFiles();
×
421

422
        maxFileUploadSizeInBytes = settingsService.getValueForKeyAsLong(Key.MaxFileUploadSizeInBytes);
×
423
        multipleUploadFilesLimit = settingsService.getValueForKeyAsLong(Key.MultipleUploadFilesLimit);
×
424
        termsOfUseSelectItems = termsOfUseSelectItemsFactory.buildLicenseSelectItems();
×
425

426
        if (dataset.getId() != null) {
×
427
            // Set Working Version and Dataset by Dataset Id
428
            dataset = datasetDao.find(dataset.getId());
×
429
            // Is the Dataset harvested? (we don't allow editing of harvested files)
430
            if (dataset == null || dataset.isHarvested()) {
×
431
                return permissionsWrapper.notFound();
×
432
            }
433
        } else {
434
            return permissionsWrapper.notFound();
×
435
        }
436
        workingVersion = dataset.getEditVersion();
×
437

438
        if (!permissionsWrapper.canCurrentUserUpdateDataset(dataset)) {
×
439
            return permissionsWrapper.notAuthorized();
×
440
        }
441
        if (datasetDao.isInReview(dataset) && !permissionsWrapper.canUpdateAndPublishDataset(dataset)) {
×
442
            return permissionsWrapper.notAuthorized();
×
443
        }
444

445
        if (mode == FileEditMode.EDIT) {
×
446
            Set<Long> selectedFileIds = Arrays.stream(StringUtils.split(StringUtils.trimToEmpty(selectedFileIdsString), ','))
×
447
                    .map(NumberUtils::toLong)
×
448
                    .filter(fileId -> fileId != 0)
×
449
                    .collect(toSet());
×
450

451
            if (selectedFileIds.isEmpty()) {
×
452
                logger.fine("No numeric file ids supplied to the page, in the edit mode. Redirecting to the 404 page.");
×
453
                return permissionsWrapper.notFound();
×
454
            }
455

456
            logger.fine("The page is called with " + selectedFileIds.size() + " file ids.");
×
457

458
            populateFileMetadatas(selectedFileIds);
×
459
            setUpRsync();
×
460
            if (fileMetadatas.isEmpty()) {
×
461
                return permissionsWrapper.notFound();
×
462
            }
463
        }
464

465
        saveEnabled = true;
×
466
        if (mode == FileEditMode.UPLOAD && workingVersion.getFileMetadatas().isEmpty() && settingsWrapper.isRsyncUpload()) {
×
467
            setUpRsync();
×
468
        }
469
        return null;
×
470
    }
471

472
    public boolean isInUploadMode() {
473
        return mode == FileEditMode.UPLOAD;
×
474
    }
475

476
    public String getMultiUploadDetailsMessage() {
477
        return getStringFromBundle("dataset.message.uploadFilesSingle.message",
×
478
                systemConfig.getGuidesBaseUrl(session.getLocale()), systemConfig.getGuidesVersion());
×
479
    }
480

481
    public boolean isInstallationPublic() {
482
        return settingsService.isTrueForKey(SettingsServiceBean.Key.PublicInstall);
×
483
    }
484

485
    // This deleteFilesCompleted method is used in editFilesFragment.xhtml
486
    public void deleteFilesCompleted() { }
×
487

488
    public void deleteFiles() {
489
        logger.fine("entering bulk file delete (EditDataFilesPage)");
×
490

491
        String fileNames = selectedFiles.stream()
×
492
                .map(FileMetadata::getLabel)
×
493
                .collect(joining(", "));
×
494

495
        for (FileMetadata markedForDelete : getSelectedFiles()) {
×
496
            logger.fine(String.format("delete requested on file %s, file metadata id: %d, datafile id: %d, page is in edit mode %s",
×
497
                    markedForDelete.getLabel(), markedForDelete.getId(), markedForDelete.getDataFile().getId(), mode.name()));
×
498

499
            // Has this filemetadata been saved already? Or is it a brand new filemetadata, created as part of a brand
500
            // new version, created when the user clicked 'delete', that hasn't been saved in the db yet?
501
            if (!markedForDelete.isNew()) {
×
502
                logger.fine("this is a filemetadata from an existing draft version");
×
503
                // so all we remove is the file from the fileMetadatas (from the file metadatas attached to the
504
                // editVersion, and from the display list of file metadatas that are being edited) and let the delete be
505
                // handled in the command (by adding it to the filesToBeDeleted list):
506

507
                fileMetadatas.remove(markedForDelete);
×
508
                filesToBeDeleted.add(markedForDelete.getDataFile());
×
509
            } else {
510
                logger.fine("this is a brand-new (unsaved) filemetadata");
×
511
                // If the bean is in the 'CREATE' mode, the page is using dataset.getEditVersion().getFileMetadatas()
512
                // directly, so there's no need to delete this meta from the local fileMetadatas list. (but doing both
513
                // just adds a no-op and won't cause an error)
514

515
                // delete the filemetadata from the local display list and version
516
                removeFileMetadataFromList(fileMetadatas, markedForDelete);
×
517
                removeFileMetadataFromList(dataset.getEditVersion().getFileMetadatas(), markedForDelete);
×
518
            }
519

520
            if (markedForDelete.getDataFile().isNew()) {
×
521
                DataFile dataFileToDelete = markedForDelete.getDataFile();
×
522
                logger.fine("this is a brand new file.");
×
523
                // the file was just added during this step, so in addition to removing it from the fileMetadatas lists
524
                // (above), we also remove it from the newFiles list and the dataset's files, so it never gets saved.
525

526
                removeDataFileFromList(dataset.getFiles(), dataFileToDelete);
×
527
                removeDataFileFromList(newFiles, dataFileToDelete);
×
528
                deleteTempFile(dataFileToDelete);
×
529
                updateCurrentBatchSizeForDeletedDataFile(dataFileToDelete);
×
530
            }
531
        }
×
532
        logger.fine("Files was removed from the list - changes will persist after save changes will be executed");
×
533
        JsfHelper.addFlashSuccessMessage(getStringFromBundle("file.deleted.success", fileNames));
×
534
        hasDuplicates = hasDuplicatesInUploadedFiles(newFiles);
×
535
    }
×
536

537
    /**
538
     * The method is used to clean temporary files on various events
539
     * such as closing the upload tab or logging out.
540
     */
541
        @PreDestroy
542
        void cleanTempFilesOnViewDestroy() {
543
        newFiles.forEach(this::deleteTempFile);
×
544
        uploadedFiles.forEach(this::deleteTempFile);
×
545
    }
×
546

547
    public String save() {
548
        // Once all the filemetadatas pass the validation, we'll only allow the user to try to save once – this it to
549
        // prevent them from creating multiple DRAFT versions, if the page gets stuck in that state where it
550
        // successfully creates a new version, but can't complete the remaining tasks. -- L.A. 4.2
551

552
        if (!saveEnabled) {
×
553
            return StringUtils.EMPTY;
×
554
        }
555

556
        int oldFilesNumber = workingVersion.getFileMetadatas().size();
×
557
        int newFilesNumber = newFiles.size();
×
558
        int expectedFilesTotal = oldFilesNumber + newFilesNumber;
×
559

560
        if (newFilesNumber > 0) {
×
561
            // SEK 10/15/2018 only apply the following tests if dataset has already been saved.
562
            if (dataset.getId() != null) {
×
563
                Dataset lockTest = datasetDao.find(dataset.getId());
×
564
                // SEK 09/19/18 Get Dataset again to test for lock just in case the user downloads the rsync script via
565
                // the api while the edit files page is open and has already loaded a file in http upload for Dual Mode
566
                if (dataset.isLockedFor(DatasetLock.Reason.DcmUpload) || lockTest.isLockedFor(DatasetLock.Reason.DcmUpload)) {
×
567
                    logger.log(Level.INFO, "Couldn''t save dataset: {0}", "DCM script has been downloaded for " +
×
568
                            "this dataset. Additional files are not permitted.");
569
                    populateDatasetUpdateFailureMessage();
×
570
                    return null;
×
571
                }
572
                for (DatasetVersion version : lockTest.getVersions()) {
×
573
                    if (version.isHasPackageFile()) {
×
574
                        logger.log(Level.INFO, ResourceBundle.getBundle("Bundle").getString("file.api.alreadyHasPackageFile"));
×
575
                        populateDatasetUpdateFailureMessage();
×
576
                        return null;
×
577
                    }
578
                }
×
579
            }
580

581
            for (DataFile newFile : newFiles) {
×
582
                TermsOfUseForm termsOfUseForm = newFile.getFileMetadata().getTermsOfUseForm();
×
583
                FileTermsOfUse termsOfUse = termsOfUseFormMapper.mapToFileTermsOfUse(termsOfUseForm);
×
584
                newFile.getFileMetadata().setTermsOfUse(termsOfUse);
×
585
            }
×
586

587
            // Try to save the NEW files permanently:
588
            List<DataFile> filesAdded = ingestService.saveAndAddFilesToDataset(workingVersion, newFiles);
×
589

590
            // reset the working list of fileMetadatas, as to only include the ones
591
            // that have been added to the version successfully:
592
            fileMetadatas.clear();
×
593
            for (DataFile addedFile : filesAdded) {
×
594
                fileMetadatas.add(addedFile.getFileMetadata());
×
595
            }
×
596
        }
597

598
        if (settingsService.isTrueForKey(SettingsServiceBean.Key.ProvCollectionEnabled)) {
×
599
            provPopupFragmentBean.updatePageMetadatasWithProvFreeform(fileMetadatas);
×
600
            try {
601
                // Note that the user may have uploaded provenance metadata file(s) for some of the new files that have
602
                // since failed to be permanently saved in storage (in the ingestService.saveAndAddFilesToDataset()
603
                // step, above); these files have been dropped from the fileMetadatas list, and we are not adding them
604
                // to the dataset; but the provenance update set still has entries for these failed files, so we are
605
                // passing the fileMetadatas list to the saveStagedProvJson() method below - so that it doesn't attempt
606
                // to save the entries that are no longer valid.
607
                provPopupFragmentBean.saveStagedProvJson(false, fileMetadatas);
×
608
            } catch (AbstractApiBean.WrappedResponse ex) {
×
609
                JsfHelper.addFlashErrorMessage(getStringFromBundle("file.metadataTab.provenance.error"));
×
610
                Logger.getLogger(EditDatafilesPage.class.getName()).log(Level.SEVERE, null, ex);
×
611
            }
×
612
        }
613

614
        logger.fine("issuing the dataset update command");
×
615
        Map<Long, String> deleteStorageLocations = null;
×
616

617
        if (!filesToBeDeleted.isEmpty()) {
×
618
            deleteStorageLocations = datafileDao.getPhysicalFilesToDelete(filesToBeDeleted);
×
619
        }
620

621
        Try<Dataset> updateDatasetOperation = Try.of(() -> datasetVersionService.updateDatasetVersion(workingVersion, filesToBeDeleted, true))
×
622
                .onSuccess(updatedDataset -> dataset = updatedDataset)
×
623
                .onFailure(ex -> {
×
624
                    logger.log(Level.SEVERE, "Couldn't update dataset with id: " + workingVersion.getDataset().getId(), ex);
×
625
                    populateDatasetUpdateFailureMessage();
×
626
                });
×
627

628
        if (updateDatasetOperation.isFailure()) {
×
629
            return StringUtils.EMPTY;
×
630
        }
631

632
        // Have we just deleted some draft datafiles successfully? Finalize the physical file deletes:
633
        // (DataFileService will double-check that the datafiles no longer exist in the database, before attempting to
634
        // delete the physical files)
635
        if (deleteStorageLocations != null) {
×
636
            datafileDao.finalizeFileDeletes(deleteStorageLocations);
×
637
        }
638

639
        saveEnabled = false;
×
640

641
        if (!newFiles.isEmpty()) {
×
642
            logger.fine("clearing newfiles list.");
×
643
            newFiles.clear();
×
644
        }
645

646
        workingVersion = dataset.getEditVersion();
×
647
        logger.fine("working version id: " + workingVersion.getId());
×
648

649
        int filesTotal = workingVersion.getFileMetadatas().size();
×
650
        if (newFilesNumber == 0 || filesTotal == expectedFilesTotal) {
×
651
            JsfHelper.addFlashSuccessMessage(getStringFromBundle("dataset.message.filesSuccess"));
×
652
        } else if (filesTotal == oldFilesNumber) {
×
653
            JsfHelper.addFlashErrorMessage(getStringFromBundle("dataset.message.addFiles.Failure"));
×
654
        } else {
655
            JsfHelper.addFlashWarningMessage(getStringFromBundle(
×
656
                    "dataset.message.addFiles.partialSuccess", filesTotal - oldFilesNumber, newFilesNumber));
×
657
        }
658

659
        // Call Ingest Service one more time to queue the data ingest jobs for asynchronous execution:
660
        if (mode == FileEditMode.UPLOAD) {
×
661
            ingestService.startIngestJobsForDataset(dataset, (AuthenticatedUser) session.getUser());
×
662
        }
663

664
        logger.fine("Redirecting to the dataset page, from the edit/upload page.");
×
665
        return returnToDraftVersion();
×
666
    }
667

668
    public String returnToDatasetOnly() {
669
        dataset = datasetDao.find(dataset.getId());
×
670
        return "/dataset.xhtml?persistentId=" + dataset.getGlobalId().asString() + "&faces-redirect=true";
×
671
    }
672

673
    public String getPageTitle(boolean datasetPage) {
674
        return datasetPage || showFileUploadFragment()
×
675
                ? getStringFromBundle("file.uploadFiles")
×
676
                : getStringFromBundle("file.editFiles") + " - " + workingVersion.getParsedTitle();
×
677
    }
678

679
    public String cancel() {
680
        uploadInProgress = false;
×
681
        // Files that have been finished and are now in the lower list on the page
682
        for (DataFile newFile : newFiles) {
×
683
            deleteTempFile(newFile);
×
684
        }
×
685
        // Files in the upload process but not yet finished
686
        for (DataFile newFile : uploadedFiles) {
×
687
            deleteTempFile(newFile);
×
688
        }
×
689
        if (workingVersion.getId() != null) {
×
690
            return returnToDraftVersion();
×
691
        }
692
        return returnToDatasetOnly();
×
693
    }
694

695
    public boolean allowMultipleFileUpload() {
696
        return true;
×
697
    }
698

699
    public boolean showFileUploadFragment() {
700
        return mode == FileEditMode.UPLOAD || mode == FileEditMode.CREATE;
×
701
    }
702

703
    public boolean showFileUploadComponent() {
704
        return mode == FileEditMode.UPLOAD || mode == FileEditMode.CREATE;
×
705
    }
706

707
    /**
708
     * Using information from the DropBox choose, ingest the chosen files
709
     * https://www.dropbox.com/developers/dropins/chooser/js
710
     */
711
    public void handleDropBoxUpload(ActionEvent event) throws IOException {
712
        if (!uploadInProgress) {
×
713
            uploadInProgress = true;
×
714
        }
715
        logger.fine("handleDropBoxUpload");
×
716
        uploadComponentId = event.getComponent().getClientId();
×
717

718
        // Read JSON object from the output of the DropBox Chooser:
719
        JsonReader dbJsonReader = Json.createReader(new StringReader(dropBoxSelection));
×
720
        JsonArray dbArray = dbJsonReader.readArray();
×
721
        dbJsonReader.close();
×
722

723
        // Iterate through the Dropbox file information (JSON)
724
        List<String> localWarningMessages = new ArrayList<>();
×
725
        for (int i = 0; i < dbArray.size(); i++) {
×
726
            JsonObject dbObject = dbArray.getJsonObject(i);
×
727

728
            // Parse information for a single file
729
            String fileLink = dbObject.getString("link");
×
730
            String fileName = dbObject.getString("name");
×
731
            int fileSize = dbObject.getInt("bytes");
×
732

733
            logger.fine("DropBox url: " + fileLink + ", filename: " + fileName + ", size: " + fileSize);
×
734

735
            // Check file size
736
            //  - Max size NOT specified in db: default is unlimited
737
            //  - Max size specified in db: check too make sure file is within limits
738
            if (!isUnlimitedUploadFileSize() && fileSize > getMaxFileUploadSizeInBytes()) {
×
739
                String warningMessage = "Dropbox file \"" + fileName + "\" exceeded the limit of " + fileSize + " bytes and was not uploaded.";
×
740
                localWarningMessages.add(warningMessage);
×
741
                continue;
×
742
            }
743

744
            GetMethod dropBoxMethod = new GetMethod(fileLink);
×
745
            List<DataFile> datafiles = new ArrayList<>();
×
746

747
            // Send it through the ingest service
748
            try (InputStream dropBoxStream = getDropBoxContent(dropBoxMethod)) {
×
749
                // Note: A single uploaded file may produce multiple datafiles -
750
                // for example, multiple files can be extracted from an uncompressed
751
                // zip file.
752
                datafiles = dataFileCreator.createDataFiles(dropBoxStream, fileName, "application/octet-stream");
×
753
            } catch (IOException | FileExceedsMaxSizeException ex) {
×
754
                logger.log(Level.SEVERE, "Error during ingest of DropBox file {0} from link {1}", new Object[]{fileName, fileLink});
×
755
                continue;
756
            } catch (VirusFoundException e) {
×
757
                localWarningMessages.add(getStringFromBundle("dataset.file.uploadScannerWarning"));
×
758
                continue;
759
            } finally {
760
                dropBoxMethod.releaseConnection();
×
761
            }
762

763
            uploadWarningMessage = processUploadedFileList(datafiles);
×
764
            logger.fine("Warning message during upload: " + uploadWarningMessage);
×
765

766
            if (!uploadInProgress) {
×
767
                logger.warning("Upload in progress cancelled");
×
768
                for (DataFile newFile : datafiles) {
×
769
                    deleteTempFile(newFile);
×
770
                }
×
771
            }
772
        }
773

774
        if (!localWarningMessages.isEmpty()) {
×
775
            uploadWarningMessage = uploadWarningMessage == null
×
776
                    ? StringUtils.join(localWarningMessages, "; ")
×
777
                    : uploadWarningMessage + "; " + StringUtils.join(localWarningMessages, "; ");
×
778
        }
779
    }
×
780

781
    public void downloadRsyncScript() {
782
        FacesContext ctx = FacesContext.getCurrentInstance();
×
783
        HttpServletResponse response = (HttpServletResponse) ctx.getExternalContext().getResponse();
×
784
        response.setContentType("application/download");
×
785

786
        String contentDispositionString = "attachment;filename=" + rsyncScriptFilename;
×
787
        response.setHeader("Content-Disposition", contentDispositionString);
×
788

789
        try {
790
            ServletOutputStream out = response.getOutputStream();
×
791
            out.write(getRsyncScript().getBytes());
×
792
            out.flush();
×
793
            ctx.responseComplete();
×
794
        } catch (IOException e) {
×
795
            String error = "Problem getting bytes from rsync script: " + e;
×
796
            logger.warning(error);
×
797
            return;
×
798
        }
×
799

800
        // If the script has been successfully downloaded, lock the dataset:
801
        String lockInfoMessage = "script downloaded";
×
802
        DatasetLock lock = datasetDao.addDatasetLock(dataset.getId(), DatasetLock.Reason.DcmUpload, session.getUser() != null ? ((AuthenticatedUser) session.getUser()).getId() : null, lockInfoMessage);
×
803
        if (lock != null) {
×
804
            dataset.addLock(lock);
×
805
        } else {
806
            logger.log(Level.WARNING, "Failed to lock the dataset (dataset id={0})", dataset.getId());
×
807
        }
808
    }
×
809

810
    public void uploadFinished() {
811
        // This method is triggered from the page, by the <p:upload ... onComplete=... attribute.
812
        // Note that its behavior is different from that of of <p:upload ... onStart=...
813
        // that's triggered only once, even for a multiple file upload. In contrast,
814
        // onComplete=... gets executed for each of the completed multiple upload events.
815
        // So when you drag-and-drop a bunch of files, you CANNOT rely on onComplete=...
816
        // to notify the page when the batch finishes uploading! There IS a way
817
        // to detect ALL the current uploads completing: the p:upload widget has
818
        // the property "files", that contains the list of all the files currently
819
        // uploading; so checking on the size of the list tells you if any uploads
820
        // are still in progress. Once it's zero, you know it's all done.
821
        // This is super important - because if the user is uploading 1000 files
822
        // via drag-and-drop, you don't want to re-render the entire page each
823
        // time every single of the 1000 uploads finishes!
824
        // (check editFilesFragment.xhtml for the exact code handling this; and
825
        // http://stackoverflow.com/questions/20747201/when-multiple-upload-is-finished-in-pfileupload
826
        // for more info). -- 4.6
827
        logger.fine("upload finished");
×
828

829
        // Add the file(s) added during this last upload event, single or multiple,
830
        // to the full list of new files, and the list of filemetadatas
831
        // used to render the page:
832

833
        for (DataFile dataFile : uploadedFiles) {
×
834
            fileMetadatas.add(dataFile.getFileMetadata());
×
835
            newFiles.add(dataFile);
×
836
        }
×
837
        if (uploadInProgress) {
×
838
            uploadedFiles = new ArrayList<>();
×
839
            uploadInProgress = false;
×
840
        }
841
        // refresh the warning message below the upload component, if exists:
842
        if (uploadComponentId != null && uploadWarningMessage != null) {
×
843
            FacesContext.getCurrentInstance()
×
844
                    .addMessage(uploadComponentId, new FacesMessage(FacesMessage.SEVERITY_ERROR, getStringFromBundle("dataset.file.uploadWarning"), uploadWarningMessage));
×
845
        } else if (uploadComponentId != null && uploadSuccessMessage != null) {
×
846
            FacesContext.getCurrentInstance()
×
847
                    .addMessage(uploadComponentId, new FacesMessage(FacesMessage.SEVERITY_INFO, getStringFromBundle("dataset.file.uploadWorked"), uploadSuccessMessage));
×
848
        }
849

850
        uploadWarningMessage = null;
×
851
        uploadSuccessMessage = null;
×
852

853
        hasDuplicates = hasDuplicatesInUploadedFiles(newFiles);
×
854
    }
×
855

856
    public Long getMaxBatchSize() {
857
        return settingsService.getValueForKeyAsLong(Key.SingleUploadBatchMaxSize);
×
858
    }
859

860
    /**
861
     * Handle native file replace
862
     */
863
    public void handleFileUpload(FileUploadEvent event) throws IOException {
864
        if (!uploadInProgress) {
×
865
            uploadInProgress = true;
×
866
        }
867

868
        UploadedFile uploadedFile = event.getFile();
×
869

870
        long fileSize = uploadedFile.getSize();
×
871
        if (getMaxBatchSize() > 0 && (currentBatchSize + fileSize) > getMaxBatchSize()) {
×
872
            uploadWarningMessage = getUploadBatchTooBigMessage();
×
873
            uploadComponentId = event.getComponent().getClientId();
×
874
            return;
×
875
        }
876
        currentBatchSize += fileSize;
×
877

878
        List<DataFile> dFileList;
879

880
        try (InputStream inputStream = uploadedFile.getInputStream()) {
×
881
            // Note: A single uploaded file may produce multiple datafiles -
882
            // for example, multiple files can be extracted from an uncompressed
883
            // zip file.
884
            dFileList = dataFileCreator.createDataFiles(inputStream, uploadedFile.getFileName(), uploadedFile.getContentType());
×
885
            dataFileUploadInfo.addSizeAndDataFiles(fileSize, dFileList);
×
NEW
886
        } catch (EJBException | IOException | FileExceedsMaxSizeException ex) {
×
887
            logger.warning("Failed to process and/or save the file " + uploadedFile.getFileName() + "; " + ex.getMessage());
×
888
            return;
×
889
        } catch (VirusFoundException e) {
×
890
            uploadWarningMessage = getStringFromBundle("dataset.file.uploadScannerWarning");
×
891
            uploadComponentId = event.getComponent().getClientId();
×
892
            return;
×
893
        }
×
894

895
        // These raw datafiles are then post-processed, in order to drop any files
896
        // already in the dataset/already uploaded, and to correct duplicate file names, etc.
897
        String warningMessage = processUploadedFileList(dFileList);
×
898

899
        if (warningMessage != null) {
×
900
            uploadWarningMessage = warningMessage;
×
901
            // save the component id of the p:upload widget, so that we could
902
            // send an info message there, from elsewhere in the code:
903
            uploadComponentId = event.getComponent().getClientId();
×
904
        }
905
        if (!uploadInProgress) {
×
906
            logger.warning("Upload in progress cancelled");
×
907
            for (DataFile newFile : dFileList) {
×
908
                deleteTempFile(newFile);
×
909
            }
×
910
        }
911
    }
×
912

913
    public boolean isTemporaryPreviewAvailable(String fileSystemId, String mimeType) {
914
        if (temporaryThumbnailsMap.get(fileSystemId) != null && !temporaryThumbnailsMap.get(fileSystemId).isEmpty()) {
×
915
            return true;
×
916
        }
917

918
        if ("".equals(temporaryThumbnailsMap.get(fileSystemId))) {
×
919
            // we've already looked once - and there's no thumbnail.
920
            return false;
×
921
        }
922

923
        String filesRootDirectory = systemConfig.getFilesDirectory();
×
924
        String fileSystemName = filesRootDirectory + "/temp/" + fileSystemId;
×
925
        String imageThumbFileName = fileSystemName + ".thumb" + ImageThumbConverter.DEFAULT_THUMBNAIL_SIZE;
×
926

927
        // ATTENTION! TODO: the current version of the method below may not be checking if files are already cached!
928
        if ("application/pdf".equals(mimeType)) {
×
929
            imageThumbConverter.generatePDFThumbnailFromFile(fileSystemName, ImageThumbConverter.DEFAULT_THUMBNAIL_SIZE, imageThumbFileName);
×
930
        } else if (mimeType != null && mimeType.startsWith("image/")) {
×
931
            imageThumbConverter.generateImageThumbnailFromFile(fileSystemName, ImageThumbConverter.DEFAULT_THUMBNAIL_SIZE, imageThumbFileName);
×
932
        }
933

934
        File imageThumbFile = new File(imageThumbFileName);
×
935
        if (imageThumbFile.exists()) {
×
936
            String previewAsBase64 = imageThumbConverter.getImageAsBase64FromFile(imageThumbFile);
×
937
            if (previewAsBase64 != null) {
×
938
                temporaryThumbnailsMap.put(fileSystemId, previewAsBase64);
×
939
                return true;
×
940
            } else {
941
                temporaryThumbnailsMap.put(fileSystemId, "");
×
942
            }
943
        }
944
        return false;
×
945
    }
946

947
    public String getTemporaryPreviewAsBase64(String fileSystemId) {
948
        return temporaryThumbnailsMap.get(fileSystemId);
×
949
    }
950

951
    public boolean isLocked() {
952
        if (dataset != null) {
×
953
            logger.log(Level.FINE, "checking lock status of dataset {0}", dataset.getId());
×
954
            Dataset lookedupDataset = datasetDao.find(dataset.getId());
×
955
            if (lookedupDataset != null && lookedupDataset.isLocked()) {
×
956
                logger.fine("locked!");
×
957
                return true;
×
958
            }
959
        }
960
        return false;
×
961
    }
962

963
    public boolean isThumbnailAvailable(FileMetadata fileMetadata) {
964
        // new and optimized logic:
965
        // - check download permission here (should be cached - so it's free!)
966
        // - only then ask the file service if the thumbnail is available/exists.
967
        // the service itself no longer checks download permissions.
968
        return fileDownloadHelper.canUserDownloadFile(fileMetadata)
×
969
                && datafileDao.isThumbnailAvailable(fileMetadata.getDataFile());
×
970
    }
971

972
    public boolean isLockedFromEdits() {
973
        if (null == lockedFromEditsVar) {
×
974
            try {
975
                permissionService.checkEditDatasetLock(dataset, dvRequestService.getDataverseRequest(),
×
976
                        new UpdateDatasetVersionCommand(dataset, dvRequestService.getDataverseRequest()));
×
977
                lockedFromEditsVar = false;
×
978
            } catch (IllegalCommandException ex) {
×
979
                lockedFromEditsVar = true;
×
980
            }
×
981
        }
982
        return lockedFromEditsVar;
×
983
    }
984

985
    // Methods for edit functions that are performed on one file at a time,
986
    // in popups that block the rest of the page:
987

988
    public boolean isDesignatedDatasetThumbnail(FileMetadata fileMetadata) {
989
        return fileMetadata != null && fileMetadata.getDataFile() != null && fileMetadata.getDataFile().getId() != null
×
990
                && fileMetadata.getDataFile().equals(dataset.getThumbnailFile());
×
991
    }
992

993
    /**
994
     * @todo For consistency, we should disallow users from setting the
995
     * thumbnail to a restricted file. We enforce this rule in the newer
996
     * workflow in dataset-widgets.xhtml. The logic to show the "Set Thumbnail"
997
     * button is in editFilesFragment.xhtml and it would be nice to move it to
998
     * Java since it's getting long and a bit complicated.
999
     */
1000
    public void setFileMetadataSelectedForThumbnailPopup(FileMetadata fm) {
1001
        fileMetadataSelectedForThumbnailPopup = fm;
×
1002
        alreadyDesignatedAsDatasetThumbnail = getUseAsDatasetThumbnail();
×
1003
    }
×
1004

1005
    public void clearFileMetadataSelectedForThumbnailPopup() {
1006
        fileMetadataSelectedForThumbnailPopup = null;
×
1007
    }
×
1008

1009
    public boolean getUseAsDatasetThumbnail() {
1010
        return isDesignatedDatasetThumbnail(fileMetadataSelectedForThumbnailPopup);
×
1011
    }
1012

1013
    public void setUseAsDatasetThumbnail(boolean useAsThumbnail) {
1014
        if (fileMetadataSelectedForThumbnailPopup != null && fileMetadataSelectedForThumbnailPopup.getDataFile() != null) {
×
1015
            if (useAsThumbnail) {
×
1016
                dataset.setThumbnailFile(fileMetadataSelectedForThumbnailPopup.getDataFile());
×
1017
            } else if (getUseAsDatasetThumbnail()) {
×
1018
                dataset.setThumbnailFile(null);
×
1019
            }
1020
        }
1021
    }
×
1022

1023
    public void saveAsDesignatedThumbnail() {
1024
        logger.fine("saving as the designated thumbnail");
×
1025
        // We don't need to do anything specific to save this setting, because
1026
        // the setUseAsDatasetThumbnail() method, above, has already updated the
1027
        // file object appropriately.
1028
        // However, once the "save" button is pressed, we want to show a success message, if this is
1029
        // a new image has been designated as such:
1030
        if (getUseAsDatasetThumbnail() && !alreadyDesignatedAsDatasetThumbnail) {
×
1031
            String successMessage = getStringFromBundle("file.assignedDataverseImage.success",
×
1032
                    fileMetadataSelectedForThumbnailPopup.getLabel());
×
1033
            logger.fine(successMessage);
×
1034
            JsfHelper.addFlashSuccessMessage(successMessage);
×
1035
        }
1036

1037
        // And reset the selected fileMetadata:
1038
        fileMetadataSelectedForThumbnailPopup = null;
×
1039
    }
×
1040

1041
    public void deleteDatasetLogoAndUseThisDataFileAsThumbnailInstead() {
1042
        logger.log(Level.FINE, "For dataset id {0} the current thumbnail is from a dataset logo rather than a dataset file, blowing away the logo and using this FileMetadata id instead: {1}", new Object[]{dataset.getId(), fileMetadataSelectedForThumbnailPopup});
×
1043

1044
        Try.of(() -> datasetService.changeDatasetThumbnail(dataset, fileMetadataSelectedForThumbnailPopup.getDataFile()))
×
1045
                .onFailure(ex -> logger.log(Level.SEVERE, "Problem setting thumbnail for dataset id " + dataset.getId(), ex))
×
1046
                .onSuccess(datasetThumbnail -> dataset = datasetDao.find(dataset.getId()));
×
1047
    }
×
1048

1049
    public boolean isThumbnailIsFromDatasetLogoRatherThanDatafile() {
1050
        DatasetThumbnail datasetThumbnail = datasetThumbnailService.getThumbnail(dataset);
×
1051
        return datasetThumbnail != null && !datasetThumbnail.isFromDataFile();
×
1052
    }
1053

1054
    public void refreshTagsPopUp(FileMetadata fm) {
1055
        setSelectedFile(fm);
×
1056
    }
×
1057

1058
    public void saveFileTagsAndCategories(FileMetadata selectedFile,
1059
                                          Collection<String> selectedFileMetadataTags,
1060
                                          Collection<String> selectedDataFileTags) {
1061
        selectedFile.getCategories().clear();
×
1062
        selectedFileMetadataTags.forEach(selectedFile::addCategoryByName);
×
1063
        setTagsForTabularData(selectedDataFileTags, selectedFile);
×
1064
    }
×
1065

1066
    public void clearFileMetadataSelectedForIngestOptionsPopup() {
1067
        fileMetadataSelectedForIngestOptionsPopup = null;
×
1068
    }
×
1069

1070
    public String getIngestLanguageEncoding() {
1071
        return ingestLanguageEncoding == null
×
1072
                ? getStringFromBundle("editdatafilepage.defaultLanguageEncoding")
×
1073
                : ingestLanguageEncoding;
1074
    }
1075

1076
    public void handleLabelsFileUpload(FileUploadEvent event) {
1077
        logger.fine("entering handleUpload method.");
×
1078
        UploadedFile file = event.getFile();
×
1079

1080
        if (file == null) {
×
1081
            return;
×
1082
        }
1083

1084
        InputStream uploadStream;
1085
        try {
1086
            uploadStream = file.getInputStream();
×
1087
        } catch (IOException ioe) {
×
1088
            logger.info("the file " + file.getFileName() + " failed to upload!");
×
1089

1090
            String msg = getStringFromBundle("dataset.file.uploadFailure.detailmsg", file.getFileName());
×
1091
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_WARN, getStringFromBundle("dataset.file.uploadFailure"), msg);
×
1092
            FacesContext.getCurrentInstance().addMessage(null, message);
×
1093
            return;
×
1094
        }
×
1095
        savedLabelsTempFile = saveTempFile(uploadStream);
×
1096
        logger.fine(file.getFileName() + " is successfully uploaded.");
×
1097
        FacesMessage message = new FacesMessage(getStringFromBundle("dataset.file.upload", file.getFileName()));
×
1098
        FacesContext.getCurrentInstance().addMessage(null, message);
×
1099
    }
×
1100

1101
    public void saveAdvancedOptions() {
1102
        // Language encoding for SPSS SAV (and, possibly, other tabular ingests:)
1103
        if (ingestLanguageEncoding != null && fileMetadataSelectedForIngestOptionsPopup != null
×
1104
                && fileMetadataSelectedForIngestOptionsPopup.getDataFile() != null) {
×
1105
            if (fileMetadataSelectedForIngestOptionsPopup.getDataFile().getIngestRequest() == null) {
×
1106
                IngestRequest ingestRequest = new IngestRequest();
×
1107
                ingestRequest.setDataFile(fileMetadataSelectedForIngestOptionsPopup.getDataFile());
×
1108
                fileMetadataSelectedForIngestOptionsPopup.getDataFile().setIngestRequest(ingestRequest);
×
1109

1110
            }
1111
            fileMetadataSelectedForIngestOptionsPopup.getDataFile().getIngestRequest().setTextEncoding(ingestLanguageEncoding);
×
1112
        }
1113
        ingestLanguageEncoding = null;
×
1114

1115
        // Extra labels for SPSS POR (and, possibly, other tabular ingests)
1116
        // (we are adding this parameter to the IngestRequest now, instead of back when it was uploaded. This is because
1117
        // we want the user to be able to hit cancel and bail out, until they actually click 'save' in the "advanced
1118
        // options" popup) -- L.A. 4.0 beta 11
1119
        if (savedLabelsTempFile != null && fileMetadataSelectedForIngestOptionsPopup != null
×
1120
                && fileMetadataSelectedForIngestOptionsPopup.getDataFile() != null) {
×
1121
            if (fileMetadataSelectedForIngestOptionsPopup.getDataFile().getIngestRequest() == null) {
×
1122
                IngestRequest ingestRequest = new IngestRequest();
×
1123
                ingestRequest.setDataFile(fileMetadataSelectedForIngestOptionsPopup.getDataFile());
×
1124
                fileMetadataSelectedForIngestOptionsPopup.getDataFile().setIngestRequest(ingestRequest);
×
1125
            }
1126
            fileMetadataSelectedForIngestOptionsPopup.getDataFile().getIngestRequest().setLabelsFile(savedLabelsTempFile);
×
1127
        }
1128
        savedLabelsTempFile = null;
×
1129
        fileMetadataSelectedForIngestOptionsPopup = null;
×
1130
    }
×
1131

1132
    public void updateTermsOfUseForSelectedFiles(TermsOfUseForm termsOfUseForm) {
1133
        for (FileMetadata selectedFile : selectedFiles) {
×
1134
            TermsOfUseForm termsOfUseCopy = new TermsOfUseForm();
×
1135
            termsOfUseCopy.setTypeWithLicenseId(termsOfUseForm.getTypeWithLicenseId());
×
1136
            termsOfUseCopy.setRestrictType(termsOfUseForm.getRestrictType());
×
1137
            termsOfUseCopy.setCustomRestrictText(termsOfUseForm.getCustomRestrictText());
×
1138
            selectedFile.setTermsOfUseForm(termsOfUseCopy);
×
1139
        }
×
1140
    }
×
1141

1142
    public void initDuplicatesDialog() {
1143
        duplicatesList = listDuplicates();
×
1144
    }
×
1145

1146
    public boolean hasDuplicatesSelected() {
1147
        return duplicatesList.stream()
×
1148
                .map(DuplicatesService.DuplicateGroup::getDuplicates)
×
1149
                .flatMap(Collection::stream)
×
1150
                .anyMatch(DuplicatesService.DuplicateItem::isSelected);
×
1151
    }
1152

1153
    public boolean hasDuplicatesWithWorkingVersion() {
1154
        return duplicatesList.stream()
×
1155
                .anyMatch(g -> !g.getExistingDuplicatesLabels().isEmpty());
×
1156
    }
1157

1158
    public void deleteSelectedDuplicates() {
1159
        selectedFiles = duplicatesList.stream()
×
1160
                .map(DuplicatesService.DuplicateGroup::getDuplicates)
×
1161
                .flatMap(Collection::stream)
×
1162
                .filter(DuplicatesService.DuplicateItem::isSelected)
×
1163
                .map(f -> f.getDataFile().getFileMetadata())
×
1164
                .collect(Collectors.toList());
×
1165
        deleteFiles();
×
1166
        duplicatesList = listDuplicates();
×
1167
        selectedFiles.clear();
×
1168

1169
        // We store the contents of the datatable and clear it.
1170
        // The reason is following: when we're removing a row it happens that contents of deleted row
1171
        // (ie. file label and description) are inserted into the next remaining row. So we store the data,
1172
        // destroy current table and then reconstruct it.
1173
        filesTableBackup.addAll(fileMetadatas);
×
1174
        fileMetadatas.clear();
×
1175
    }
×
1176

1177
    public void duplicatesDeletionFinished() {
1178
        // We reconstruct the contents of files table.
1179
        fileMetadatas.addAll(filesTableBackup);
×
1180
        filesTableBackup.clear();
×
1181
    }
×
1182

1183
    // -------------------- PRIVATE --------------------
1184

1185
    private List<DuplicatesService.DuplicateGroup> listDuplicates() {
1186
        return duplicatesService.listDuplicates(listExistingFiles(), newFiles);
×
1187
    }
1188

1189
    private boolean hasDuplicatesInUploadedFiles(List<DataFile> files) {
1190
        return duplicatesService.hasDuplicatesInUploadedFiles(listExistingFiles(), files);
×
1191
    }
1192

1193
    private List<DataFile> listExistingFiles() {
1194
        return workingVersion.getFileMetadatas().stream()
×
1195
                .map(FileMetadata::getDataFile)
×
1196
                .collect(Collectors.toList());
×
1197
    }
1198

1199
    private void updateCurrentBatchSizeForDeletedDataFile(DataFile dataFileToDelete) {
1200
        dataFileUploadInfo.removeFromDataFilesToSave(dataFileToDelete);
×
1201
        if (dataFileUploadInfo.canSubtractSize(dataFileToDelete)) {
×
1202
            currentBatchSize -= dataFileUploadInfo.getSourceFileSize(dataFileToDelete);
×
1203
        }
1204
    }
×
1205

1206
    private void cleanupTempFiles() {
1207
        final long purgeTime = System.currentTimeMillis() - TEMP_VALID_TIME_MILLIS;
×
1208
        final File tempDirectory = new File(FileUtil.getFilesTempDirectory());
×
1209
        if (!tempDirectory.exists() || !tempDirectory.isDirectory()) {
×
1210
            logger.warning("Failed to cleanup temporary file " + FileUtil.getFilesTempDirectory());
×
1211
        }
1212
        final File[] tempFilesList = tempDirectory.listFiles();
×
1213
        for (File tempFile : (tempFilesList != null ? tempFilesList : new File[0])) {
×
1214
            if (tempFile.isFile() && tempFile.lastModified() < purgeTime && !tempFile.delete()) {
×
1215
                logger.warning("Failed to delete temporary file " + tempFile.getName());
×
1216
            }
1217
        }
1218
    }
×
1219

1220
    private void deleteTempFile(DataFile dataFile) {
1221
        // Before we remove the file from the list and forget about it:
1222
        //
1223
        // The physical uploaded file is still sitting in the temporary directory. If it were saved, it would be moved
1224
        // into its permanent location. But since the user chose not to save it, we have to delete the temp file too.
1225
        //
1226
        // Eventually, we will likely add a dedicated mechanism for managing temp files, similar to (or part of) the
1227
        // storage access framework, that would allow us to handle specialized configurations - highly sensitive/private
1228
        // data, that has to be kept encrypted even in temp files, and such. But for now, we just delete the file
1229
        // directly on the local filesystem:
1230

1231
        try {
1232
            List<Path> generatedTempFiles = ingestService.listGeneratedTempFiles(
×
1233
                    Paths.get(FileUtil.getFilesTempDirectory()), dataFile.getStorageIdentifier());
×
1234
            if (generatedTempFiles != null) {
×
1235
                for (Path generated : generatedTempFiles) {
×
1236
                    logger.fine("(Deleting generated thumbnail file " + generated.toString() + ")");
×
1237
                    try {
1238
                        Files.delete(generated);
×
1239
                    } catch (IOException ioex) {
×
1240
                        logger.warning("Failed to delete generated file " + generated.toString());
×
1241
                    }
×
1242
                }
×
1243
            }
1244
            Files.delete(Paths.get(FileUtil.getFilesTempDirectory() + "/" + dataFile.getStorageIdentifier()));
×
1245
        } catch (IOException ioe) {
×
1246
            // safe to ignore - it's just a temp file.
1247
            logger.warning("Failed to delete temporary file " + FileUtil.getFilesTempDirectory() + "/"
×
1248
                    + dataFile.getStorageIdentifier());
×
1249
        }
×
1250
    }
×
1251

1252
    private void removeFileMetadataFromList(List<FileMetadata> metadatas, FileMetadata toDelete) {
1253
        Iterator<FileMetadata> iterator = metadatas.iterator();
×
1254
        while (iterator.hasNext()) {
×
1255
            FileMetadata fileMetadata = iterator.next();
×
1256
            if (toDelete.getDataFile().getStorageIdentifier().equals(fileMetadata.getDataFile().getStorageIdentifier())) {
×
1257
                iterator.remove();
×
1258
                break;
×
1259
            }
1260
        }
×
1261
    }
×
1262

1263
    private void removeDataFileFromList(List<DataFile> datafiles, DataFile toDelete) {
1264
        Iterator<DataFile> iterator = datafiles.iterator();
×
1265
        while (iterator.hasNext()) {
×
1266
            DataFile file = iterator.next();
×
1267
            if (toDelete.getStorageIdentifier().equals(file.getStorageIdentifier())) {
×
1268
                iterator.remove();
×
1269
                break;
×
1270
            }
1271
        }
×
1272
    }
×
1273

1274
    private void populateDatasetUpdateFailureMessage() {
1275
        JsfHelper.addErrorMessage(getStringFromBundle("dataset.message.filesFailure"), "");
×
1276
    }
×
1277

1278
    private String returnToDraftVersion() {
1279
        return "/dataset.xhtml?persistentId=" + dataset.getGlobalId().asString() + "&version=DRAFT&faces-redirect=true";
×
1280
    }
1281

1282
    /** Download a file from drop box */
1283
    private InputStream getDropBoxContent(GetMethod dropBoxMethod) throws IOException {
1284
        // Make http call, download the file:
1285
        int status;
1286

1287
        try {
1288
            HttpClient httpclient = new HttpClient();
×
1289
            status = httpclient.executeMethod(dropBoxMethod);
×
1290
            if (status != 200) {
×
1291
                logger.log(Level.WARNING, "Failed to get DropBox InputStream for file: {0}, status code: {1}",
×
1292
                        new Object[] {dropBoxMethod.getPath(), status});
×
1293
                throw new IOException("Non 200 status code returned from dropbox");
×
1294
            }
1295
            return dropBoxMethod.getResponseBodyAsStream();
×
1296
        } catch (IOException ex) {
×
1297
            logger.log(Level.WARNING, "Failed to access DropBox url: {0}!", dropBoxMethod.getPath());
×
1298
            throw ex;
×
1299
        }
1300
    }
1301

1302
    private void setUpRsync() {
1303
        logger.fine("setUpRsync called...");
×
1304
        if (DataCaptureModuleUtil.rsyncSupportEnabled(settingsService.getValueForKey(SettingsServiceBean.Key.UploadMethods))
×
1305
                && dataset.getFiles().isEmpty()) {
×
1306

1307
            Try<Option<RsyncInfo>> rsyncFetchOperation = Try.of(() -> fileService.retrieveRsyncScript(dataset, workingVersion))
×
1308
                    .onFailure(ex -> logger.log(Level.WARNING, "There was a problem with getting rsync script", ex));
×
1309

1310
            rsyncFetchOperation.onSuccess(this::setupScriptInfo);
×
1311
        }
1312
    }
×
1313

1314
    private void setupScriptInfo(Option<RsyncInfo> rsyncScript) {
1315
        rsyncScript.peek(rsyncInfo -> {
×
1316
            setRsyncScript(rsyncInfo.getRsyncScript());
×
1317
            rsyncScriptFilename = rsyncInfo.getRsyncScriptFileName();
×
1318
            setHasRsyncScript(true);
×
1319
        })
×
1320
                .onEmpty(() -> setHasRsyncScript(false));
×
1321
    }
×
1322

1323
    /**
1324
     * After uploading via the site or Dropbox,
1325
     * check the list of DataFile objects
1326
     */
1327
    private String processUploadedFileList(List<DataFile> dFileList) {
1328
        if (dFileList == null) {
×
1329
            return null;
×
1330
        }
1331

1332
        DataFile dataFile;
1333
        String warningMessage = null;
×
1334

1335
        // NOTE: for native file uploads, the dFileList will only
1336
        // contain 1 file--method is called for every file even if the UI shows "simultaneous uploads"
1337

1338
        // Iterate through list of DataFile objects
1339
        for (DataFile currentFile : dFileList) {
×
1340
            dataFile = currentFile;
×
1341
            // Check for ingest warnings
1342
            if (dataFile.isIngestProblem()) {
×
1343
                if (dataFile.getIngestReport() != null) {
×
1344
                    warningMessage = warningMessage == null
×
1345
                            ? dataFile.getIngestReport().getIngestReportMessage()
×
1346
                            : warningMessage.concat("; " + dataFile.getIngestReport().getIngestReportMessage());
×
1347
                }
1348
                dataFile.setIngestDone();
×
1349
            }
1350

1351
            // Let's check if filename is a duplicate of another
1352
            // file already uploaded, or already in the dataset:
1353
            dataFile.getFileMetadata().setLabel(createNewNameIfDuplicated(dataFile.getFileMetadata()));
×
1354
            if (isTemporaryPreviewAvailable(dataFile.getStorageIdentifier(), dataFile.getContentType())) {
×
1355
                dataFile.setPreviewImageAvailable(true);
×
1356
            }
1357
            uploadedFiles.add(dataFile);
×
1358
        }
×
1359

1360
        if (warningMessage != null) {
×
1361
            logger.severe(warningMessage);
×
1362
            return warningMessage;
×
1363
        }
1364
        return null;
×
1365
    }
1366

1367
    private String createNewNameIfDuplicated(FileMetadata fileMetadata) {
1368
        if (fileLabelsExisting == null) {
×
1369
            fileLabelsExisting = IngestUtil.existingPathNamesAsSet(workingVersion);
×
1370
        }
1371
        return IngestUtil.createNewNameIfDuplicated(fileMetadata, fileLabelsExisting);
×
1372
    }
1373

1374
    private void setTagsForTabularData(Collection<String> selectedDataFileTags, FileMetadata fileMetadata) {
1375
        fileMetadata.getDataFile().getTags().clear();
×
1376

1377
        selectedDataFileTags.forEach(selectedTag -> {
×
1378
            DataFileTag tag = new DataFileTag();
×
1379
            tag.setTypeByLabel(selectedTag);
×
1380
            tag.setDataFile(fileMetadata.getDataFile());
×
1381
            fileMetadata.getDataFile().addTag(tag);
×
1382
        });
×
1383
    }
×
1384

1385
    private String saveTempFile(InputStream input) {
1386
        if (input == null) {
×
1387
            return null;
×
1388
        }
1389
        byte[] buffer = new byte[8192];
×
1390
        int bytesRead;
1391
        File labelsFile;
1392
        FileOutputStream output = null;
×
1393
        try {
1394
            labelsFile = File.createTempFile("tempIngestLabels.", ".txt");
×
1395
            output = new FileOutputStream(labelsFile);
×
1396
            while ((bytesRead = input.read(buffer)) > -1) {
×
1397
                output.write(buffer, 0, bytesRead);
×
1398
            }
1399
        } catch (IOException ioex) {
×
1400
            return null;
×
1401
        } finally {
1402
            IOUtils.closeQuietly(input);
×
1403
            IOUtils.closeQuietly(output);
×
1404
        }
1405
        if (labelsFile != null) {
×
1406
            return labelsFile.getAbsolutePath();
×
1407
        }
1408
        return null;
×
1409
    }
1410

1411
    private void populateFileMetadatas(Set<Long> selectedFileIds) {
1412
        for (FileMetadata fileMetadata : workingVersion.getFileMetadatas()) {
×
1413
            Long fileId = fileMetadata.getDataFile().getId();
×
1414

1415
            if (selectedFileIds.contains(fileId)) {
×
1416
                logger.fine("Success! - found the file id " + fileId + " in the edit version.");
×
1417
                fileMetadatas.add(fileMetadata);
×
1418
                selectedFileIds.remove(fileId);
×
1419
            }
1420

1421
            if (selectedFileIds.isEmpty()) {
×
1422
                break;
×
1423
            }
1424
        }
×
1425
    }
×
1426

1427
    // -------------------- SETTERS --------------------
1428

1429
    public void setFileMetadatas(List<FileMetadata> fileMetadatas) {
1430
        this.fileMetadatas = fileMetadatas;
×
1431
    }
×
1432

1433
    public void setPersistentId(String persistentId) {
1434
        this.persistentId = persistentId;
×
1435
    }
×
1436

1437
    public void setDropBoxSelection(String dropBoxSelection) {
1438
        this.dropBoxSelection = dropBoxSelection;
×
1439
    }
×
1440

1441
    public void setDataset(Dataset dataset) {
1442
        this.dataset = dataset;
×
1443
    }
×
1444

1445
    public void setOwnerId(Long ownerId) {
1446
        this.ownerId = ownerId;
×
1447
    }
×
1448

1449
    public void setVersionId(Long versionId) {
1450
        this.versionId = versionId;
×
1451
    }
×
1452

1453
    public void setCurrentBatchSize(long currentBatchSize) {
1454
        this.currentBatchSize = currentBatchSize;
×
1455
    }
×
1456

1457
    public void setSelectedFiles(List<FileMetadata> selectedFiles) {
1458
        this.selectedFiles = selectedFiles;
×
1459
    }
×
1460

1461
    public void setVersionString(String versionString) {
1462
        this.versionString = versionString;
×
1463
    }
×
1464

1465
    public void setHasRsyncScript(Boolean hasRsyncScript) {
1466
        this.hasRsyncScript = hasRsyncScript;
×
1467
    }
×
1468

1469
    public void setRsyncScript(String rsyncScript) {
1470
        this.rsyncScript = rsyncScript;
×
1471
    }
×
1472

1473
    public void setWarningMessageForPopUp(String warningMessageForPopUp) {
1474
        this.warningMessageForPopUp = warningMessageForPopUp;
×
1475
    }
×
1476

1477
    public void setSelectedFile(FileMetadata fm) {
1478
        selectedFile = fm;
×
1479
    }
×
1480

1481
    public void setFileMetadataSelectedForIngestOptionsPopup(FileMetadata fm) {
1482
        fileMetadataSelectedForIngestOptionsPopup = fm;
×
1483
    }
×
1484

1485
    public void setIngestLanguageEncoding(String ingestLanguageEncoding) {
1486
        this.ingestLanguageEncoding = ingestLanguageEncoding;
×
1487
    }
×
1488

1489
    public void setIngestEncoding(String ingestEncoding) {
1490
        this.ingestLanguageEncoding = ingestEncoding;
×
1491
    }
×
1492
}
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

© 2026 Coveralls, Inc