From 35e3587055274ede6deecd0295cda76664b71b48 Mon Sep 17 00:00:00 2001 From: Paurikova2 <107862249+Paurikova2@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:41:55 +0200 Subject: [PATCH] Internal/content preview (#691) * updated comments * added new databased entity preview content * not works: map fileinfo to preview content * get data from database if it exists * create preview content from file info * Failing tests - temp commit * changed name varchar length * added _ to sql script names --- .../org/dspace/content/PreviewContent.java | 155 ++++++++++++++++++ .../content/PreviewContentServiceImpl.java | 90 ++++++++++ .../dspace/content/dao/PreviewContentDAO.java | 44 +++++ .../dao/impl/PreviewContentDAOImpl.java | 55 +++++++ .../factory/ContentServiceFactory.java | 2 + .../factory/ContentServiceFactoryImpl.java | 8 + .../service/PreviewContentService.java | 98 +++++++++++ .../main/java/org/dspace/util/FileInfo.java | 8 + ...07.28__Upgrade_to_Lindat_Clarin_schema.sql | 2 +- .../V7.6_2024.08.05__Added_Preview_Tables.sql | 78 +++++++++ .../V7.6_2024.08.05__Added_Preview_Tables.sql | 88 ++++++++++ .../org/dspace/builder/AbstractBuilder.java | 4 + .../dspace/builder/PreviewContentBuilder.java | 97 +++++++++++ .../util/AbstractBuilderCleanupUtil.java | 2 + .../converter/PreviewContentConverter.java | 40 +++++ .../app/rest/model/PreviewContentRest.java | 70 ++++++++ .../model/hateoas/PreviewContentResource.java | 19 +++ .../MetadataBitstreamRestRepository.java | 79 ++++++++- .../PreviewContentRestRepository.java | 65 ++++++++ .../rest/PreviewContentRestRepositoryIT.java | 105 ++++++++++++ .../app/rest/PreviewContentServiceImplIT.java | 132 +++++++++++++++ dspace/config/clarin-dspace.cfg | 2 +- dspace/config/hibernate.cfg.xml | 1 + .../config/spring/api/core-dao-services.xml | 1 + dspace/config/spring/api/core-services.xml | 1 + 25 files changed, 1241 insertions(+), 5 deletions(-) create mode 100644 dspace-api/src/main/java/org/dspace/content/PreviewContent.java create mode 100644 dspace-api/src/main/java/org/dspace/content/PreviewContentServiceImpl.java create mode 100644 dspace-api/src/main/java/org/dspace/content/dao/PreviewContentDAO.java create mode 100644 dspace-api/src/main/java/org/dspace/content/dao/impl/PreviewContentDAOImpl.java create mode 100644 dspace-api/src/main/java/org/dspace/content/service/PreviewContentService.java create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.08.05__Added_Preview_Tables.sql create mode 100644 dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.08.05__Added_Preview_Tables.sql create mode 100644 dspace-api/src/test/java/org/dspace/builder/PreviewContentBuilder.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PreviewContentConverter.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PreviewContentRest.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/PreviewContentResource.java create mode 100644 dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PreviewContentRestRepository.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentRestRepositoryIT.java create mode 100644 dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentServiceImplIT.java diff --git a/dspace-api/src/main/java/org/dspace/content/PreviewContent.java b/dspace-api/src/main/java/org/dspace/content/PreviewContent.java new file mode 100644 index 000000000000..125f7309bd62 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/PreviewContent.java @@ -0,0 +1,155 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content; + +import java.util.Hashtable; +import java.util.Map; +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToOne; +import javax.persistence.MapKeyColumn; +import javax.persistence.OneToMany; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; + +import org.dspace.core.Context; +import org.dspace.core.ReloadableEntity; + +/** + * Database entity representation of the previewcontent table. + * It is not possible to create entity from FileInfo class (without modifications) + * so we created PreviewContent (which serves as an entity for FileInfo) + * with corresponding database table previewcontent. + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +@Entity +@Table(name = "previewcontent") +public class PreviewContent implements ReloadableEntity { + + @Id + @Column(name = "previewcontent_id") + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "previewcontent_previewcontent_id_seq") + @SequenceGenerator(name = "previewcontent_previewcontent_id_seq", + sequenceName = "previewcontent_previewcontent_id_seq", allocationSize = 1) + private Integer id; + + @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST}) + @JoinColumn(name = "bitstream_id") + private Bitstream bitstream; + + @Column(name = "name") + public String name; + + @Column(name = "content") + public String content; + + @Column(name = "isDirectory") + public boolean isDirectory; + + @Column(name = "size") + public String size; + + @OneToMany(cascade = CascadeType.ALL) + @JoinTable( + name = "preview2preview", + joinColumns = @JoinColumn(name = "parent_id"), + inverseJoinColumns = @JoinColumn(name = "child_id") + ) + @MapKeyColumn(name = "name") + public Map sub = new Hashtable<>(); + + /** + * Protected constructor. + */ + protected PreviewContent() {} + + /** + * Protected constructor, create object using: + * {@link org.dspace.content.service.PreviewContentService#create(Context, PreviewContent)} + */ + protected PreviewContent(PreviewContent previewContent) { + this.bitstream = previewContent.getBitstream(); + this.name = previewContent.getName(); + this.content = previewContent.getContent(); + this.isDirectory = previewContent.isDirectory(); + this.size = previewContent.getSize(); + this.sub = previewContent.getSubPreviewContents(); + } + + /** + * Protected constructor, create object using: + * {@link org.dspace.content.service.PreviewContentService#create(Context, Bitstream, String, String, boolean, + * String, Map)} + */ + protected PreviewContent(Bitstream bitstream, String name, String content, boolean isDirectory, String size, + Map subPreviewContents) { + this.bitstream = bitstream; + this.name = name; + this.content = content; + this.isDirectory = isDirectory; + this.size = size; + this.sub = subPreviewContents; + } + + @Override + public Integer getID() { + return id; + } + + public Bitstream getBitstream() { + return bitstream; + } + + public void setBitstream(Bitstream bitstream) { + this.bitstream = bitstream; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public boolean isDirectory() { + return isDirectory; + } + + public void setDirectory(boolean directory) { + isDirectory = directory; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + + public Map getSubPreviewContents() { + return sub; + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/PreviewContentServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/PreviewContentServiceImpl.java new file mode 100644 index 000000000000..2d528ca0a884 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/PreviewContentServiceImpl.java @@ -0,0 +1,90 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.content.dao.PreviewContentDAO; +import org.dspace.content.service.PreviewContentService; +import org.dspace.core.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Service implementation for the PreviewContent object. + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +public class PreviewContentServiceImpl implements PreviewContentService { + + /** + * logger + */ + private static final Logger log = LoggerFactory.getLogger(PreviewContentServiceImpl.class); + + + @Autowired + PreviewContentDAO previewContentDAO; + @Autowired(required = true) + AuthorizeService authorizeService; + + @Override + public PreviewContent create(Context context, Bitstream bitstream, String name, String content, + boolean isDirectory, String size, Map subPreviewContents) + throws SQLException { + //no authorization required! + // Create a table row + PreviewContent previewContent = previewContentDAO.create(context, new PreviewContent(bitstream, name, content, + isDirectory, size, subPreviewContents)); + log.info("Created new preview content of ID = {}", previewContent.getID()); + return previewContent; + } + + @Override + public PreviewContent create(Context context, PreviewContent previewContent) throws SQLException { + //no authorization required! + PreviewContent newPreviewContent = previewContentDAO.create(context, new PreviewContent(previewContent)); + log.info("Created new preview content of ID = {}", newPreviewContent.getID()); + return newPreviewContent; + } + + @Override + public void delete(Context context, PreviewContent previewContent) throws SQLException, AuthorizeException { + if (!authorizeService.isAdmin(context)) { + throw new AuthorizeException( + "You must be an admin to delete an CLARIN Content Preview"); + } + previewContentDAO.delete(context, previewContent); + } + + @Override + public PreviewContent find(Context context, int valueId) throws SQLException { + return previewContentDAO.findByID(context, PreviewContent.class, valueId); + } + + @Override + public List findByBitstream(Context context, UUID bitstreamId) throws SQLException { + return previewContentDAO.findByBitstream(context, bitstreamId); + } + + @Override + public List findRootByBitstream(Context context, UUID bitstreamId) throws SQLException { + return previewContentDAO.findRootByBitstream(context, bitstreamId); + } + + @Override + public List findAll(Context context) throws SQLException { + return previewContentDAO.findAll(context, PreviewContent.class); + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/dao/PreviewContentDAO.java b/dspace-api/src/main/java/org/dspace/content/dao/PreviewContentDAO.java new file mode 100644 index 000000000000..9abc1e732b74 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/dao/PreviewContentDAO.java @@ -0,0 +1,44 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.dao; + +import java.sql.SQLException; +import java.util.List; +import java.util.UUID; + +import org.dspace.content.PreviewContent; +import org.dspace.core.Context; +import org.dspace.core.GenericDAO; + +/** + * Database Access Object interface class for the PreviewContent object. + * This class should only be accessed from a single service and should never be exposed outside of the API + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +public interface PreviewContentDAO extends GenericDAO { + /** + * Find all preview content based on ID of bitstream the preview content is added to. + * + * @param context DSpace context + * @param bitstreamId The bitstream ID + * @return List of found preview content + * @throws SQLException If a database error occurs + */ + List findByBitstream(Context context, UUID bitstreamId) throws SQLException; + + /** + * Find all preview content based on bitstream that are the root directory. + * + * @param context DSpace context + * @param bitstreamId The bitstream ID + * @return List of found preview content + * @throws SQLException If a database error occurs + */ + List findRootByBitstream(Context context, UUID bitstreamId) throws SQLException; +} diff --git a/dspace-api/src/main/java/org/dspace/content/dao/impl/PreviewContentDAOImpl.java b/dspace-api/src/main/java/org/dspace/content/dao/impl/PreviewContentDAOImpl.java new file mode 100644 index 000000000000..9e1cc3dc0593 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/dao/impl/PreviewContentDAOImpl.java @@ -0,0 +1,55 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.dao.impl; + +import java.sql.SQLException; +import java.util.List; +import java.util.UUID; +import javax.persistence.Query; + +import org.dspace.content.PreviewContent; +import org.dspace.content.dao.PreviewContentDAO; +import org.dspace.core.AbstractHibernateDAO; +import org.dspace.core.Context; + +/** + * Hibernate implementation of the Database Access Object interface class for the PreviewContent object. + * This class should never be accessed directly. + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +public class PreviewContentDAOImpl extends AbstractHibernateDAO implements PreviewContentDAO { + + protected PreviewContentDAOImpl() { + super(); + } + + @Override + public List findByBitstream(Context context, UUID bitstreamId) throws SQLException { + Query query = createQuery(context, "SELECT pc FROM " + PreviewContent.class.getSimpleName() + + " as pc join pc.bitstream as b WHERE b.id = :bitstream_id"); + query.setParameter("bitstream_id", bitstreamId); + query.setHint("org.hibernate.cacheable", Boolean.TRUE); + return findMany(context, query); + } + + @Override + public List findRootByBitstream(Context context, UUID bitstreamId) throws SQLException { + // select only data from the previewcontent table whose ID is not a child in the preview2preview table + Query query = createQuery(context, + "SELECT pc FROM " + PreviewContent.class.getSimpleName() + " pc " + + "JOIN pc.bitstream b " + + "WHERE b.id = :bitstream_id " + + "AND pc.id NOT IN (SELECT child.id FROM " + PreviewContent.class.getSimpleName() + " parent " + + "JOIN parent.sub child)" + ); + query.setParameter("bitstream_id", bitstreamId); + query.setHint("org.hibernate.cacheable", Boolean.TRUE); + return findMany(context, query); + } +} diff --git a/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactory.java b/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactory.java index 53749de86352..dbe842a4194f 100644 --- a/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactory.java +++ b/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactory.java @@ -29,6 +29,7 @@ import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; import org.dspace.content.service.MetadataValueService; +import org.dspace.content.service.PreviewContentService; import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.content.service.SiteService; @@ -76,6 +77,7 @@ public abstract class ContentServiceFactory { public abstract SiteService getSiteService(); public abstract SubscribeService getSubscribeService(); + public abstract PreviewContentService getPreviewContentService(); /** * Return the implementation of the RelationshipTypeService interface diff --git a/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactoryImpl.java b/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactoryImpl.java index e4cd23988954..a38dec0c0a9d 100644 --- a/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactoryImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/factory/ContentServiceFactoryImpl.java @@ -26,6 +26,7 @@ import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; import org.dspace.content.service.MetadataValueService; +import org.dspace.content.service.PreviewContentService; import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.content.service.SiteService; @@ -83,6 +84,8 @@ public class ContentServiceFactoryImpl extends ContentServiceFactory { private EntityTypeService entityTypeService; @Autowired(required = true) private EntityService entityService; + @Autowired(required = true) + private PreviewContentService previewContentService; @Autowired(required = true) private DspaceObjectClarinService dspaceObjectClarinService; @@ -165,6 +168,11 @@ public SubscribeService getSubscribeService() { return subscribeService ; } + @Override + public PreviewContentService getPreviewContentService() { + return previewContentService; + } + @Override public RelationshipTypeService getRelationshipTypeService() { return relationshipTypeService; diff --git a/dspace-api/src/main/java/org/dspace/content/service/PreviewContentService.java b/dspace-api/src/main/java/org/dspace/content/service/PreviewContentService.java new file mode 100644 index 000000000000..8ecf7066e2b4 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/content/service/PreviewContentService.java @@ -0,0 +1,98 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.content.service; + +import java.sql.SQLException; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Bitstream; +import org.dspace.content.PreviewContent; +import org.dspace.core.Context; + +/** + * Service interface class for the PreviewContent object. + * + * @author Michaela Paurikova (dspace at dataquest.sk) + */ +public interface PreviewContentService { + + /** + * Create a new preview content in the database. + * + * @param context DSpace context + * @param bitstream The bitstream to create a preview content for + * @param name The name of preview content + * @param content The content of preview content + * @param isDirectory True if preview content is directory else false + * @param size The size of preview content + * @param subPreviewContents The sub preview contents of preview content + * @return The newly created preview content + * @throws SQLException If a database error occurs + */ + PreviewContent create(Context context, Bitstream bitstream, String name, String content, + boolean isDirectory, String size, Map subPreviewContents) + throws SQLException; + + /** + * Create a new preview content in the database. + * + * @param context DSpace context + * @param previewContent The preview content + * @return The newly created preview content + * @throws SQLException If a database error occurs + */ + PreviewContent create(Context context, PreviewContent previewContent) throws SQLException; + + /** + * Delete a preview content from the database. + * + * @param context DSpace context + * @param previewContent Deleted preview content + * @throws SQLException If a database error occurs + * @throws AuthorizeException If a user is not authorized + */ + void delete(Context context, PreviewContent previewContent) throws SQLException, AuthorizeException; + + /** + * Find preview content based on ID. + * + * @param context DSpace context + * @param valueId The ID of the preview content to search for + * @throws SQLException If a database error occurs + */ + PreviewContent find(Context context, int valueId) throws SQLException; + + /** + * Find all preview content based on bitstream. + * + * @param context DSpace context + * @param bitstream_id The ID of the bitstream + * @throws SQLException If a database error occurs + */ + List findByBitstream(Context context, UUID bitstream_id) throws SQLException; + + /** + * Find all preview content based on bitstream that are the root directory. + * + * @param context DSpace context + * @param bitstream_id The ID of the bitstream + * @throws SQLException If a database error occurs + */ + List findRootByBitstream(Context context, UUID bitstream_id) throws SQLException; + + /** + * Find all preview contents from database. + * + * @param context DSpace context + * @throws SQLException If a database error occurs + */ + List findAll(Context context) throws SQLException; +} diff --git a/dspace-api/src/main/java/org/dspace/util/FileInfo.java b/dspace-api/src/main/java/org/dspace/util/FileInfo.java index 3a637e2255e9..fa9e75a06f6e 100644 --- a/dspace-api/src/main/java/org/dspace/util/FileInfo.java +++ b/dspace-api/src/main/java/org/dspace/util/FileInfo.java @@ -22,6 +22,14 @@ public class FileInfo { public Hashtable sub = null; + public FileInfo(String name, String content, String size, boolean isDirectory, Hashtable sub) { + this.name = name; + this.content = content; + this.size = size; + this.isDirectory = isDirectory; + this.sub = sub; + } + public FileInfo(String name) { this.name = name; sub = new Hashtable(); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.2_2022.07.28__Upgrade_to_Lindat_Clarin_schema.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.2_2022.07.28__Upgrade_to_Lindat_Clarin_schema.sql index 6c433443a8ba..529577b1b800 100644 --- a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.2_2022.07.28__Upgrade_to_Lindat_Clarin_schema.sql +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.2_2022.07.28__Upgrade_to_Lindat_Clarin_schema.sql @@ -410,4 +410,4 @@ ALTER TABLE handle ALTER TABLE eperson ADD welcome_info varchar(30); -ALTER TABLE eperson ADD can_edit_submission_metadata BOOL; \ No newline at end of file +ALTER TABLE eperson ADD can_edit_submission_metadata BOOL; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.08.05__Added_Preview_Tables.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.08.05__Added_Preview_Tables.sql new file mode 100644 index 000000000000..068f80f9430a --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.6_2024.08.05__Added_Preview_Tables.sql @@ -0,0 +1,78 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +-- +-- Name: previewcontent; Type: TABLE; Schema: public; Owner: dspace; Tablespace: +-- + +CREATE TABLE previewcontent ( + previewcontent_id integer NOT NULL, + bitstream_id uuid NOT NULL, + name varchar(2000), + content varchar(2000), + isDirectory boolean DEFAULT false, + size varchar(256) +); + +-- +-- Name: previewcontent_previewcontent_id_seq; Type: SEQUENCE; Schema: public; Owner: dspace +-- + +CREATE SEQUENCE previewcontent_previewcontent_id_seq + START WITH 1 + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + +ALTER TABLE previewcontent ALTER COLUMN previewcontent_id SET DEFAULT nextval('previewcontent_previewcontent_id_seq'); + +-- +-- Name: previewcontent_pkey; Type: CONSTRAINT; Schema: public; Owner: dspace; Tablespace: +-- + +ALTER TABLE previewcontent + ADD CONSTRAINT previewcontent_pkey PRIMARY KEY (previewcontent_id); + +-- +-- Name: previewcontent_bitstream_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE previewcontent + ADD CONSTRAINT previewcontent_bitstream_fk FOREIGN KEY (bitstream_id) REFERENCES bitstream(uuid) ON DELETE CASCADE; + +-- +-- Name: preview2preview; Type: TABLE; Schema: public; Owner: dspace; Tablespace: +-- + +CREATE TABLE preview2preview ( + parent_id integer NOT NULL, + child_id integer NOT NULL, + name varchar(2000) +); + +-- +-- Name: preview2preview_pkey; Type: CONSTRAINT; Schema: public; Owner: dspace; Tablespace: +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_pkey PRIMARY KEY (parent_id, child_id); + +-- +-- Name: preview2preview_parent_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_parent_fk FOREIGN KEY (parent_id) REFERENCES previewcontent(previewcontent_id) ON DELETE CASCADE; + +-- +-- Name: preview2preview_child_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_child_fk FOREIGN KEY (child_id) REFERENCES previewcontent(previewcontent_id) ON DELETE CASCADE; diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.08.05__Added_Preview_Tables.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.08.05__Added_Preview_Tables.sql new file mode 100644 index 000000000000..57919fbfa8e6 --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.6_2024.08.05__Added_Preview_Tables.sql @@ -0,0 +1,88 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +-- +-- Name: previewcontent; Type: TABLE; Schema: public; Owner: dspace; Tablespace: +-- + +CREATE TABLE previewcontent ( + previewcontent_id integer NOT NULL, + bitstream_id uuid NOT NULL, + name varchar(2000), + content varchar(2000), + isDirectory boolean DEFAULT false, + size varchar(256) +); + +ALTER TABLE public.previewcontent OWNER TO dspace; + +-- +-- Name: previewcontent_previewcontent_id_seq; Type: SEQUENCE; Schema: public; Owner: dspace +-- + +CREATE SEQUENCE previewcontent_previewcontent_id_seq + START WITH 1 + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + +ALTER TABLE public.previewcontent_previewcontent_id_seq OWNER TO dspace; + +-- +-- Name: previewcontent_previewcontent_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: dspace +-- + +ALTER SEQUENCE previewcontent_previewcontent_id_seq OWNED BY previewcontent.previewcontent_id; + +-- +-- Name: previewcontent_pkey; Type: CONSTRAINT; Schema: public; Owner: dspace; Tablespace: +-- + +ALTER TABLE ONLY previewcontent + ADD CONSTRAINT previewcontent_pkey PRIMARY KEY (previewcontent_id); + +-- +-- Name: previewcontent_bitstream_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE ONLY previewcontent + ADD CONSTRAINT previewcontent_bitstream_fk FOREIGN KEY (bitstream_id) REFERENCES bitstream(uuid) ON DELETE CASCADE; + +-- +-- Name: preview2preview; Type: TABLE; Schema: public; Owner: dspace; Tablespace: +-- + +CREATE TABLE preview2preview ( + parent_id integer NOT NULL, + child_id integer NOT NULL, + name varchar(2000) +); + +ALTER TABLE public.preview2preview OWNER TO dspace; + +-- +-- Name: preview2preview_pkey; Type: CONSTRAINT; Schema: public; Owner: dspace; Tablespace: +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_pkey PRIMARY KEY (parent_id, child_id); + +-- +-- Name: preview2preview_parent_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_parent_fk FOREIGN KEY (parent_id) REFERENCES previewcontent(previewcontent_id) ON DELETE CASCADE; + +-- +-- Name: preview2preview_child_fk; Type: FK CONSTRAINT; Schema: public; Owner: dspace +-- + +ALTER TABLE preview2preview + ADD CONSTRAINT preview2preview_child_fk FOREIGN KEY (child_id) REFERENCES previewcontent(previewcontent_id) ON DELETE CASCADE; diff --git a/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java b/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java index ee94ace18218..1eee780fbbbd 100644 --- a/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java +++ b/dspace-api/src/test/java/org/dspace/builder/AbstractBuilder.java @@ -35,6 +35,7 @@ import org.dspace.content.service.ItemService; import org.dspace.content.service.MetadataFieldService; import org.dspace.content.service.MetadataSchemaService; +import org.dspace.content.service.PreviewContentService; import org.dspace.content.service.RelationshipService; import org.dspace.content.service.RelationshipTypeService; import org.dspace.content.service.SiteService; @@ -132,6 +133,7 @@ public abstract class AbstractBuilder { static ClarinUserMetadataService clarinUserMetadataService; static ClarinLicenseResourceUserAllowanceService clarinLicenseResourceUserAllowanceService; static CreativeCommonsService creativeCommonsService; + static PreviewContentService previewContentService; @@ -184,6 +186,7 @@ public static void init() { requestItemService = RequestItemServiceFactory.getInstance().getRequestItemService(); versioningService = DSpaceServicesFactory.getInstance().getServiceManager() .getServiceByName(VersioningService.class.getName(), VersioningService.class); + previewContentService = ContentServiceFactory.getInstance().getPreviewContentService(); // Temporarily disabled claimedTaskService = XmlWorkflowServiceFactory.getInstance().getClaimedTaskService(); @@ -259,6 +262,7 @@ public static void destroy() { subscribeService = null; supervisionOrderService = null; submissionConfigService = null; + previewContentService = null; } diff --git a/dspace-api/src/test/java/org/dspace/builder/PreviewContentBuilder.java b/dspace-api/src/test/java/org/dspace/builder/PreviewContentBuilder.java new file mode 100644 index 000000000000..87b09d4f4bab --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/builder/PreviewContentBuilder.java @@ -0,0 +1,97 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.builder; + +import java.sql.SQLException; +import java.util.Map; +import java.util.Objects; + +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.Bitstream; +import org.dspace.content.PreviewContent; +import org.dspace.content.service.PreviewContentService; +import org.dspace.core.Context; + +public class PreviewContentBuilder extends AbstractBuilder { + + private PreviewContent previewContent; + + protected PreviewContentBuilder(Context context) { + super(context); + } + + public static PreviewContentBuilder createPreviewContent(final Context context, Bitstream bitstream, String name, + String content, boolean isDirectory, String size, + Map subPreviewContents) { + PreviewContentBuilder builder = new PreviewContentBuilder(context); + return builder.create(context, bitstream, name, content, isDirectory, size, subPreviewContents); + } + + private PreviewContentBuilder create(final Context context, Bitstream bitstream, String name, String content, + boolean isDirectory, String size, + Map subPreviewContents) { + this.context = context; + try { + previewContent = previewContentService.create(context, bitstream, name, content, + isDirectory, size, subPreviewContents); + } catch (Exception e) { + return handleException(e); + } + return this; + } + + public static void deletePreviewContent(Integer id) throws Exception { + if (Objects.isNull(id)) { + return; + } + try (Context c = new Context()) { + c.turnOffAuthorisationSystem(); + PreviewContent previewContent = previewContentService.find(c, id); + + if (previewContent != null) { + previewContentService.delete(c, previewContent); + } + c.complete(); + } + } + + @Override + public void cleanup() throws Exception { + try (Context c = new Context()) { + c.turnOffAuthorisationSystem(); + // Ensure object and any related objects are reloaded before checking to see what needs cleanup + previewContent = c.reloadEntity(previewContent); + delete(c, previewContent); + c.complete(); + indexingService.commit(); + } + } + + @Override + public PreviewContent build() throws SQLException, AuthorizeException { + try { + context.dispatchEvents(); + indexingService.commit(); + return previewContent; + } catch (Exception e) { + return handleException(e); + } + } + + @Override + public void delete(Context c, PreviewContent dso) throws Exception { + if (dso != null) { + getService().delete(c, dso); + } + } + + @Override + protected PreviewContentService getService() { + return previewContentService; + } +} diff --git a/dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java b/dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java index 6a8daa432eb6..7ff2ff720017 100644 --- a/dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java +++ b/dspace-api/src/test/java/org/dspace/builder/util/AbstractBuilderCleanupUtil.java @@ -29,6 +29,7 @@ import org.dspace.builder.OrcidQueueBuilder; import org.dspace.builder.OrcidTokenBuilder; import org.dspace.builder.PoolTaskBuilder; +import org.dspace.builder.PreviewContentBuilder; import org.dspace.builder.ProcessBuilder; import org.dspace.builder.RelationshipBuilder; import org.dspace.builder.RelationshipTypeBuilder; @@ -83,6 +84,7 @@ private void initMap() { map.put(MetadataSchemaBuilder.class.getName(), new ArrayList<>()); map.put(SiteBuilder.class.getName(), new ArrayList<>()); map.put(ProcessBuilder.class.getName(), new ArrayList<>()); + map.put(PreviewContentBuilder.class.getName(), new ArrayList<>()); } /** diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PreviewContentConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PreviewContentConverter.java new file mode 100644 index 000000000000..5d89391ddd47 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/PreviewContentConverter.java @@ -0,0 +1,40 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.converter; + +import org.dspace.app.rest.model.PreviewContentRest; +import org.dspace.app.rest.projection.Projection; +import org.dspace.content.PreviewContent; +import org.springframework.stereotype.Component; + +/** + * This is the converter from/to the PreviewContent in the DSpace API data model and the + * REST data model + * + * @author Michaela Paurikova (michaela.paurikova at dataquest.sk) + */ +@Component +public class PreviewContentConverter implements DSpaceConverter { + + @Override + public PreviewContentRest convert(PreviewContent modelObject, Projection projection) { + PreviewContentRest previewContentRest = new PreviewContentRest(); + previewContentRest.setProjection(projection); + previewContentRest.setId(modelObject.getID()); + previewContentRest.setContent(modelObject.getContent()); + previewContentRest.setName(modelObject.getName()); + previewContentRest.setSize(modelObject.getSize()); + previewContentRest.setDirectory(modelObject.isDirectory()); + return previewContentRest; + } + + @Override + public Class getModelClass() { + return PreviewContent.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PreviewContentRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PreviewContentRest.java new file mode 100644 index 000000000000..410c3b941169 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/PreviewContentRest.java @@ -0,0 +1,70 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.model; + +import org.dspace.app.rest.RestResourceController; + +public class PreviewContentRest extends BaseObjectRest { + public static final String NAME = "previewContent"; + public static final String CATEGORY = RestAddressableModel.CORE; + + private String name; + private String content; + private boolean isDirectory; + private String size; + + public PreviewContentRest() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public boolean isDirectory() { + return isDirectory; + } + + public void setDirectory(boolean directory) { + isDirectory = directory; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } + + @Override + public String getCategory() { + return CATEGORY; + } + + @Override + public Class getController() { + return RestResourceController.class; + } + + @Override + public String getType() { + return NAME; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/PreviewContentResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/PreviewContentResource.java new file mode 100644 index 000000000000..0dca4edbc6fb --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/PreviewContentResource.java @@ -0,0 +1,19 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.model.hateoas; + +import org.dspace.app.rest.model.PreviewContentRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +@RelNameDSpaceResource(PreviewContentRest.NAME) +public class PreviewContentResource extends DSpaceResource { + public PreviewContentResource(PreviewContentRest ms, Utils utils) { + super(ms, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java index 4778cec279e3..aebb260cd438 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/MetadataBitstreamRestRepository.java @@ -20,9 +20,12 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Hashtable; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; @@ -51,9 +54,11 @@ import org.dspace.content.Bundle; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; +import org.dspace.content.PreviewContent; import org.dspace.content.Thumbnail; import org.dspace.content.service.BitstreamService; import org.dspace.content.service.ItemService; +import org.dspace.content.service.PreviewContentService; import org.dspace.content.service.clarin.ClarinLicenseResourceMappingService; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -105,6 +110,9 @@ public class MetadataBitstreamRestRepository extends DSpaceRestRepository findByHandle(@Parameter(value = "handl List fileInfos = new ArrayList<>(); boolean canPreview = findOutCanPreview(context, bitstream); if (canPreview) { - fileInfos = getFilePreviewContent(context, bitstream, fileInfos); + List prContents = previewContentService.findRootByBitstream(context, + bitstream.getID()); + // Generate new content if we didn't find any + if (prContents.isEmpty()) { + fileInfos = getFilePreviewContent(context, bitstream, fileInfos); + for (FileInfo fi : fileInfos) { + createPreviewContent(context, bitstream, fi); + } + context.commit(); + } else { + for (PreviewContent pc : prContents) { + fileInfos.add(createFileInfo(pc)); + } + } } MetadataBitstreamWrapper bts = new MetadataBitstreamWrapper(bitstream, fileInfos, bitstream.getFormat(context).getMIMEType(), @@ -220,6 +241,58 @@ private List getFilePreviewContent(Context context, Bitstream bitstrea return fileInfos; } + /** + * Define the hierarchy organization for preview content and file info. + * The hierarchy is established by the sub map. + * If a content item contains a sub map, it is considered a directory; if not, it is a file. + * @param sourceMap sub map that is used as a pattern + * @param creator creator function + * @return created sub map + */ + private Hashtable createSubMap(Map sourceMap, Function creator) { + if (sourceMap == null) { + return null; + } + + Hashtable sub = new Hashtable<>(); + for (Map.Entry entry : sourceMap.entrySet()) { + sub.put(entry.getKey(), creator.apply(entry.getValue())); + } + return sub; + } + + /** + * Create file info from preview content. + * @param pc preview content + * @return created file info + */ + private FileInfo createFileInfo(PreviewContent pc) { + Hashtable sub = createSubMap(pc.sub, this::createFileInfo); + return new FileInfo(pc.name, pc.content, pc.size, pc.isDirectory, sub); + } + + /** + * Create preview content from file info for bitstream. + * @param context DSpace context object + * @param bitstream bitstream + * @param fi file info + * @return created preview content + * @throws SQLException If database error is occurred + */ + private PreviewContent createPreviewContent(Context context, Bitstream bitstream, FileInfo fi) throws SQLException { + Hashtable sub = createSubMap(fi.sub, value -> { + try { + return createPreviewContent(context, bitstream, value); + } catch (SQLException e) { + String msg = "Database error occurred while creating new preview content " + + "for bitstream with ID = " + bitstream.getID() + " Error msg: " + e.getMessage(); + log.error(msg, e); + throw new RuntimeException(msg, e); + } + }); + return previewContentService.create(context, bitstream, fi.name, fi.content, fi.isDirectory, fi.size, sub); + } + /** * Convert InputStream of the ZIP file into FileInfo classes. * @@ -243,10 +316,10 @@ private List processInputStreamToFilePreview(Context context, Bitstrea fileInfos.add(new FileInfo(data, false)); } else { String data = ""; - if (bitstream.getFormat(context).getExtensions().contains("zip")) { + if (bitstream.getFormat(context).getMIMEType().equals("application/zip")) { data = extractFile(inputStream, "zip"); fileInfos = FileTreeViewGenerator.parse(data); - } else if (bitstream.getFormat(context).getExtensions().contains("tar")) { + } else if (bitstream.getFormat(context).getMIMEType().equals("application/x-tar")) { ArchiveInputStream is = new ArchiveStreamFactory().createArchiveInputStream(ArchiveStreamFactory.TAR, inputStream); data = extractFile(is, "tar"); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PreviewContentRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PreviewContentRestRepository.java new file mode 100644 index 000000000000..3fc352ec4f5d --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/PreviewContentRestRepository.java @@ -0,0 +1,65 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; + +import org.apache.logging.log4j.Logger; +import org.dspace.app.rest.model.PreviewContentRest; +import org.dspace.content.PreviewContent; +import org.dspace.content.service.PreviewContentService; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; + +@Component(PreviewContentRest.CATEGORY + "." + PreviewContentRest.NAME) +public class PreviewContentRestRepository extends DSpaceRestRepository { + + private static Logger log = org.apache.logging.log4j.LogManager.getLogger(PreviewContentRestRepository.class); + + @Autowired + PreviewContentService previewContentService; + + @Override + public PreviewContentRest findOne(Context context, Integer integer) { + PreviewContent previewContent; + try { + previewContent = previewContentService.find(context, integer); + } catch (SQLException e) { + String msg = "Database error occurred while finding preview content " + + "for ID = " + integer + " Error msg: " + e.getMessage(); + log.error(msg, e); + throw new RuntimeException(msg, e); + } + if (Objects.isNull(previewContent)) { + return null; + } + return converter.toRest(previewContent, utils.obtainProjection()); + } + + @Override + public Page findAll(Context context, Pageable pageable) { + try { + List previewContentList = previewContentService.findAll(context); + return converter.toRestPage(previewContentList, pageable, utils.obtainProjection()); + } catch (SQLException e) { + String msg = "Database error occurred while finding all preview contents. Error msg: " + e.getMessage(); + log.error(msg, e); + throw new RuntimeException(msg, e); + } + } + + @Override + public Class getDomainClass() { + return PreviewContentRest.class; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentRestRepositoryIT.java new file mode 100644 index 000000000000..494c7cffc889 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentRestRepositoryIT.java @@ -0,0 +1,105 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest; + + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.io.IOUtils; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.BundleBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.builder.PreviewContentBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.PreviewContent; +import org.dspace.content.service.PreviewContentService; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class PreviewContentRestRepositoryIT extends AbstractControllerIntegrationTest { + + private PreviewContent previewContent; + private Bitstream bitstream; + + @Autowired + private PreviewContentService previewContentService; + + @Before + public void setup() throws SQLException, AuthorizeException, IOException { + context.turnOffAuthorisationSystem(); + // create bitstream + Community comm = CommunityBuilder.createCommunity(context) + .withName("Community Test") + .build(); + Collection col = CollectionBuilder.createCollection(context, comm).withName("Collection Test").build(); + Item publicItem = ItemBuilder.createItem(context, col) + .withTitle("Test") + .withIssueDate("2010-10-17") + .withAuthor("Smith, Donald") + .withSubject("ExtraEntry") + .build(); + Bundle bundle1 = BundleBuilder.createBundle(context, publicItem) + .withName("Bundle Test") + .build(); + String bitstreamContent = "ThisIsSomeDummyText"; + bitstream = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream = BitstreamBuilder. + createBitstream(context, bundle1, is) + .withName("Bitstream1 Test") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + // create content previews + previewContent = PreviewContentBuilder.createPreviewContent(context, bitstream, "test1.txt", + null, false, "100", null).build(); + } + + @After + @Override + public void destroy() throws Exception { + //clean all + BitstreamBuilder.deleteBitstream(bitstream.getID()); + PreviewContentBuilder.deletePreviewContent(previewContent.getID()); + super.destroy(); + } + + @Test + public void findOne() throws Exception { + String adminToken = getAuthToken(admin.getEmail(), password); + + getClient(adminToken).perform(get("/api/core/previewContents/" + previewContent.getID())) + .andExpect(status().isOk()); + } + + @Test + public void findAll() throws Exception { + String adminToken = getAuthToken(admin.getEmail(), password); + + getClient(adminToken).perform(get("/api/core/previewContents") + .param("size", String.valueOf(100))) + .andExpect(status().isOk()); + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentServiceImplIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentServiceImplIT.java new file mode 100644 index 000000000000..5a4fbf7f2ddf --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/PreviewContentServiceImplIT.java @@ -0,0 +1,132 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest; + +import java.io.IOException; +import java.io.InputStream; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.io.IOUtils; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.BitstreamBuilder; +import org.dspace.builder.BundleBuilder; +import org.dspace.builder.CollectionBuilder; +import org.dspace.builder.CommunityBuilder; +import org.dspace.builder.ItemBuilder; +import org.dspace.builder.PreviewContentBuilder; +import org.dspace.content.Bitstream; +import org.dspace.content.Bundle; +import org.dspace.content.Collection; +import org.dspace.content.Community; +import org.dspace.content.Item; +import org.dspace.content.PreviewContent; +import org.dspace.content.service.PreviewContentService; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +public class PreviewContentServiceImplIT extends AbstractControllerIntegrationTest { + + @Autowired + PreviewContentService previewContentService; + PreviewContent previewContent0; + PreviewContent previewContent1; + PreviewContent previewContent2; + Bitstream bitstream1; + Bitstream bitstream2; + + @Before + public void setup() throws SQLException, AuthorizeException, IOException { + context.turnOffAuthorisationSystem(); + // create bitstream + Community comm = CommunityBuilder.createCommunity(context) + .withName("Community Test") + .build(); + Collection col = CollectionBuilder.createCollection(context, comm).withName("Collection Test").build(); + Item publicItem = ItemBuilder.createItem(context, col) + .withTitle("Test") + .withIssueDate("2010-10-17") + .withAuthor("Smith, Donald") + .withSubject("ExtraEntry") + .build(); + Bundle bundle1 = BundleBuilder.createBundle(context, publicItem) + .withName("Bundle Test") + .build(); + String bitstreamContent = "ThisIsSomeDummyText"; + bitstream1 = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream1 = BitstreamBuilder. + createBitstream(context, bundle1, is) + .withName("Bitstream1 Test") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + bitstream2 = null; + try (InputStream is = IOUtils.toInputStream(bitstreamContent, CharEncoding.UTF_8)) { + bitstream2 = BitstreamBuilder. + createBitstream(context, bundle1, is) + .withName("Bitstream2 Test") + .withDescription("description") + .withMimeType("text/plain") + .build(); + } + // create content previews + previewContent0 = PreviewContentBuilder.createPreviewContent(context, bitstream1, "test1.txt", + null, false, "100", null).build(); + Map previewContentMap = new HashMap<>(); + previewContentMap.put(previewContent0.getName(), previewContent0); + previewContent1 = PreviewContentBuilder.createPreviewContent(context, bitstream1, "", null, + true, "0", previewContentMap).build(); + previewContent2 = PreviewContentBuilder.createPreviewContent(context, bitstream2, "test2.txt", null, + false, "200", previewContentMap).build(); + } + + @After + @Override + public void destroy() throws Exception { + //clean all + BitstreamBuilder.deleteBitstream(bitstream1.getID()); + PreviewContentBuilder.deletePreviewContent(previewContent0.getID()); + PreviewContentBuilder.deletePreviewContent(previewContent1.getID()); + BitstreamBuilder.deleteBitstream(bitstream2.getID()); + PreviewContentBuilder.deletePreviewContent(previewContent2.getID()); + super.destroy(); + } + + @Test + public void testFindAll() throws Exception { + List previewContentList = previewContentService.findAll(context); + Assert.assertEquals(previewContentList.size(), 3); + Assert.assertEquals(previewContent0.getID(), previewContentList.get(0).getID()); + Assert.assertEquals(previewContent1.getID(), previewContentList.get(1).getID()); + Assert.assertEquals(previewContent2.getID(), previewContentList.get(2).getID()); + } + + @Test + public void testFindByBitstream() throws Exception { + List previewContentList = previewContentService.findByBitstream(context, bitstream2.getID()); + Assert.assertEquals(previewContentList.size(), 1); + Assert.assertEquals(previewContent2.getID(), previewContentList.get(0).getID()); + } + + @Test + public void testFindRootByBitstream() throws Exception { + List previewContentList = + previewContentService.findRootByBitstream(context, bitstream1.getID()); + Assert.assertEquals(previewContentList.size(), 1); + Assert.assertEquals(previewContent1.getID(), previewContentList.get(0).getID()); + } +} diff --git a/dspace/config/clarin-dspace.cfg b/dspace/config/clarin-dspace.cfg index 730ce6118c3e..6c00f45b5a1e 100644 --- a/dspace/config/clarin-dspace.cfg +++ b/dspace/config/clarin-dspace.cfg @@ -245,7 +245,7 @@ shibboleth.name.conversion.outputEncoding = UTF-8 ### File preview ### # File preview is enabled by default -file.preview.enabled = false +file.preview.enabled = true # It the ZIP file contains more than 1000 files show only the first 1000 files file.preview.zip.limit.length = 1000 diff --git a/dspace/config/hibernate.cfg.xml b/dspace/config/hibernate.cfg.xml index eee136f64146..82e4fd738038 100644 --- a/dspace/config/hibernate.cfg.xml +++ b/dspace/config/hibernate.cfg.xml @@ -49,6 +49,7 @@ + diff --git a/dspace/config/spring/api/core-dao-services.xml b/dspace/config/spring/api/core-dao-services.xml index d4b46b2b36d0..d4f87e187747 100644 --- a/dspace/config/spring/api/core-dao-services.xml +++ b/dspace/config/spring/api/core-dao-services.xml @@ -31,6 +31,7 @@ + diff --git a/dspace/config/spring/api/core-services.xml b/dspace/config/spring/api/core-services.xml index 9fdb45d082fa..305d41f64ee5 100644 --- a/dspace/config/spring/api/core-services.xml +++ b/dspace/config/spring/api/core-services.xml @@ -57,6 +57,7 @@ +