diff --git a/ccm-core/pom.xml b/ccm-core/pom.xml
index 790dbf8c9..77939b898 100644
--- a/ccm-core/pom.xml
+++ b/ccm-core/pom.xml
@@ -178,7 +178,13 @@
h2
test
-
+
+
+ com.opencsv
+ opencsv
+
+
+
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/ObjectExporter.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/ObjectExporter.java
new file mode 100644
index 000000000..b6dc2a927
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/ObjectExporter.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter;
+
+import com.opencsv.CSVWriter;
+import org.apache.commons.lang.NullArgumentException;
+import org.apache.log4j.Logger;
+import org.libreccm.security.User;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Main class for exporting database objects as .csv-textfiles. Subclasses
+ * are required to implement the method {@code asList} matching their own
+ * needs. This is necessary, because every object class stored in the
+ * database has its own parameters which refer sometimes to other object
+ * classes. But these other object classes do not need to be exported in
+ * their entirety, only their uuid for later re-identification.
+ *
+ * @author Tobias Osmers
+ * @version 13/01/2016
+ */
+public abstract class ObjectExporter {
+
+ private static final Logger log = Logger.getLogger(ObjectExporter.class);
+
+ private String filename = "ccm_ng-defaultExportFilename.csv";
+ private char separator = ',';
+
+ //> Begin GETTER & SETTER
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+ public char getSeparator() {
+ return separator;
+ }
+
+ public void setSeparator(char separator) {
+ this.separator = separator;
+ }
+
+ //< End GETTER & SETTER
+
+ /**
+ * Empty constructor.
+ */
+ public ObjectExporter() {}
+
+ /**
+ * Exports a list of objects of type {@code T}, e.g. a list of
+ * {@link User}s, as a .csv-textfile with the specified {@code filename}.
+ *
+ * @param exportObjects List of objects of type {@code T} to be exported
+ */
+ public void export(List exportObjects) throws NullArgumentException {
+ CSVWriter csvWriter = null;
+ if (filename == null) {
+ throw new NullArgumentException(filename);
+ }
+ try {
+ csvWriter = new CSVWriter(new FileWriter(filename),
+ separator);
+ csvWriter.writeAll(asList(exportObjects));
+ csvWriter.close();
+ } catch (IOException e) {
+ //Todo: what to do
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Abstract method to force extending subclasses to implement this
+ * method, so the needed list for the export is matching their special
+ * needs.
+ *
+ * @param exportObjects List of objects of type {@code T} to be exported
+ * @return A list of strings containing all database information of the
+ * wanted object class.
+ */
+ public abstract List asList(List exportObjects);
+
+ /**
+ * Abstract method to reduce the types of a single export object to
+ * strings.
+ *
+ * @param exportObject A single exportObject
+ * @return A list of strings representing the parameters of the
+ * export object
+ */
+ protected abstract String[] reduceToString(T exportObject);
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/BlobObject.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/BlobObject.java
new file mode 100644
index 000000000..6a3b379ab
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/BlobObject.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+
+import org.hibernate.validator.constraints.NotEmpty;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Lob;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * Entity class for a blob object in the doc-repository. Instances of this class
+ * will be persisted into the database.
+ *
+ * @author Tobias Osmers
+ * @version 01/10/2015
+ */
+@Entity
+@Table(schema = "CCM_DOCREPO", name = "BLOB_OBJECTS")
+public class BlobObject implements Serializable {
+
+ private static final long serialVersionUID = -7468014879548796218L;
+
+ /**
+ * The ID/primary key for the {@code BlobObject}. Please note that it is not
+ * necessary to define an additional ID on classes which extend this class.
+ */
+ @Id
+ @Column(name = "BLOB_OBJECT_ID")
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private long blobObjectId;
+
+ /**
+ * The Content of the {@code BlobObject}.
+ */
+ @Column(name = "CONTENT")
+ @Lob
+ @NotEmpty
+ private byte[] content;
+
+ /**
+ * The {@link Resource} the {@code BlobObject} was assigned to.
+ */
+ @OneToOne(mappedBy = "content")
+ @NotEmpty
+ private Resource resource;
+
+ /**
+ * Constructor.
+ */
+ public BlobObject() {}
+
+ //> Begin GETTER & SETTER
+
+ public long getBlobObjectId() {
+ return blobObjectId;
+ }
+
+ public void setBlobObjectId(long blobObjectId) {
+ this.blobObjectId = blobObjectId;
+ }
+
+ public byte[] getContent() {
+ return content;
+ }
+
+ public void setContent(byte[] content) {
+ this.content = content;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public void setResource(Resource resource) {
+ this.resource = resource;
+ }
+
+ //< End GETTER & SETTER
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 61 * hash + (int) (blobObjectId ^ (blobObjectId >>> 32));
+ hash = 61 * hash + Objects.hashCode(content);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof BlobObject)) {
+ return false;
+ }
+
+ final BlobObject other = (BlobObject) obj;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+
+ return blobObjectId == other.getBlobObjectId() &&
+ Objects.equals(content, other.getContent());
+ }
+
+ public boolean canEqual(final Object obj) {
+ return obj instanceof BlobObject;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s{ "
+ + "blobObjectId = %d, "
+ + "content = %s, "
+ + " }",
+ super.toString(),
+ blobObjectId,
+ content.toString());
+ }
+
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/File.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/File.java
new file mode 100644
index 000000000..5951475a2
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/File.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * Entity class for a file in the doc-repository. Instances will be persisted
+ * into the database. Instance variables are inherited from {@link Resource}.
+ *
+ * @author Tobias Osmers
+ * @version 01/10/2015
+ */
+@Entity
+@Table(schema = "CCM_DOCREPO", name = "FILES")
+public class File extends Resource {
+
+ private static final long serialVersionUID = -504220783419811504L;
+
+ /**
+ * Constructor calls the super-class-constructor of {@link Resource}.
+ */
+ public File() {
+ super();
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Folder.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Folder.java
new file mode 100644
index 000000000..cad360f92
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Folder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+import javax.persistence.Entity;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+
+/**
+ * Entity class of a folder in the doc-repository. Instances will be persisted
+ * into the database. Instance variables are inherited from {@link Resource}.
+ *
+ * @author Tobias Osmers
+ * @version 01/10/2015
+ */
+@Entity
+@Table(schema = "CCM_DOCREPO", name = "FOLDERS")
+public class Folder extends Resource {
+
+ private static final long serialVersionUID = 1561466556458872622L;
+
+ /**
+ * The {@link Repository} this {@code Folder} is assigned to as root.
+ */
+ @OneToOne(mappedBy = "rootFolder")
+ private Repository repository;
+
+ /**
+ * Constructor calls the super-class-constructor of {@link Resource}.
+ */
+ public Folder() {
+ super();
+ }
+
+ //> Begin GETTER & SETTER
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ //< End GETTER & SETTER
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/RecUpdDocsPortlet.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/RecUpdDocsPortlet.java
new file mode 100644
index 000000000..41ae4cca2
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/RecUpdDocsPortlet.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+import org.libreccm.portal.Portlet;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * Entity class for a portlet of recent updated documents in the doc-repository.
+ * Instances will be persisted into the database. Instance variables are inherited
+ * form {@link Portlet}.
+ *
+ * @author Tobias Osmers
+ * @version 01/10/2015
+ */
+@Entity
+@Table(schema = "CCM_DOCREPO", name = "REC_UPD_DOCS_PORTLETS")
+public class RecUpdDocsPortlet extends Portlet {
+
+ private static final long serialVersionUID = -4091024367070127101L;
+
+ /**
+ * Constructor calls the super-class-constructor of {@link Portlet}.
+ */
+ public RecUpdDocsPortlet() {
+ super();
+ }
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Repository.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Repository.java
new file mode 100644
index 000000000..dc0ff2f16
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Repository.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+import org.hibernate.validator.constraints.NotBlank;
+import org.libreccm.security.User;
+import org.libreccm.web.CcmApplication;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import java.util.List;
+
+/**
+ * Entity class of a repository for documents. Instances will be persisted into
+ * the database. Instance variables are inherited from {@link CcmApplication}.
+ *
+ * @author Tobias Osmers
+ * @version 01/10/2015
+ */
+@Entity
+@Table(schema = "CCM_DOCREPO", name = "REPOSITORIES")
+@NamedQueries({
+ @NamedQuery(name = "DocRepo.findRepositoriesForOwner",
+ query = "SELECT r FROM Repository r WHERE r.owner = :owner")
+})
+public class Repository extends CcmApplication {
+
+ private static final long serialVersionUID = 6673243021462798036L;
+
+ /**
+ * Name des {@code Repository}s.
+ */
+ @Column(name = "NAME", length = 512, unique = true, nullable = false)
+ @NotBlank
+ private String name;
+
+ /**
+ * The root of the {@code Repository}.
+ */
+ @OneToOne
+ @JoinColumn(name = "ROOT_FOLDER_ID", unique = true, nullable = false)
+ @NotBlank
+ private Folder rootFolder;
+
+ /**
+ * The owner of the {@code Repository}.
+ */
+ @OneToOne
+ @JoinColumn(name = "OWNER_ID", nullable = false)
+ @NotBlank
+ private User owner;
+
+ /**
+ * All {@link Resource}s contained in this {@code Repository}.
+ */
+ @OneToMany(mappedBy = "repository")
+ private List resources;
+
+
+ /**
+ * Constructor calls the super-class-constructor of {@link CcmApplication}.
+ */
+ public Repository() {
+ super();
+ }
+
+ //> Begin GETTER & SETTER
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Folder getRootFolder() {
+ return rootFolder;
+ }
+
+ public void setRootFolder(Folder root_folder) {
+ this.rootFolder = root_folder;
+ }
+
+ public User getOwner() {
+ return owner;
+ }
+
+ public void setOwner(User owner) {
+ this.owner = owner;
+ }
+
+ public List getResources() {
+ return resources;
+ }
+
+ public void setResources(List resources) {
+ this.resources = resources;
+ }
+
+ //< End GETTER & SETTER
+
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/RepositoryRepository.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/RepositoryRepository.java
new file mode 100644
index 000000000..5bda9b246
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/RepositoryRepository.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+import org.libreccm.auditing.AbstractAuditedEntityRepository;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.security.PermissionChecker;
+import org.libreccm.security.User;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Repository class for retrieving, storing and deleting {@code Repository}s.
+ *
+ * @author Tobias Osmers
+ * @version 25/11/2015
+ */
+@RequestScoped
+public class RepositoryRepository extends
+ AbstractAuditedEntityRepository {
+
+ @Inject
+ private EntityManager entityManager;
+
+ @Override
+ public Long getEntityId(Repository entity) {
+ return entity.getObjectId();
+ }
+
+ @Override
+ public Class getEntityClass() {
+ return Repository.class;
+ }
+
+ @Override
+ public boolean isNew(Repository entity) {
+ if (entity == null) {
+ throw new IllegalArgumentException("Entity to save can't be null.");
+ }
+ return entity.getObjectId() == 0;
+ }
+
+ /**
+ * Checks if the current subject has permissions grating him the
+ * privilege to read the requested {@link Repository}(s) and removes the
+ * ones he is not allowed to access.
+ *
+ * @param repositories The requested {@link Resource}s, found in the database
+ * @return A list of {@link Resource}s the subject is allowed to access
+ */
+ private List permissionFilter(List repositories) {
+ final CdiUtil cdiUtil = new CdiUtil();
+ final PermissionChecker permissionChecker = cdiUtil.findBean(
+ PermissionChecker.class);
+ return repositories.stream().filter(repository -> permissionChecker
+ .isPermitted("read", repository)).collect(Collectors.toList());
+ }
+
+ /**
+ * Retrieve all {@link Repository}s a given {@link User} ownes.
+ *
+ * @param owner The owner of the {@link Repository}s
+ *
+ * @return The {@link Repository}s owned by the given {@link User}
+ */
+ public List findForOwner(User owner) {
+ final TypedQuery query = entityManager.createNamedQuery(
+ "DocRepo.findRepositoriesForOwner", Repository.class);
+ query.setParameter("owner", owner);
+
+ return permissionFilter(query.getResultList());
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Resource.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Resource.java
new file mode 100644
index 000000000..24f8489c9
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/Resource.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+import org.hibernate.validator.constraints.NotBlank;
+import org.libreccm.core.CcmObject;
+import org.libreccm.security.User;
+
+import javax.activation.MimeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * Abstract entity class of a resource. Instances will be persisted into the
+ * database through the inheriting subclasses.
+ *
+ * The inheriting subclasses and therefore resources are: {@link File},
+ * {@link Folder}
+ *
+ * @author Tobias Osmers
+ * @version 01/10/2015
+ */
+@Entity(name = "DocRepoResource")
+@Table(schema = "CCM_DOCREPO", name = "RESOURCES")
+@NamedQueries({
+ @NamedQuery(name = "DocRepo.findResourceByPath",
+ query = "SELECT r FROM DocRepoResource r WHERE " +
+ "r.path = :pathName"),
+ @NamedQuery(name = "DocRepo.findResourcesByName",
+ query = "SELECT r FROM DocRepoResource r WHERE " +
+ "r.name = :name"),
+ @NamedQuery(name = "DocRepo.findCreatedResourcesFromUser",
+ query = "SELECT r FROM DocRepoResource r WHERE " +
+ "r.creationUser = :user"),
+ @NamedQuery(name = "DocRepo.findModifiedResourcesFromUser",
+ query = "SELECT r FROM DocRepoResource r WHERE " +
+ "r.lastModifiedUser = :user")})
+public abstract class Resource extends CcmObject {
+
+ private static final long serialVersionUID = -910317798106611214L;
+
+ /**
+ * Name of the {@code Resource}.
+ */
+ @Column(name = "NAME", length = 512, unique = true, nullable = false)
+ @NotBlank
+ private String name;
+
+ /**
+ * Description of the {@code Resource}.
+ */
+ @Column(name = "DESCRIPTION")
+ private String description;
+
+ /**
+ * Flag, wheather the {@code Resource} is a {@link Folder} or not.
+ */
+ @Column(name = "IS_FOLDER")
+ @NotBlank
+ private boolean isFolder;
+
+ /**
+ * Path to the {@code Resource}.
+ */
+ @Column(name = "PATH", unique = true)
+ @NotBlank
+ private String path;
+
+ /**
+ * Mime-type of the {@code Resource}.
+ */
+ @Column(name = "MIME_TYPE")
+ private MimeType mimeType;
+
+ /**
+ * Size of the {@code Resource}.
+ */
+ @Column(name = "SIZE")
+ private long size;
+
+ /**
+ * Content of the {@code Resource} as a {@link BlobObject}.
+ */
+ @OneToOne
+ @JoinColumn(name = "CONTENT_ID")
+ private BlobObject content;
+
+ /**
+ * Creation date of the {@code Resource}.
+ */
+ @Column(name = "CREATION_DATE")
+ @NotBlank
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date creationDate;
+
+ /**
+ * Date of the latest modification of the {@code Resource}.
+ */
+ @Column(name = "LAST_MODIFIED_DATE")
+ @NotBlank
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date lastModifiedDate;
+
+ /**
+ * Creation ip of the {@code Resource}.
+ */
+ @Column(name = "CREATION_IP")
+ private String creationIp;
+
+ /**
+ * Ip of the latest modification of the {@code Resource}.
+ */
+ @Column(name = "LAST_MODIFIED_IP")
+ private String lastModifiedIp;
+
+ /**
+ * The {@link User}, who created the {@code Resource}.
+ */
+ @ManyToOne
+ @JoinColumn(name = "CREATION_USER_ID")
+ private User creationUser;
+
+ /**
+ * The {@link User}, who last modified the {@code Resource}.
+ */
+ @ManyToOne
+ @JoinColumn(name = "LAST_MODIFIED_USER_ID")
+ private User lastModifiedUser;
+
+ /**
+ * The parent-{@code Resource} of the {@code Resource}.
+ */
+ @ManyToOne
+ @JoinColumn(name = "PARENT_ID")
+ private Resource parent;
+
+ /**
+ * The child-{@code Resource}s of the {@code Resource}.
+ */
+ @OneToMany(mappedBy = "parent")
+ private List immediateChildren;
+
+ /**
+ * The {@link Repository} containing this {@code Resource}.
+ */
+ @ManyToOne
+ @JoinColumn(name = "REPOSITORY_ID")
+ private Repository repository;
+
+ /**
+ * Constructor calls the super-class-constructor of {@link CcmObject}.
+ */
+ public Resource() {
+ super();
+ }
+
+ //> Begin GETTER & SETTER
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public boolean isFolder() {
+ return isFolder;
+ }
+
+ public void setIsFolder(boolean isFolder) {
+ this.isFolder = isFolder;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public MimeType getMimeType() {
+ return mimeType;
+ }
+
+ public void setMimeType(MimeType mimeType) {
+ this.mimeType = mimeType;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public BlobObject getContent() {
+ return content;
+ }
+
+ public void setContent(BlobObject content) {
+ this.content = content;
+ }
+
+ public Date getCreationDate() {
+ return creationDate;
+ }
+
+ public void setCreationDate(Date creationDate) {
+ this.creationDate = creationDate;
+ }
+
+ public Date getLastModifiedDate() {
+ return lastModifiedDate;
+ }
+
+ public void setLastModifiedDate(Date lastModifiedDate) {
+ this.lastModifiedDate = lastModifiedDate;
+ }
+
+ public String getCreationIp() {
+ return creationIp;
+ }
+
+ public void setCreationIp(String creationIp) {
+ this.creationIp = creationIp;
+ }
+
+ public String getLastModifiedIp() {
+ return lastModifiedIp;
+ }
+
+ public void setLastModifiedIp(String lastModifiedIp) {
+ this.lastModifiedIp = lastModifiedIp;
+ }
+
+ public User getCreationUser() {
+ return creationUser;
+ }
+
+ public void setCreationUser(User creationUser) {
+ this.creationUser = creationUser;
+ }
+
+ public User getLastModifiedUser() {
+ return lastModifiedUser;
+ }
+
+ public void setLastModifiedUser(User lastModifiedUser) {
+ this.lastModifiedUser = lastModifiedUser;
+ }
+
+ public Resource getParent() {
+ return parent;
+ }
+
+ public void setParent(Resource parent) {
+ this.parent = parent;
+ }
+
+ public List getImmediateChildren() {
+ return immediateChildren;
+ }
+
+ public void setImmediateChildren(List immediateChildren) {
+ this.immediateChildren = immediateChildren;
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ //< End GETTER & SETTER
+
+ public boolean isRoot() {
+ return isFolder() && getParent() == null;
+ }
+
+ public boolean isFile() {
+ return !isFolder();
+ }
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/ResourceManager.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/ResourceManager.java
new file mode 100644
index 000000000..4d1286929
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/ResourceManager.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+import org.apache.log4j.Logger;
+import org.apache.oro.text.perl.Perl5Util;
+
+import javax.activation.MimetypesFileTypeMap;
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+
+/**
+ * Manager class for complex operations on {@code Resource}-objects.
+ *
+ * @author Tobias Osmers
+ * @version 01/10/2015
+ */
+@RequestScoped
+public class ResourceManager {
+ private static final Logger log = Logger.getLogger(ResourceManager.class);
+
+ @Inject
+ private ResourceRepository resourceRepository;
+
+ /**
+ * Copies a given {@link Resource} to a given {@link Folder}.
+ *
+ * @param original The {@link Resource} to be copied
+ * @param folder The {@link Folder} to copy to
+ */
+ public void copyToFolder(Resource original, Folder folder) {
+ Resource copy = original.isFolder() ? new Folder() : new File();
+ copy.setName(original.getName());
+ copy.setDescription(original.getDescription());
+ copy.setIsFolder(original.isFolder());
+ copy.setPath(String.format("%s/%s", folder.getPath(), copy.getName()));
+ copy.setMimeType(original.getMimeType());
+ copy.setSize(original.getSize());
+ copy.setContent(original.getContent());
+
+ copy.setParent(folder);
+ copy.setImmediateChildren(original.getImmediateChildren());
+
+ resourceRepository.save(copy);
+ }
+
+ /**
+ * Determines weather the given name is a valid new name for the also
+ * given {@link Resource}.
+ *
+ * Verifies that the string only contains valid characters for
+ * {@link Resource} names. The following name patterns are allowed:
+ *
+ * [a-z][A-Z][0-9][-., ]
+ *
+ * In addition, names cannot begin with ".", i.e. we do NOT support file
+ * names like ".profile" at the moment. The current implementation does
+ * not allow international characters for resource names.
+ *
+ * @param name The resource name for validation
+ * @param resource The resource for which the new name needs to be validated
+ *
+ * @return true for a system-valid resource name, otherwise false
+ */
+ public boolean isValidNewResourceName(String name, Resource resource) {
+ Perl5Util perl5Util = new Perl5Util();
+
+ final String INVALID_START_PATTERN = "/^[.]+/";
+ final String INVALID_NAME_PATTERN = "/[^a-zA-Z0-9\\_\\.\\-\\ ]+/";
+ final String EXTENSION_PATTERN = "/^([^\\.].*)(\\.\\w+)$/";
+
+ // adds an extension if non-existent
+ if (!perl5Util.match(EXTENSION_PATTERN, name)) {
+ if (perl5Util.match(EXTENSION_PATTERN, resource.getName())) {
+ name += perl5Util.group(2);
+ }
+ }
+
+ // checks pattern of the name
+ boolean validName = !(perl5Util.match(INVALID_START_PATTERN, name) ||
+ perl5Util.match(INVALID_NAME_PATTERN, name));
+
+ // checks duplication of the name; database access (mind performance)
+ validName &= resourceRepository.findByName(name).isEmpty();
+
+ // checks that the name corresponds to a compatible MIME type
+ validName &= new MimetypesFileTypeMap().getContentType(name).equals
+ (resource.getMimeType().toString());
+
+ return validName;
+ }
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/ResourceRepository.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/ResourceRepository.java
new file mode 100644
index 000000000..1f206898b
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/ResourceRepository.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo;
+
+
+import org.libreccm.auditing.AbstractAuditedEntityRepository;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.security.PermissionChecker;
+import org.libreccm.security.User;
+
+import javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Repository class for retrieving, storing and deleting {@code Resource}s.
+ *
+ * @author Tobias Osmers
+ * @version 01/10/2015
+ */
+@RequestScoped
+public class ResourceRepository extends AbstractAuditedEntityRepository {
+
+ @Inject
+ private EntityManager entityManager;
+
+ @Override
+ public Long getEntityId(Resource entity) {
+ return entity.getObjectId();
+ }
+
+ @Override
+ public Class getEntityClass() {
+ return Resource.class;
+ }
+
+ @Override
+ public boolean isNew(Resource entity) {
+ if (entity == null) {
+ throw new IllegalArgumentException("Entity to save can't be null.");
+ }
+ return entity.getObjectId() == 0;
+ }
+
+ /**
+ * Checks if the current subject has permissions grating him the
+ * privilege to read the requested {@link Resource}(s) and removes the
+ * ones he is not allowed to access.
+ *
+ * @param resources The requested {@link Resource}s, found in the database
+ * @return A list of {@link Resource}s the subject is allowed to access
+ */
+ private List permissionFilter(List resources) {
+ final CdiUtil cdiUtil = new CdiUtil();
+ final PermissionChecker permissionChecker = cdiUtil.findBean(
+ PermissionChecker.class);
+ return resources.stream().filter(resource -> permissionChecker
+ .isPermitted("read", resource)).collect(Collectors.toList());
+ }
+
+ /**
+ * Checks if the current subject has permissions grating him the
+ * privilege to read the one requested {@link Resource} and removes it if
+ * he is not allowed to access.
+ *
+ * @param resource The requested {@link Resource}, found in the database
+ * @return A list of at most one {@link Resource} the subject is allowed to
+ * access
+ */
+ private Resource permissionFilter(Resource resource) {
+ return permissionFilter(Arrays.asList(resource)).get(0);
+ }
+
+ /**
+ * Retrieve a {@code Resource} by its {@code path}.
+ *
+ * @param pathName The {@code path} to the {@code Resource}.
+ *
+ * @return The {@code Resource} identified by the given {@code path}, if there is
+ * such a {@code Resource}, {@code null} if not.
+ */
+ public Resource findByPathName(final String pathName) {
+ final TypedQuery query = entityManager.createNamedQuery(
+ "DocRepo.findResourceByPath", Resource.class);
+ query.setParameter("pathName", pathName);
+
+ return permissionFilter(query.getSingleResult());
+ }
+
+ /**
+ * Retrieve the {@code Resource}s, a given {@link User} created.
+ *
+ * @param creator The {@link User}, who created the {@code Resource}s.
+ *
+ * @return The {@code Resource}s, created by the given {@link User}, if there
+ * are such {@code Resource}s, {@code EmptyList} if not.
+ */
+ public List findForCreator(final User creator) {
+ final TypedQuery query = entityManager.createNamedQuery(
+ "DocRepo.findCreatedResourcesFromUser", Resource.class);
+ query.setParameter("user", creator);
+
+ return permissionFilter(query.getResultList());
+ }
+
+ /**
+ * Retrieve the {@code Resource}s, a given {@link User} last modified.
+ *
+ * @param modifier The {@link User}, who last modified the {@code Resource}s.
+ *
+ * @return The {@code Resource}s, last modified by the given {@link User}, if
+ * there are such {@code Resource}s, {@code EmptyList} if not.
+ */
+ public List findForModifier(final User modifier) {
+ final TypedQuery query = entityManager.createNamedQuery(
+ "DocRepo.findModifiedResourcesFromUser", Resource.class);
+ query.setParameter("user", modifier);
+
+ return permissionFilter(query.getResultList());
+ }
+
+ /**
+ * Retrieve all {@link Resource}s with the given name.
+ *
+ * @param name The name for the searched {@link Resource}
+ * @return The {@link Resource}s with the given name, if there aren't
+ * any an {@code EmptyList}
+ */
+ public List findByName(final String name) {
+ final TypedQuery query = entityManager.createNamedQuery(
+ "DocRepo.findResourcesByName", Resource.class);
+ query.setParameter("name", name);
+
+ return permissionFilter(query.getResultList());
+ }
+}
diff --git a/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/exchange/exporter/ResourceExporter.java b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/exchange/exporter/ResourceExporter.java
new file mode 100644
index 000000000..aaaea7f5e
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/exchange/exporter/docrepo/exchange/exporter/ResourceExporter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.exchange.exporter.docrepo.exchange;
+
+import org.libreccm.exchange.exporter.ObjectExporter;
+import org.libreccm.exchange.exporter.docrepo.Resource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Exporter class for resources. Implements the abstract method of its super.
+ *
+ * @author Tobias Osmers
+ * @version 13/01/2016
+ */
+public class ResourceExporter extends ObjectExporter {
+ @Override
+ public List asList(List exportObjects) {
+ List exportList = new ArrayList<>();
+
+ exportList.add(new String[]{Resource.class.getName()});
+ exportList.add(new String[]{
+ "name",
+ "description",
+ "isFolder",
+ "path",
+ "mimeType",
+ "size",
+ "blobObject_ID",
+ "creationDate",
+ "lastModifiedDate",
+ "creationIP",
+ "lastModifiedIp",
+ "creator_ID",
+ "modifier_ID",
+ "parent_ID",
+ "repo_ID"
+ });
+
+ return exportList.addAll(exportObjects.stream().map(
+ this::reduceToString).collect(Collectors.toList()))
+ ? exportList : exportList;
+ }
+
+ @Override
+ protected String[] reduceToString(Resource exportObject) {
+ return new String[] {
+ // name
+ exportObject.getName(),
+ // description
+ exportObject.getDescription(),
+ // isFolder
+ String.valueOf(exportObject.isFolder()),
+ // path
+ exportObject.getPath(),
+ // mimeType
+ exportObject.getMimeType().toString(),
+ // size
+ String.valueOf(exportObject.getSize()),
+ // blobObject_ID
+ String.valueOf(exportObject.getContent().getBlobObjectId()),
+ // creationDate
+ exportObject.getCreationDate().toString(),
+ // lastModifiedDate
+ exportObject.getLastModifiedDate().toString(),
+ // creationIp
+ exportObject.getCreationIp(),
+ // lastModifiedIp
+ exportObject.getLastModifiedIp(),
+ // creator_ID
+ exportObject.getCreationUser().getName(),
+ // modifier_ID
+ exportObject.getLastModifiedUser().getName(),
+ // parent_ID
+ String.valueOf(exportObject.getParent().getObjectId()),
+ // repo_ID
+ String.valueOf(exportObject.getRepository().getObjectId()),
+ };
+ }
+}
diff --git a/pom.xml b/pom.xml
index 365114234..429e74431 100644
--- a/pom.xml
+++ b/pom.xml
@@ -425,6 +425,12 @@
xml-resolver
1.2
+
+
+ com.opencsv
+ opencsv
+ 3.6
+