Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ private void processGzipFile(List<String> filePaths, File file, Bitstream bitstr
if (fileName == null) {
logBitstreamNameIsNull();
} else {
if (fileName.toLowerCase().endsWith("tar.gz")) {
if (fileName.toLowerCase().endsWith(".tar.gz") || fileName.toLowerCase().endsWith(".tgz")) {
processTarGzipFile(filePaths, file, bitstream);
} else {
try (InputStream is = new GzipCompressorInputStream(new FileInputStream(file))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@

import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.dspace.authenticate.AuthenticationMethod;
import org.dspace.authenticate.factory.AuthenticateServiceFactory;
import org.dspace.authenticate.service.AuthenticationService;
import org.dspace.content.Bitstream;
import org.dspace.content.Bundle;
import org.dspace.content.Item;
import org.dspace.content.PreviewContent;
import org.dspace.content.factory.ContentServiceFactory;
import org.dspace.content.service.ItemService;
import org.dspace.content.service.PreviewContentService;
Expand All @@ -46,21 +44,19 @@ public class FilePreview extends DSpaceRunnable<FilePreviewConfiguration> {
ContentServiceFactory.getInstance().getPreviewContentService();
private EPersonService ePersonService = EPersonServiceFactory.getInstance()
.getEPersonService();
private AuthenticationService authenticateService = AuthenticateServiceFactory.getInstance()
.getAuthenticationService();

/**
* `-i`: Info, show help information.
*/
private boolean info = false;
private boolean force = false;

/**
* `-u`: UUID of the Item for which to create a preview of its bitstreams.
*/
private String specificItemUUID = null;

private String epersonMail = null;
private String epersonPassword = null;

@Override
public FilePreviewConfiguration getScriptConfiguration() {
Expand All @@ -84,11 +80,14 @@ public void setup() throws ParseException {
specificItemUUID);
}

if (commandLine.hasOption('f')) {
force = true;
}

epersonMail = commandLine.getOptionValue('e');
epersonPassword = commandLine.getOptionValue('p');

if (getEpersonIdentifier() == null && (epersonMail == null || epersonPassword == null)) {
throw new ParseException("Provide both -e/--email and -p/--password when no eperson is supplied.");
if (getEpersonIdentifier() == null && epersonMail == null) {
throw new ParseException("Provide -e/--email when no eperson is supplied.");
Comment thread
milanmajchrak marked this conversation as resolved.
}
}

Expand All @@ -101,7 +100,7 @@ public void internalRun() throws Exception {

Context context = new Context();
try {
context.setCurrentUser(getAuthenticatedEperson((context)));
context.setCurrentUser(getEperson(context));
handler.logInfo("Authentication by user: " + context.getCurrentUser().getEmail());
if (StringUtils.isNotBlank(specificItemUUID)) {
// Generate the preview only for a specific item
Expand Down Expand Up @@ -152,7 +151,17 @@ private void generateItemFilePreviews(Context context, UUID itemUUID) throws Exc
}
// Generate new content if we didn't find any
if (previewContentService.hasPreview(context, bitstream)) {
continue;
if (force) {
List<PreviewContent> previewContents = previewContentService
.findByBitstream(context, bitstream.getID());
for (PreviewContent content : previewContents) {
handler.logInfo("Deleting existing preview content: '" + content.getName() +
"', for bitstream: '" + bitstream.getName() + "'");
previewContentService.delete(context, content);
}
} else {
continue;
}
}

List<FileInfo> fileInfos = previewContentService.getFilePreviewContent(context, bitstream);
Expand All @@ -162,6 +171,7 @@ private void generateItemFilePreviews(Context context, UUID itemUUID) throws Exc
continue;
}

handler.logInfo("Generating file preview for bitstream: " + bitstream.getName());
for (FileInfo fi : fileInfos) {
previewContentService.createPreviewContent(context, bitstream, fi);
}
Expand All @@ -176,38 +186,30 @@ public void printHelp() {
"You can choose from these available options:\n" +
" -i, --info Show help information\n" +
" -u, --uuid The UUID of the ITEM for which to create a preview of its bitstreams\n" +
" -e, --email Email for authentication\n" +
" -p, --password Password for authentication\n");
" -f, --force Force to create preview, even when the preview exists\n" +
" -e, --email Email of the eperson to run the script as\n");

}

/**
* Retrieves an EPerson object either by its identifier or by performing an email-based lookup.
* It then authenticates the EPerson using the provided email and password.
* If the authentication is successful, it returns the EPerson object; otherwise,
* it throws an AuthenticationException.
* Resolves the EPerson the script runs as: the eperson supplied by the launching context
* (e.g. the logged-in user when started from the admin UI) if present, otherwise the eperson
* looked up by the {@code -e}/--email option. Like other CLI scripts, command-line invocation
* is trusted (shell access implies full server access), so no password is verified here;
* admin-only operations remain guarded by authorization checks in the service layer.
*
* @param context The Context object used for interacting with the DSpace database and service layer.
* @return The authenticated EPerson object corresponding to the provided email,
* if authentication is successful.
* @throws SQLException If a database error occurs while retrieving or interacting with the EPerson data.
* @throws AuthenticationException If no EPerson is found for the provided email
* or if the authentication fails.
* @return The EPerson the script should run as.
* @throws SQLException If a database error occurs while retrieving the EPerson data.
* @throws AuthenticationException If no EPerson is found for the provided email.
*/
private EPerson getAuthenticatedEperson(Context context) throws SQLException, AuthenticationException {
private EPerson getEperson(Context context) throws SQLException, AuthenticationException {
if (getEpersonIdentifier() != null) {
return ePersonService.find(context, getEpersonIdentifier());
}
Comment thread
milanmajchrak marked this conversation as resolved.
Comment thread
milanmajchrak marked this conversation as resolved.
String msg;
EPerson ePerson = ePersonService.findByEmail(context, epersonMail);
if (ePerson == null) {
msg = "No EPerson found for this email: " + epersonMail;
handler.logError(msg);
throw new AuthenticationException(msg);
}
int authenticated = authenticateService.authenticate(context, epersonMail, epersonPassword, null, null);
if (AuthenticationMethod.SUCCESS != authenticated) {
msg = "Authentication failed for email: " + epersonMail;
String msg = "No EPerson found for this email: " + epersonMail;
handler.logError(msg);
throw new AuthenticationException(msg);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,11 @@ public Options getOptions() {
options.getOption("u").setType(String.class);
options.getOption("u").setRequired(false);

options.addOption("f", "force", false, "Force to create preview, even when the preview exists.");

options.addOption("e", "email", true,
"Email for authentication.");
"Email of the eperson to run the script as.");
options.getOption("e").setType(String.class);
options.getOption("e").setRequired(true);

options.addOption("p", "password", true,
"Password for authentication.");
options.getOption("p").setType(String.class);
options.getOption("p").setRequired(true);

super.options = options;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,21 +104,11 @@ public void testUnauthorizedEmail() throws Exception {
assertEquals(1, run); // Since a ParseException was caught, expect return code 1
}
Comment thread
milanmajchrak marked this conversation as resolved.

@Test
public void testUnauthorizedPassword() throws Exception {
// Run the script
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "file-preview", "-e", ePerson.getEmail()};
int run = ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl),
testDSpaceRunnableHandler, kernelImpl);
assertEquals(1, run); // Since a ParseException was caught, expect return code 1
}

@Test
public void testWhenNoFilesRun() throws Exception {
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();

String[] args = new String[] { "file-preview", "-e", ePerson.getEmail(), "-p", PASSWORD };
String[] args = new String[] { "file-preview", "-e", ePerson.getEmail() };
int run = ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl),
testDSpaceRunnableHandler, kernelImpl);
assertEquals(0, run);
Expand All @@ -129,7 +119,8 @@ public void testWhenNoFilesRun() throws Exception {
public void testForSpecificItem() throws Exception {
Item item2 = createOtherWorkspaceItemWithBitstream(ePerson, 0);
// Run the script
runScriptForItemWithBitstreams(item2, ePerson, PASSWORD);
TestDSpaceRunnableHandler testHandler = runScriptForItemWithBitstreams(item2, ePerson);
checkHandlerMessages(testHandler, ePerson, item2, "logos.tgz", true);

Bitstream b = bitstreamService.findAll(context).stream()
.filter(bitstream -> bitstream.getName().equals("logos.tgz"))
Expand All @@ -147,7 +138,8 @@ public void testForSpecificItem() throws Exception {
public void testWhenScriptCannotCreateFilePreview() throws Exception {
Item item2 = createOtherWorkspaceItemWithBitstream(eperson, 0);
// Run the script as another user, without admin rights
runScriptForItemWithBitstreams(item2, ePerson, PASSWORD);
TestDSpaceRunnableHandler testHandler = runScriptForItemWithBitstreams(item2, ePerson);
checkHandlerMessages(testHandler, ePerson, item2, null, false);

Bitstream b = bitstreamService.findAll(context).stream()
.filter(bitstream -> bitstream.getName().equals("logos.tgz"))
Expand All @@ -161,7 +153,8 @@ public void testWhenScriptCannotCreateFilePreview() throws Exception {
assertFalse("Expects preview content not created.", previewContentService.hasPreview(context, b));

// Run the script as admin user
runScriptForItemWithBitstreams(item2, admin, password);
testHandler = runScriptForItemWithBitstreams(item2, admin);
checkHandlerMessages(testHandler, admin, item2, "logos.tgz", true);

// now the preview content was created since the script was run by admin user
assertTrue("Expects preview content created.", previewContentService.hasPreview(context, b));
Expand All @@ -173,7 +166,8 @@ public void testPreviewWithSyncStorage() throws Exception {
configurationService.setProperty("sync.storage.service.enabled", true);
Item item2 = createOtherWorkspaceItemWithBitstream(ePerson, SYNC_STORE_NUMBER);
// Run the script
runScriptForItemWithBitstreams(item2, ePerson, PASSWORD);
TestDSpaceRunnableHandler testHandler = runScriptForItemWithBitstreams(item2, ePerson);
checkHandlerMessages(testHandler, ePerson, item2, "logos.tgz", true);

Bitstream b = bitstreamService.findAll(context).stream()
.filter(bitstream -> bitstream.getStoreNumber() == SYNC_STORE_NUMBER)
Expand All @@ -188,37 +182,76 @@ public void testPreviewWithSyncStorage() throws Exception {
public void testForAllItem() throws Exception {
// Run the script
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "file-preview", "-e", ePerson.getEmail(), "-p", PASSWORD};
String[] args = new String[] { "file-preview", "-e", ePerson.getEmail()};
int run = ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl),
testDSpaceRunnableHandler, kernelImpl);
assertEquals(0, run);
// There should be no errors or warnings
checkNoError(testDSpaceRunnableHandler);
}

@Test
public void testPreviewWithForce() throws Exception {
Item item2 = createOtherWorkspaceItemWithBitstream(ePerson, 0);
// Run the script
TestDSpaceRunnableHandler testHandler1 = runScriptForItemWithBitstreams(item2, ePerson);
checkHandlerMessages(testHandler1, ePerson, item2, "logos.tgz", true);

// run again with force option, the existing preview content should be deleted and new one created
TestDSpaceRunnableHandler testHandler2 = new TestDSpaceRunnableHandler();
String[] args = new String[] { "file-preview", "-u", item2.getID().toString(),
"-e", admin.getEmail(), "-f"};
int run = ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl), testHandler2, kernelImpl);
assertEquals(0, run);
checkNoError(testHandler2);

List<String> messages = testHandler2.getInfoMessages();
assertThat(messages, hasSize(7));

assertThat(messages, hasItem(containsString("Deleting existing preview content:")));

Bitstream b = bitstreamService.findAll(context).stream()
.filter(bitstream -> bitstream.getName().equals("logos.tgz"))
.findFirst().orElse(null);

assertTrue("Expects preview content created.", previewContentService.hasPreview(context, b));
assertEquals(2, previewContentService.getPreview(context, b).size());
}

private void checkNoError(TestDSpaceRunnableHandler testDSpaceRunnableHandler) {
assertThat(testDSpaceRunnableHandler.getErrorMessages(), empty());
assertThat(testDSpaceRunnableHandler.getWarningMessages(), empty());
}

private void runScriptForItemWithBitstreams(Item item, EPerson user, String password) throws Exception {
private TestDSpaceRunnableHandler runScriptForItemWithBitstreams(Item item, EPerson user)
throws Exception {
// Run the script
TestDSpaceRunnableHandler testDSpaceRunnableHandler = new TestDSpaceRunnableHandler();
String[] args = new String[] { "file-preview", "-u", item.getID().toString(),
"-e", user.getEmail(), "-p", password};
"-e", user.getEmail()};
int run = ScriptLauncher.handleScript(args, ScriptLauncher.getConfig(kernelImpl),
testDSpaceRunnableHandler, kernelImpl);
assertEquals(0, run);
// There should be no errors or warnings
checkNoError(testDSpaceRunnableHandler);

// There should be an info message about generating the file previews for the specified item
return testDSpaceRunnableHandler;
}

private void checkHandlerMessages(TestDSpaceRunnableHandler testDSpaceRunnableHandler,
EPerson user,
Item item,
String fileName,
boolean previewGenerationExpected) {
List<String> messages = testDSpaceRunnableHandler.getInfoMessages();
assertThat(messages, hasSize(2));
assertThat(messages, hasSize(previewGenerationExpected ? 3 : 2));
assertThat(messages, hasItem(containsString("Generate the file previews for the specified item with " +
"the given UUID: " + item.getID())));
assertThat(messages,
hasItem(containsString("Authentication by user: " + user.getEmail())));
assertThat(messages, hasItem(containsString("Authentication by user: " + user.getEmail())));
if (previewGenerationExpected) {
// There should be an info message about generating the file previews for the specified bitstream
assertThat(messages, hasItem(containsString("Generating file preview for bitstream: " + fileName)));
}
}

private Item createOtherWorkspaceItemWithBitstream(EPerson user, int storageNumber) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public class PreviewContentServiceImplIT extends AbstractControllerIntegrationTe
Bitstream gzFile;
Bitstream tarXzFile;
Bitstream xzFile;
Bitstream tgzFileWithGzipMimeType;
Bitstream tarGzFileWithWrongExtension;
Bitstream tarXzFileWithIncorrectMimeType;

Expand Down Expand Up @@ -133,6 +134,15 @@ public void setup() throws SQLException, AuthorizeException, IOException {
.build();
}

try (InputStream is = getClass().getResourceAsStream("assetstore/logos.tgz")) {
tgzFileWithGzipMimeType = BitstreamBuilder.
createBitstream(context, bundle1, is)
.withName("logos.tgz")
.withDescription("tar.gz compressed file with tgz extension")
.withMimeType("application/x-gzip")
.build();
}

try (InputStream is = getClass().getResourceAsStream("assetstore/logos.tgz")) {
tgzFile = BitstreamBuilder.
createBitstream(context, bundle1, is)
Expand Down Expand Up @@ -235,18 +245,21 @@ public void destroy() throws Exception {
BitstreamBuilder.deleteBitstream(tarGzFile.getID());

BitstreamFormat customMimeTypeFormat = tarXGzipFile.getFormat(context);
BitstreamBuilder.deleteBitstream(tarXGzipFile.getID());
if (customMimeTypeFormat != null) {
bitstreamFormatService.delete(context, customMimeTypeFormat);
}

BitstreamBuilder.deleteBitstream(tarXGzipFile.getID());
BitstreamBuilder.deleteBitstream(tgzFile.getID());
BitstreamBuilder.deleteBitstream(gzFile.getID());
BitstreamBuilder.deleteBitstream(tarXzFile.getID());
BitstreamBuilder.deleteBitstream(xzFile.getID());
BitstreamBuilder.deleteBitstream(tgzFileWithGzipMimeType.getID());
BitstreamBuilder.deleteBitstream(tarGzFileWithWrongExtension.getID());
BitstreamBuilder.deleteBitstream(tarXzFileWithIncorrectMimeType.getID());

// removing custom mime type format created for tarXGzipFile and tgzFileWithGzipMimeType files
if (customMimeTypeFormat != null) {
bitstreamFormatService.delete(context, customMimeTypeFormat);
}

super.destroy();
}

Expand Down Expand Up @@ -338,6 +351,11 @@ public void testXzContent() throws Exception {
assertFileInfo(xzFile, "logos", 24);
}

@Test
public void testTgzContentWithGzipMimetype() throws Exception {
assertFileInfos(tgzFileWithGzipMimeType);
}

@Test
public void testGzContentForFileWithWrongExtension() throws Exception {
assertFileInfo(tarGzFileWithWrongExtension, "TAR GZ File", 24);
Expand Down
Loading