diff --git a/ccm-cms/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_cms_schema.sql b/ccm-cms/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_cms_schema.sql
index 47290cb8f..dc1ebc6e0 100644
--- a/ccm-cms/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_cms_schema.sql
+++ b/ccm-cms/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_cms_schema.sql
@@ -590,6 +590,7 @@ drop sequence if exists HIBERNATE_SEQUENCE;
NAME varchar(255) not null,
FILE_PATH varchar(8192) not null,
UUID varchar(255) not null,
+ VERSION varchar(255),
PARENT_DIRECTORY_ID bigint,
primary key (FILE_ID)
);
diff --git a/ccm-cms/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_cms_schema.sql b/ccm-cms/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_cms_schema.sql
index 8c6536233..6f515194b 100644
--- a/ccm-cms/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_cms_schema.sql
+++ b/ccm-cms/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_cms_schema.sql
@@ -592,6 +592,7 @@ drop sequence if exists HIBERNATE_SEQUENCE;
NAME varchar(255) not null,
FILE_PATH varchar(8192) not null,
UUID varchar(255) not null,
+ VERSION varchar(255),
PARENT_DIRECTORY_ID int8,
primary key (FILE_ID)
);
diff --git a/ccm-core/src/main/java/org/libreccm/theming/db/DataFile.java b/ccm-core/src/main/java/org/libreccm/theming/db/DataFile.java
new file mode 100644
index 000000000..16cc16c75
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/db/DataFile.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2018 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.theming.db;
+
+import org.libreccm.core.CoreConstants;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Objects;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+/**
+ * A file inside the directory structure of a theme stored in the database.
+ *
+ * @author Jens Pelzetter
+ */
+@Entity
+@Table(name = "THEME_DATA_FILES", schema = CoreConstants.DB_SCHEMA)
+public class DataFile extends ThemeFile {
+
+ private static final long serialVersionUID = 7513785608453872667L;
+
+ @Column(name = "TYPE")
+ private String type;
+
+ @Column(name = "FILE_SIZE")
+ private long size;
+
+ @Column(name = "CREATION_DATE")
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date creationDate;
+
+ @Column(name = "LAST_MODIFIED")
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date lastModified;
+
+ @Column(name = "FILE_DATA")
+ @Lob
+ private byte[] data;
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(final String type) {
+ this.type = type;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public void setSize(final long size) {
+ this.size = size;
+ }
+
+ public Date getCreationDate() {
+ if (creationDate == null) {
+ return null;
+ } else {
+ return new Date(creationDate.getTime());
+ }
+ }
+
+ protected void setCreationDate(final Date creationDate) {
+ if (creationDate == null) {
+ this.creationDate = null;
+ } else {
+ this.creationDate = new Date(creationDate.getTime());
+ }
+ }
+
+ public Date getLastModified() {
+ if (lastModified == null) {
+ return null;
+ } else {
+ return new Date(lastModified.getTime());
+ }
+ }
+
+ protected void setLastModified(final Date lastModified) {
+ if (lastModified == null) {
+ this.lastModified = null;
+ } else {
+ this.lastModified = new Date(lastModified.getTime());
+ }
+ }
+
+ public byte[] getData() {
+ if (data == null) {
+ return null;
+ } else {
+ return Arrays.copyOf(data, data.length);
+ }
+ }
+
+ public void setData(final byte[] data) {
+ if (data == null) {
+ this.data = null;
+ } else {
+ this.data = Arrays.copyOf(data, data.length);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = super.hashCode();
+ hash = 47 * hash + Objects.hashCode(type);
+ hash = 47 * hash + (int) (size ^ (size >>> 32));
+ if (creationDate == null) {
+ hash = 47 * hash + 0;
+ } else {
+ hash = 47 * hash + Objects.hashCode(creationDate.getTime());
+ }
+ if (lastModified == null) {
+ hash = 47 * hash + 0;
+ } else {
+ hash = 47 * hash + Objects.hashCode(lastModified.getTime());
+ }
+ hash = 47 * hash + Arrays.hashCode(data);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof DataFile)) {
+ return false;
+ }
+ final DataFile other = (DataFile) obj;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (size != other.getSize()) {
+ return false;
+ }
+ if (!Objects.equals(type, other.getType())) {
+ return false;
+ }
+ if (!Objects.equals(creationDate, other.getCreationDate())) {
+ return false;
+ }
+ if (!Objects.equals(lastModified, other.getLastModified())) {
+ return false;
+ }
+ return Arrays.equals(data, other.getData());
+ }
+
+ @Override
+ public boolean canEqual(final Object other) {
+ return other instanceof DataFile;
+ }
+
+ @Override
+ public String toString(final String toStringData) {
+ return super.toString(String.format(", size = %d, "
+ + "type = \"%s\", "
+ + "creationDate = %tF %Jens Pelzetter
+ */
+@Entity
+@Table(name = "theme_directories", schema = CoreConstants.DB_SCHEMA)
+public class Directory extends ThemeFile {
+
+ private static final long serialVersionUID = 3553722448470575337L;
+
+ @OneToMany(mappedBy = "parent")
+ @OrderBy("name ASC")
+ private List files;
+
+ public List getFiles() {
+ return Collections.unmodifiableList(files);
+ }
+
+ protected void setFiles(final List files) {
+ this.files = new ArrayList<>(files);
+ }
+
+ protected void addFile(final ThemeFile file) {
+ files.add(file);
+ }
+
+ protected void removeFile(final ThemeFile file) {
+ files.remove(file);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = super.hashCode();
+ hash = 47 * hash;
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (!(obj instanceof Directory)) {
+ return false;
+ }
+ final Directory other = (Directory) obj;
+ return other.canEqual(this);
+ }
+
+ @Override
+ public boolean canEqual(final Object other) {
+ return other instanceof Directory;
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/theming/db/Theme.java b/ccm-core/src/main/java/org/libreccm/theming/db/Theme.java
new file mode 100644
index 000000000..cfd787ca4
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/db/Theme.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2018 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.theming.db;
+
+import org.libreccm.core.CoreConstants;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@Entity
+@Table(name = "THEMES", schema = CoreConstants.DB_SCHEMA)
+@NamedQueries({
+ @NamedQuery(name = "Theme.findByUuid",
+ query = "SELECT t FROM Theme t "
+ + "WHERE t.uuid = :uuid "
+ + "AND t.version = :version")
+ ,
+ @NamedQuery(name = "Theme.findByName",
+ query = "SELECT t FROM Theme t "
+ + "WHERE t.name = :name "
+ + "AND t.version = :version")
+})
+public class Theme implements Serializable {
+
+ private static final long serialVersionUID = -5229641158755727717L;
+
+ @Id
+ @Column(name = "THEME_ID")
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private long themeId;
+
+ @Column(name = "NAME", length = 255)
+ private String name;
+
+ @Column(name = "UUID")
+ private String uuid;
+
+ @Column(name = "VERSION")
+ @Enumerated(EnumType.STRING)
+ private ThemeVersion version;
+
+ @OneToOne
+ @JoinColumn(name = "ROOT_DIRECTORY_ID")
+ private Directory rootDirectory;
+
+ public long getThemeId() {
+ return themeId;
+ }
+
+ protected void setThemeId(final long themeId) {
+ this.themeId = themeId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ protected void setUuid(final String uuid) {
+ this.uuid = uuid;
+ }
+
+ public ThemeVersion getVersion() {
+ return version;
+ }
+
+ protected void setVersion(final ThemeVersion version) {
+ this.version = version;
+ }
+
+ public Directory getRootDirectory() {
+ return rootDirectory;
+ }
+
+ protected void setRootDirectory(final Directory rootDirectory) {
+ this.rootDirectory = rootDirectory;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 7;
+ hash = 79 * hash + (int) (themeId ^ (themeId >>> 32));
+ hash = 79 * hash + Objects.hashCode(name);
+ hash = 79 * hash + Objects.hashCode(uuid);
+ hash = 79 * hash + Objects.hashCode(version);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof Theme)) {
+ return false;
+ }
+ final Theme other = (Theme) obj;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (themeId != other.getThemeId()) {
+ return false;
+ }
+ if (!Objects.equals(name, other.getName())) {
+ return false;
+ }
+ if (!Objects.equals(uuid, other.getUuid())) {
+ return false;
+ }
+ return version == other.getVersion();
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof Theme;
+ }
+
+ @Override
+ public final String toString() {
+ return toString("");
+ }
+
+ public String toString(final String data) {
+ return String.format("%s{ "
+ + "themeId = %d, "
+ + "name = \"%s\", "
+ + "uuid = %s, "
+ + "version = %s%s"
+ + " }",
+ super.toString(),
+ themeId,
+ name,
+ uuid,
+ Objects.toString(version),
+ data);
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/theming/db/ThemeFile.java b/ccm-core/src/main/java/org/libreccm/theming/db/ThemeFile.java
new file mode 100644
index 000000000..e15591fcb
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/db/ThemeFile.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2018 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.theming.db;
+
+import org.libreccm.core.CoreConstants;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+/**
+ * Basic class for files of theme stored in the database.
+ *
+ * @author Jens Pelzetter
+ */
+@Entity
+@Table(name = "THEME_FILES", schema = CoreConstants.DB_SCHEMA)
+@Inheritance(strategy = InheritanceType.JOINED)
+@NamedQueries({
+ @NamedQuery(name = "ThemeFile.findByUuid",
+ query = "SELECT f FROM ThemeFile f "
+ + "WHERE f.uuid = :uuid "
+ + "AND f.version = :version")
+ ,
+ @NamedQuery(name = "ThemeFile.findByPath",
+ query = "SELECT f FROM ThemeFile f "
+ + "WHERE f.path = :path "
+ + "AND f.version = :version")
+ ,
+ @NamedQuery(name = "ThemeFile.findByNameAndParent",
+ query = "SELECT f FROM ThemeFile f "
+ + "WHERE f.name = :name "
+ + "AND f.parent = :parent")
+})
+public class ThemeFile implements Serializable {
+
+ private static final long serialVersionUID = -622867075507267065L;
+
+ @Id
+ @Column(name = "FILE_ID")
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private long fileId;
+
+ @Column(name = "UUID", nullable = false)
+ @NotNull
+ private String uuid;
+
+ @Column(name = "NAME", length = 255, nullable = false)
+ @NotNull
+ private String name;
+
+ @Column(name = "VERSION")
+ @Enumerated(EnumType.STRING)
+ private ThemeVersion version;
+
+ @Column(name = "FILE_PATH", length = 8192, nullable = false)
+ @NotNull
+ private String path;
+
+ @ManyToOne
+ @JoinColumn(name = "PARENT_DIRECTORY_ID")
+ private Directory parent;
+
+ public long getFileId() {
+ return fileId;
+ }
+
+ public void setFileId(long fileId) {
+ this.fileId = fileId;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ protected void setPath(final String path) {
+ this.path = path;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ protected void setUuid(final String uuid) {
+ this.uuid = uuid;
+ }
+
+ public ThemeVersion getVersion() {
+ return version;
+ }
+
+ protected void setVersion(final ThemeVersion version) {
+ this.version = version;
+ }
+
+ public Directory getParent() {
+ return parent;
+ }
+
+ protected void setParent(final Directory parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 37 * hash + (int) (fileId ^ (fileId >>> 32));
+ hash = 37 * hash + Objects.hashCode(name);
+ hash = 37 * hash + Objects.hashCode(path);
+ hash = 37 * hash + Objects.hashCode(uuid);
+ hash = 37 * hash + Objects.hashCode(parent);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof ThemeFile)) {
+ return false;
+ }
+ final ThemeFile other = (ThemeFile) obj;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+ if (fileId != other.getFileId()) {
+ return false;
+ }
+ if (!Objects.equals(name, other.getName())) {
+ return false;
+ }
+ if (!Objects.equals(path, other.getPath())) {
+ return false;
+ }
+ if (!Objects.equals(uuid, other.getUuid())) {
+ return false;
+ }
+ return Objects.equals(parent, other.getParent());
+ }
+
+ public boolean canEqual(final Object other) {
+ return other instanceof ThemeFile;
+ }
+
+ @Override
+ public final String toString() {
+ return toString("");
+ }
+
+ public String toString(final String data) {
+ return String.format("%s{ "
+ + "fileId = %d, "
+ + "name = \"%s\", "
+ + "path = \"%s\", "
+ + "uuid = \"%s\"%s"
+ + " }",
+ super.toString(),
+ fileId,
+ name,
+ path,
+ uuid,
+ data);
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/theming/db/ThemeFileRepository.java b/ccm-core/src/main/java/org/libreccm/theming/db/ThemeFileRepository.java
new file mode 100644
index 000000000..ac66d303b
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/db/ThemeFileRepository.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 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.theming.db;
+
+import org.libreccm.core.AbstractEntityRepository;
+
+import java.util.Optional;
+
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class ThemeFileRepository extends AbstractEntityRepository {
+
+ private static final long serialVersionUID = -490998638396765429L;
+
+ @Override
+ public Class getEntityClass() {
+ return ThemeFile.class;
+ }
+
+ @Override
+ public String getIdAttributeName() {
+ return "fileId";
+ }
+
+ @Override
+ public Long getIdOfEntity(final ThemeFile entity) {
+ return entity.getFileId();
+ }
+
+ @Override
+ public boolean isNew(final ThemeFile entity) {
+ return entity.getFileId() == 0;
+ }
+
+ public Optional findByUuid(final String uuid,
+ final ThemeVersion version) {
+
+ final TypedQuery query = getEntityManager()
+ .createNamedQuery("ThemeFile.findByUuid", ThemeFile.class);
+ query.setParameter("uuid", uuid);
+ query.setParameter("version", version);
+
+ try {
+ return Optional.of(query.getSingleResult());
+ } catch (NoResultException ex) {
+ return Optional.empty();
+ }
+ }
+
+ public Optional findByPath(final String path,
+ final ThemeVersion version) {
+
+ final TypedQuery query = getEntityManager()
+ .createNamedQuery("ThemeFile.findByPath", ThemeFile.class);
+ query.setParameter("path", path);
+ query.setParameter("version", version);
+
+ try {
+ return Optional.of(query.getSingleResult());
+ } catch (NoResultException ex) {
+ return Optional.empty();
+ }
+ }
+
+ public Optional findByNameAndParent(final String name,
+ final Directory parent) {
+
+ final TypedQuery query = getEntityManager()
+ .createNamedQuery("ThemeFile.findByNameAndParent", ThemeFile.class);
+ query.setParameter("name", name);
+ query.setParameter("parent", parent);
+
+ try {
+ return Optional.of(query.getSingleResult());
+ } catch (NoResultException ex) {
+ return Optional.empty();
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/theming/db/ThemeRepository.java b/ccm-core/src/main/java/org/libreccm/theming/db/ThemeRepository.java
new file mode 100644
index 000000000..e8d296e9b
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/db/ThemeRepository.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 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.theming.db;
+
+import org.libreccm.core.AbstractEntityRepository;
+
+import java.util.Optional;
+
+import javax.persistence.NoResultException;
+import javax.persistence.TypedQuery;
+import javax.transaction.Transactional;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class ThemeRepository extends AbstractEntityRepository {
+
+ private static final long serialVersionUID = 2243313600794241908L;
+
+ @Override
+ public Class getEntityClass() {
+ return Theme.class;
+ }
+
+ @Override
+ public String getIdAttributeName() {
+ return "themeId";
+ }
+
+ @Override
+ public Long getIdOfEntity(final Theme entity) {
+ return entity.getThemeId();
+ }
+
+ @Override
+ public boolean isNew(final Theme entity) {
+ return entity.getThemeId() == 0;
+ }
+
+ @Override
+ public void save(final Theme theme) {
+ super.save(theme);;
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public Optional findThemeByUuid(final String uuid,
+ final ThemeVersion version) {
+
+ final TypedQuery query = getEntityManager()
+ .createNamedQuery("Theme.findByUuid", Theme.class);
+ query.setParameter("uuid", uuid);
+ query.setParameter("version", version);
+
+ try {
+ return Optional.of(query.getSingleResult());
+ } catch (NoResultException ex) {
+ return Optional.empty();
+ }
+ }
+
+ @Transactional(Transactional.TxType.REQUIRED)
+ public Optional findThemeByName(final String name,
+ final ThemeVersion version) {
+
+ final TypedQuery query = getEntityManager()
+ .createNamedQuery("Theme.findByName", Theme.class);
+ query.setParameter("name", name);
+ query.setParameter("version", version);
+
+ try {
+ return Optional.of(query.getSingleResult());
+ } catch (NoResultException ex) {
+ return Optional.empty();
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/theming/db/ThemeVersion.java b/ccm-core/src/main/java/org/libreccm/theming/db/ThemeVersion.java
new file mode 100644
index 000000000..38a3c57c6
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/theming/db/ThemeVersion.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.theming.db;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public enum ThemeVersion {
+
+ DRAFT,
+ LIVE
+
+}
diff --git a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_19__add_theme_files.sql b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_19__add_theme_files.sql
index 6577b3c8d..d8edcbead 100644
--- a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_19__add_theme_files.sql
+++ b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_19__add_theme_files.sql
@@ -18,6 +18,7 @@
NAME varchar(255) not null,
FILE_PATH varchar(8192) not null,
UUID varchar(255) not null,
+ VERSION varchar(255),
PARENT_DIRECTORY_ID bigint,
primary key (FILE_ID)
);
diff --git a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_19__add_theme_files.sql b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_19__add_theme_files.sql
index 401a19701..6af188aa7 100644
--- a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_19__add_theme_files.sql
+++ b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_19__add_theme_files.sql
@@ -1,3 +1,38 @@
+ create table CCM_CORE.THEME_DATA_FILES (
+ CREATION_DATE timestamp,
+ FILE_DATA blob,
+ LAST_MODIFIED timestamp,
+ FILE_SIZE bigint,
+ TYPE varchar(255),
+ FILE_ID bigint not null,
+ primary key (FILE_ID)
+ );
+
+ create table CCM_CORE.theme_directories (
+ FILE_ID bigint not null,
+ primary key (FILE_ID)
+ );
+
+ create table CCM_CORE.THEME_FILES (
+ FILE_ID bigint not null,
+ NAME varchar(255) not null,
+ FILE_PATH varchar(8192) not null,
+ UUID varchar(255) not null,
+ VERSION varchar(255),
+ PARENT_DIRECTORY_ID bigint,
+ primary key (FILE_ID)
+ );
+
+ create table CCM_CORE.THEMES (
+ THEME_ID bigint not null,
+ NAME varchar(255),
+ UUID varchar(255),
+ VERSION varchar(255),
+ ROOT_DIRECTORY_ID bigint,
+ primary key (THEME_ID)
+ );
+
+
alter table CCM_CORE.THEME_DATA_FILES
add constraint FK630m2y2p7pp487ofowbefrm89
foreign key (FILE_ID)
diff --git a/ccm-core/src/test/java/org/libreccm/theming/db/EqualsAndHashCodeTest.java b/ccm-core/src/test/java/org/libreccm/theming/db/EqualsAndHashCodeTest.java
new file mode 100644
index 000000000..e6de7f136
--- /dev/null
+++ b/ccm-core/src/test/java/org/libreccm/theming/db/EqualsAndHashCodeTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 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.theming.db;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.libreccm.tests.categories.UnitTest;
+import org.libreccm.testutils.EqualsVerifier;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Objects;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RunWith(Parameterized.class)
+@org.junit.experimental.categories.Category(UnitTest.class)
+public class EqualsAndHashCodeTest extends EqualsVerifier {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection> data() {
+ return Arrays.asList(new Class>[]{
+ Directory.class,
+ DataFile.class,
+ Theme.class,
+ ThemeFile.class
+ });
+ }
+
+ public EqualsAndHashCodeTest(final Class> entityClass) {
+ super(entityClass);
+ }
+
+ @Override
+ protected void addPrefabValues(
+ final nl.jqno.equalsverifier.EqualsVerifier> verifier) {
+
+ Objects.requireNonNull(verifier);
+
+ final DataFile dataFile1 = new DataFile();
+ dataFile1.setFileId(1);
+ dataFile1.setName("file1");
+
+ final DataFile dataFile2 = new DataFile();
+ dataFile2.setFileId(2);
+ dataFile2.setName("file2");
+
+ final Directory directory1 = new Directory();
+ directory1.setName("directory1");
+
+ final Directory directory2 = new Directory();
+ directory2.setName("directory2");
+
+ verifier
+ .withPrefabValues(DataFile.class, dataFile1, dataFile2)
+ .withPrefabValues(Directory.class, directory1, directory2);
+ }
+
+}
diff --git a/ccm-core/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_core_schema.sql b/ccm-core/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_core_schema.sql
index 65b1f5646..6c497c351 100644
--- a/ccm-core/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_core_schema.sql
+++ b/ccm-core/src/test/resources-wildfly-remote-h2-mem/scripts/create_ccm_core_schema.sql
@@ -589,6 +589,7 @@ drop sequence if exists HIBERNATE_SEQUENCE;
NAME varchar(255) not null,
FILE_PATH varchar(8192) not null,
UUID varchar(255) not null,
+ VERSION varchar(255),
PARENT_DIRECTORY_ID bigint,
primary key (FILE_ID)
);
diff --git a/ccm-core/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_core_schema.sql b/ccm-core/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_core_schema.sql
index 3dc429702..dc50280b9 100644
--- a/ccm-core/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_core_schema.sql
+++ b/ccm-core/src/test/resources-wildfly-remote-pgsql/scripts/create_ccm_core_schema.sql
@@ -589,6 +589,7 @@ drop sequence if exists HIBERNATE_SEQUENCE;
NAME varchar(255) not null,
FILE_PATH varchar(8192) not null,
UUID varchar(255) not null,
+ VERSION varchar(255),
PARENT_DIRECTORY_ID int8,
primary key (FILE_ID)
);