CCM NG: Next part of DatabaseThemeProvider
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5342 8810af33-2d31-482b-a856-94f89814c4dfpull/2/head
parent
982b4d4c26
commit
70d2bc50ca
|
|
@ -92,14 +92,16 @@ public interface ThemeProvider extends Serializable {
|
||||||
* should throw an NullPointerException if {@code null} is
|
* should throw an NullPointerException if {@code null} is
|
||||||
* provided as path.
|
* provided as path.
|
||||||
*
|
*
|
||||||
* @return A list of all files in the provided directory. If there is such
|
* @return A list of all files in the provided directory. If there is no
|
||||||
* path in the theme the list is empty. If the path is the path of a
|
* such path in the theme the list is empty. If the path is the path
|
||||||
* file and not a directory the list should have one element, the
|
* of a file and not a directory the list should have one element,
|
||||||
* data about the file itself.
|
* the data about the file itself.
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException If {@code theme} is an empty string,
|
* @throws IllegalArgumentException If {@code theme} is an empty string, if
|
||||||
* if there is no theme with the name provided by {@code theme} or
|
* there is no theme with the name provided
|
||||||
* if there is no file/directory with the provided path in the theme.
|
* by {@code theme} or if there is no
|
||||||
|
* file/directory with the provided path in
|
||||||
|
* the theme.
|
||||||
*/
|
*/
|
||||||
List<ThemeFileInfo> listThemeFiles(String theme,
|
List<ThemeFileInfo> listThemeFiles(String theme,
|
||||||
ThemeVersion version,
|
ThemeVersion version,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,245 @@
|
||||||
|
/*
|
||||||
|
* 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.UnexpectedErrorException;
|
||||||
|
import org.libreccm.theming.ThemeConstants;
|
||||||
|
import org.libreccm.theming.ThemeFileInfo;
|
||||||
|
import org.libreccm.theming.ThemeInfo;
|
||||||
|
import org.libreccm.theming.ThemeProvider;
|
||||||
|
import org.libreccm.theming.ThemeVersion;
|
||||||
|
import org.libreccm.theming.manifest.ThemeManifest;
|
||||||
|
import org.libreccm.theming.manifest.ThemeManifestUtil;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.enterprise.context.RequestScoped;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of {@link ThemeProvider} which serves themes from the
|
||||||
|
* database.
|
||||||
|
*
|
||||||
|
* Supports all operations.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||||
|
*/
|
||||||
|
@RequestScoped
|
||||||
|
public class DatabaseThemeProvider implements ThemeProvider {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -8661840420214119753L;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ThemeFileManager fileManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ThemeFileRepository fileRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ThemeManifestUtil manifestUtil;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ThemeManager themeManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ThemeRepository themeRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public List<ThemeInfo> getThemes() {
|
||||||
|
|
||||||
|
return themeRepository
|
||||||
|
.findAll(ThemeVersion.DRAFT)
|
||||||
|
.stream()
|
||||||
|
.map(this::createThemeInfo)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ThemeInfo> getLiveThemes() {
|
||||||
|
|
||||||
|
return themeRepository
|
||||||
|
.findAll(ThemeVersion.LIVE)
|
||||||
|
.stream()
|
||||||
|
.map(this::createThemeInfo)
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ThemeInfo> getThemeInfo(final String themeName,
|
||||||
|
final ThemeVersion version) {
|
||||||
|
|
||||||
|
return themeRepository
|
||||||
|
.findThemeByName(themeName, version)
|
||||||
|
.map(this::createThemeInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean providesTheme(final String theme,
|
||||||
|
final ThemeVersion version) {
|
||||||
|
|
||||||
|
return themeRepository
|
||||||
|
.findThemeByName(theme, version)
|
||||||
|
.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ThemeFileInfo> listThemeFiles(final String themeName,
|
||||||
|
final ThemeVersion version,
|
||||||
|
final String path) {
|
||||||
|
|
||||||
|
final Theme theme = themeRepository
|
||||||
|
.findThemeByName(path, version)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String
|
||||||
|
.format("No Theme \"%s\" in the database.", themeName)));
|
||||||
|
|
||||||
|
final Optional<ThemeFile> themeFile = fileRepository
|
||||||
|
.findByPath(theme, path, version);
|
||||||
|
|
||||||
|
final List<ThemeFileInfo> result = new ArrayList<>();
|
||||||
|
if (themeFile.isPresent()) {
|
||||||
|
if (themeFile.get() instanceof DataFile) {
|
||||||
|
|
||||||
|
result.add(themeFile.map(this::createThemeFileInfo).get());
|
||||||
|
|
||||||
|
} else if (themeFile.get() instanceof Directory) {
|
||||||
|
|
||||||
|
final Directory directory = (Directory) themeFile.get();
|
||||||
|
result.addAll(directory
|
||||||
|
.getFiles()
|
||||||
|
.stream()
|
||||||
|
.map(this::createThemeFileInfo)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("Unknown type \"%s\".",
|
||||||
|
themeFile.get().getClass().getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<InputStream> getThemeFileAsStream(String theme,
|
||||||
|
ThemeVersion version,
|
||||||
|
String path) {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream getOutputStreamForThemeFile(String theme, String path) {
|
||||||
|
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsChanges() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsDraftThemes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishTheme(final String themeName) {
|
||||||
|
|
||||||
|
themeRepository
|
||||||
|
.findThemeByName(themeName, ThemeVersion.DRAFT)
|
||||||
|
.ifPresent(themeManager::publishTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ThemeInfo createThemeInfo(final Theme theme) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(theme);
|
||||||
|
|
||||||
|
final Optional<ThemeFile> manifestFileJson = fileRepository
|
||||||
|
.findByNameAndParent(ThemeConstants.THEME_MANIFEST_JSON,
|
||||||
|
theme.getRootDirectory());
|
||||||
|
final Optional<ThemeFile> manifestFileXml = fileRepository
|
||||||
|
.findByNameAndParent(ThemeConstants.THEME_MANIFEST_XML,
|
||||||
|
theme.getRootDirectory());
|
||||||
|
|
||||||
|
final DataFile manifestFile;
|
||||||
|
final String filename;
|
||||||
|
if (manifestFileJson.isPresent()) {
|
||||||
|
manifestFile = (DataFile) manifestFileJson.get();
|
||||||
|
filename = ThemeConstants.THEME_MANIFEST_JSON;
|
||||||
|
} else if (manifestFileXml.isPresent()) {
|
||||||
|
manifestFile = (DataFile) manifestFileXml.get();
|
||||||
|
filename = ThemeConstants.THEME_MANIFEST_XML;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("No manifest file found for theme \"%s\".",
|
||||||
|
theme.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
try (final InputStream inputStream = new ByteArrayInputStream(
|
||||||
|
manifestFile.getData())) {
|
||||||
|
final ThemeManifest manifest = manifestUtil
|
||||||
|
.loadManifest(inputStream, filename);
|
||||||
|
final ThemeInfo themeInfo = new ThemeInfo();
|
||||||
|
themeInfo.setManifest(manifest);
|
||||||
|
themeInfo.setProvider(getClass());
|
||||||
|
themeInfo.setVersion(theme.getVersion());
|
||||||
|
|
||||||
|
return themeInfo;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UnexpectedErrorException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ThemeFileInfo createThemeFileInfo(final ThemeFile file) {
|
||||||
|
|
||||||
|
final ThemeFileInfo fileInfo = new ThemeFileInfo();
|
||||||
|
|
||||||
|
fileInfo.setName(file.getName());
|
||||||
|
fileInfo.setWritable(true);
|
||||||
|
|
||||||
|
if (file instanceof DataFile) {
|
||||||
|
|
||||||
|
final DataFile dataFile = (DataFile) file;
|
||||||
|
|
||||||
|
fileInfo.setDirectory(false);
|
||||||
|
fileInfo.setMimeType(dataFile.getType());
|
||||||
|
fileInfo.setSize(dataFile.getSize());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file instanceof Directory) {
|
||||||
|
fileInfo.setDirectory(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,7 +23,6 @@ import org.libreccm.core.CoreConstants;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
package org.libreccm.theming.db;
|
package org.libreccm.theming.db;
|
||||||
|
|
||||||
import org.libreccm.core.CoreConstants;
|
import org.libreccm.core.CoreConstants;
|
||||||
|
import org.libreccm.theming.ThemeVersion;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -43,6 +44,9 @@ import javax.persistence.Table;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "THEMES", schema = CoreConstants.DB_SCHEMA)
|
@Table(name = "THEMES", schema = CoreConstants.DB_SCHEMA)
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
|
@NamedQuery(name = "Theme.findAllForVersion",
|
||||||
|
query = "SELECT t FROM Theme t WHERE t.version = :version")
|
||||||
|
,
|
||||||
@NamedQuery(name = "Theme.findByUuid",
|
@NamedQuery(name = "Theme.findByUuid",
|
||||||
query = "SELECT t FROM Theme t "
|
query = "SELECT t FROM Theme t "
|
||||||
+ "WHERE t.uuid = :uuid "
|
+ "WHERE t.uuid = :uuid "
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
package org.libreccm.theming.db;
|
package org.libreccm.theming.db;
|
||||||
|
|
||||||
import org.libreccm.core.CoreConstants;
|
import org.libreccm.core.CoreConstants;
|
||||||
|
import org.libreccm.theming.ThemeVersion;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -88,6 +89,10 @@ public class ThemeFile implements Serializable {
|
||||||
@NotNull
|
@NotNull
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "THEME_ID")
|
||||||
|
private Theme theme;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne
|
||||||
@JoinColumn(name = "PARENT_DIRECTORY_ID")
|
@JoinColumn(name = "PARENT_DIRECTORY_ID")
|
||||||
private Directory parent;
|
private Directory parent;
|
||||||
|
|
@ -132,6 +137,14 @@ public class ThemeFile implements Serializable {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Theme getTheme() {
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setTheme(final Theme theme) {
|
||||||
|
this.theme = theme;
|
||||||
|
}
|
||||||
|
|
||||||
public Directory getParent() {
|
public Directory getParent() {
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +160,7 @@ public class ThemeFile implements Serializable {
|
||||||
hash = 37 * hash + Objects.hashCode(name);
|
hash = 37 * hash + Objects.hashCode(name);
|
||||||
hash = 37 * hash + Objects.hashCode(path);
|
hash = 37 * hash + Objects.hashCode(path);
|
||||||
hash = 37 * hash + Objects.hashCode(uuid);
|
hash = 37 * hash + Objects.hashCode(uuid);
|
||||||
|
hash = 37 * hash + Objects.hashCode(theme);
|
||||||
hash = 37 * hash + Objects.hashCode(parent);
|
hash = 37 * hash + Objects.hashCode(parent);
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +192,10 @@ public class ThemeFile implements Serializable {
|
||||||
if (!Objects.equals(uuid, other.getUuid())) {
|
if (!Objects.equals(uuid, other.getUuid())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!Objects.equals(theme, other.getTheme())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return Objects.equals(parent, other.getParent());
|
return Objects.equals(parent, other.getParent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,13 +213,17 @@ public class ThemeFile implements Serializable {
|
||||||
+ "fileId = %d, "
|
+ "fileId = %d, "
|
||||||
+ "name = \"%s\", "
|
+ "name = \"%s\", "
|
||||||
+ "path = \"%s\", "
|
+ "path = \"%s\", "
|
||||||
+ "uuid = \"%s\"%s"
|
+ "uuid = \"%s\", "
|
||||||
|
+ "theme = \"%s\", "
|
||||||
|
+ "parent = \"%s\"%s"
|
||||||
+ " }",
|
+ " }",
|
||||||
super.toString(),
|
super.toString(),
|
||||||
fileId,
|
fileId,
|
||||||
name,
|
name,
|
||||||
path,
|
path,
|
||||||
uuid,
|
uuid,
|
||||||
|
Objects.toString(theme),
|
||||||
|
Objects.toString(parent),
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,15 @@
|
||||||
package org.libreccm.theming.db;
|
package org.libreccm.theming.db;
|
||||||
|
|
||||||
import org.libreccm.security.RequiresPrivilege;
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
import org.libreccm.theming.ThemeVersion;
|
||||||
import org.libreccm.theming.ThemingPrivileges;
|
import org.libreccm.theming.ThemingPrivileges;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides methods for managing the files of the theme stored in the database.
|
* Provides methods for managing the files of the theme stored in the database.
|
||||||
|
|
@ -35,32 +40,93 @@ public class ThemeFileManager {
|
||||||
@Inject
|
@Inject
|
||||||
private ThemeFileRepository fileRepository;
|
private ThemeFileRepository fileRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ThemeRepository themeRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link DataFile}.
|
* Creates a new, empty {@link DataFile}.
|
||||||
*
|
*
|
||||||
* @param parent The directory in which the {@link DataFile} is created.
|
* @param theme The {@link Theme} to which the file belongs.
|
||||||
|
* @param parent The {@link Directory} in which the {@link DataFile} is
|
||||||
|
* created.
|
||||||
* @param name The name of the new {@link DataFile}.
|
* @param name The name of the new {@link DataFile}.
|
||||||
*
|
*
|
||||||
* @return The new {@link DataFile}.
|
* @return The new {@link DataFile}.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
public DataFile createDataFile(final Directory parent,
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public DataFile createDataFile(final Theme theme,
|
||||||
|
final Directory parent,
|
||||||
final String name) {
|
final String name) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(parent);
|
||||||
|
Objects.requireNonNull(name);
|
||||||
|
|
||||||
|
if (name.matches("\\s*")) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The name of file can't be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Date now = new Date();
|
||||||
|
final String path = String.join("/", parent.getPath(), name);
|
||||||
|
|
||||||
|
final DataFile dataFile = new DataFile();
|
||||||
|
dataFile.setCreationDate(now);
|
||||||
|
dataFile.setLastModified(now);
|
||||||
|
dataFile.setName(name);
|
||||||
|
dataFile.setParent(parent);
|
||||||
|
dataFile.setPath(path);
|
||||||
|
dataFile.setTheme(theme);
|
||||||
|
dataFile.setVersion(ThemeVersion.DRAFT);
|
||||||
|
|
||||||
|
parent.addFile(dataFile);
|
||||||
|
|
||||||
|
fileRepository.save(dataFile);
|
||||||
|
fileRepository.save(parent);
|
||||||
|
themeRepository.save(theme);
|
||||||
|
|
||||||
|
return dataFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link Directory}.
|
* Creates a new {@link Directory}.
|
||||||
*
|
*
|
||||||
|
* @param theme The {@link Theme} to which the file belongs.
|
||||||
* @param parent The parent directory of the new {@link Directory}.
|
* @param parent The parent directory of the new {@link Directory}.
|
||||||
* @param name The name of the new {@link Directory}
|
* @param name The name of the new {@link Directory}
|
||||||
*
|
*
|
||||||
* @return The new {@link Directory}.
|
* @return The new {@link Directory}.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
public Directory createDirectory(final Directory parent,
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public Directory createDirectory(final Theme theme,
|
||||||
|
final Directory parent,
|
||||||
final String name) {
|
final String name) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(parent);
|
||||||
|
Objects.requireNonNull(name);
|
||||||
|
|
||||||
|
if (name.matches("\\s*")) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The name of file can't be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String path = String.join("/", parent.getPath(), name);
|
||||||
|
|
||||||
|
final Directory directory = new Directory();
|
||||||
|
directory.setName(name);
|
||||||
|
directory.setParent(parent);
|
||||||
|
directory.setPath(path);
|
||||||
|
directory.setTheme(theme);
|
||||||
|
directory.setVersion(ThemeVersion.DRAFT);
|
||||||
|
|
||||||
|
parent.addFile(directory);
|
||||||
|
|
||||||
|
fileRepository.save(directory);
|
||||||
|
fileRepository.save(parent);
|
||||||
|
themeRepository.save(theme);
|
||||||
|
|
||||||
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -70,8 +136,33 @@ public class ThemeFileManager {
|
||||||
* @param file The {@link ThemeFile} to delete.
|
* @param file The {@link ThemeFile} to delete.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void delete(final ThemeFile file) {
|
public void delete(final ThemeFile file) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(file);
|
||||||
|
|
||||||
|
if (file instanceof DataFile) {
|
||||||
|
final Directory parent = file.getParent();
|
||||||
|
parent.removeFile(file);
|
||||||
|
fileRepository.delete(file);
|
||||||
|
fileRepository.save(parent);
|
||||||
|
} else if (file instanceof Directory) {
|
||||||
|
final Directory directory = (Directory) file;
|
||||||
|
if (directory.getFiles().isEmpty()) {
|
||||||
|
final Directory parent = file.getParent();
|
||||||
|
parent.removeFile(file);
|
||||||
|
fileRepository.delete(file);
|
||||||
|
fileRepository.save(parent);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("File \"%s\" is a directory and not empty.",
|
||||||
|
directory.getPath()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("Don't know how handle file type \"%s\".",
|
||||||
|
file.getClass().getName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -84,8 +175,28 @@ public class ThemeFileManager {
|
||||||
* @param file The {@link ThemeFile} to delete.
|
* @param file The {@link ThemeFile} to delete.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void deleteRecursive(final ThemeFile file) {
|
public void deleteRecursive(final ThemeFile file) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(file);
|
||||||
|
|
||||||
|
if (file instanceof DataFile) {
|
||||||
|
delete(file);
|
||||||
|
} else if (file instanceof Directory) {
|
||||||
|
|
||||||
|
final Directory directory = (Directory) file;
|
||||||
|
directory
|
||||||
|
.getFiles()
|
||||||
|
.forEach(subFile -> deleteRecursive(subFile));
|
||||||
|
final Directory parent = file.getParent();
|
||||||
|
parent.removeFile(file);
|
||||||
|
fileRepository.delete(file);
|
||||||
|
fileRepository.save(parent);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("Don't know how handle file type \"%s\".",
|
||||||
|
file.getClass().getName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -98,8 +209,13 @@ public class ThemeFileManager {
|
||||||
* @return The newly created copy.
|
* @return The newly created copy.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public ThemeFile copy(final ThemeFile file, final Directory target) {
|
public ThemeFile copy(final ThemeFile file, final Directory target) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(file);
|
||||||
|
Objects.requireNonNull(target);
|
||||||
|
|
||||||
|
return copy(file, target, file.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -112,10 +228,73 @@ public class ThemeFileManager {
|
||||||
* @return The copy.
|
* @return The copy.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public ThemeFile copy(final ThemeFile file,
|
public ThemeFile copy(final ThemeFile file,
|
||||||
final Directory target,
|
final Directory target,
|
||||||
final String nameOfCopy) {
|
final String nameOfCopy) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(file);
|
||||||
|
Objects.requireNonNull(target);
|
||||||
|
Objects.requireNonNull(nameOfCopy);
|
||||||
|
|
||||||
|
if (nameOfCopy.matches("\\s*")) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The name of the copy can't be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
target
|
||||||
|
.getFiles()
|
||||||
|
.stream()
|
||||||
|
.filter(subFile -> subFile.getName().equals(nameOfCopy))
|
||||||
|
.findAny()
|
||||||
|
.ifPresent(subFile -> {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("The target directory \"%s\"already contains a "
|
||||||
|
+ "file with name \"%s\".",
|
||||||
|
target.getPath(),
|
||||||
|
nameOfCopy));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file instanceof DataFile) {
|
||||||
|
|
||||||
|
final DataFile source = (DataFile) file;
|
||||||
|
final DataFile copy = new DataFile();
|
||||||
|
final Date now = new Date();
|
||||||
|
copy.setCreationDate(now);
|
||||||
|
copy.setData(source.getData());
|
||||||
|
copy.setLastModified(now);
|
||||||
|
copy.setName(nameOfCopy);
|
||||||
|
copy.setParent(target);
|
||||||
|
copy.setPath(String.join("/", target.getPath(), copy.getName()));
|
||||||
|
copy.setSize(source.getSize());
|
||||||
|
copy.setTheme(source.getTheme());
|
||||||
|
copy.setType(source.getType());
|
||||||
|
copy.setVersion(source.getVersion());
|
||||||
|
|
||||||
|
fileRepository.save(copy);
|
||||||
|
fileRepository.save(target);
|
||||||
|
themeRepository.save(copy.getTheme());
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
} else if (file instanceof Directory) {
|
||||||
|
|
||||||
|
final Directory source = (Directory) file;
|
||||||
|
final Directory copy = new Directory();
|
||||||
|
copy.setName(nameOfCopy);
|
||||||
|
copy.setParent(target);
|
||||||
|
copy.setPath(String.join("/", target.getPath(), copy.getName()));
|
||||||
|
copy.setTheme(source.getTheme());
|
||||||
|
copy.setVersion(source.getVersion());
|
||||||
|
|
||||||
|
fileRepository.save(copy);
|
||||||
|
fileRepository.save(target);
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("Don't know how handle file type \"%s\".",
|
||||||
|
file.getClass().getName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -128,9 +307,14 @@ public class ThemeFileManager {
|
||||||
* @return The copy.
|
* @return The copy.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public ThemeFile copyRecursive(final ThemeFile file,
|
public ThemeFile copyRecursive(final ThemeFile file,
|
||||||
final Directory target) {
|
final Directory target) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(file);
|
||||||
|
Objects.requireNonNull(target);
|
||||||
|
|
||||||
|
return copyRecursive(file, target, file.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -144,10 +328,32 @@ public class ThemeFileManager {
|
||||||
* @return The copy.
|
* @return The copy.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public ThemeFile copyRecursive(final ThemeFile file,
|
public ThemeFile copyRecursive(final ThemeFile file,
|
||||||
final Directory target,
|
final Directory target,
|
||||||
final String nameOfCopy) {
|
final String nameOfCopy) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(file);
|
||||||
|
Objects.requireNonNull(target);
|
||||||
|
Objects.requireNonNull(nameOfCopy);
|
||||||
|
|
||||||
|
if (nameOfCopy.matches("\\s*")) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The name of a file can't be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final ThemeFile copy = copy(file, target, nameOfCopy);
|
||||||
|
|
||||||
|
if (file instanceof Directory) {
|
||||||
|
final Directory source = (Directory) file;
|
||||||
|
final Directory copiedDirectory = (Directory) copy;
|
||||||
|
|
||||||
|
source
|
||||||
|
.getFiles()
|
||||||
|
.forEach(subFile -> copyRecursive(subFile, copiedDirectory));
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -161,7 +367,11 @@ public class ThemeFileManager {
|
||||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||||
public ThemeFile move(final ThemeFile file,
|
public ThemeFile move(final ThemeFile file,
|
||||||
final Directory target) {
|
final Directory target) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(file);
|
||||||
|
Objects.requireNonNull(target);
|
||||||
|
|
||||||
|
return move(file, target, file.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -177,7 +387,42 @@ public class ThemeFileManager {
|
||||||
public ThemeFile move(final ThemeFile file,
|
public ThemeFile move(final ThemeFile file,
|
||||||
final Directory target,
|
final Directory target,
|
||||||
final String newName) {
|
final String newName) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(file);
|
||||||
|
Objects.requireNonNull(target);
|
||||||
|
Objects.requireNonNull(newName);
|
||||||
|
|
||||||
|
if (newName.matches("\\s*")) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The name of a file can't be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
target
|
||||||
|
.getFiles()
|
||||||
|
.stream()
|
||||||
|
.filter(subFile -> subFile.getName().equals(newName))
|
||||||
|
.findAny()
|
||||||
|
.ifPresent(subFile -> {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("The target directory \"%s\"already contains a "
|
||||||
|
+ "file with name \"%s\".",
|
||||||
|
target.getPath(),
|
||||||
|
newName));
|
||||||
|
});
|
||||||
|
|
||||||
|
final Directory oldParent = file.getParent();
|
||||||
|
|
||||||
|
file.setName(newName);
|
||||||
|
oldParent.removeFile(file);
|
||||||
|
target.addFile(file);
|
||||||
|
file.setParent(target);
|
||||||
|
|
||||||
|
fileRepository.save(file);
|
||||||
|
fileRepository.save(oldParent);
|
||||||
|
fileRepository.save(target);
|
||||||
|
|
||||||
|
return file;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,17 @@
|
||||||
package org.libreccm.theming.db;
|
package org.libreccm.theming.db;
|
||||||
|
|
||||||
import org.libreccm.core.AbstractEntityRepository;
|
import org.libreccm.core.AbstractEntityRepository;
|
||||||
|
import org.libreccm.core.UnexpectedErrorException;
|
||||||
|
import org.libreccm.theming.ThemeVersion;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.persistence.NoResultException;
|
import javax.persistence.NoResultException;
|
||||||
|
|
@ -55,6 +64,33 @@ public class ThemeFileRepository extends AbstractEntityRepository<Long, ThemeFil
|
||||||
return entity.getFileId() == 0;
|
return entity.getFileId() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initNewEntity(final ThemeFile themeFile) {
|
||||||
|
if (themeFile.getUuid() == null || themeFile.getUuid().isEmpty()) {
|
||||||
|
themeFile.setUuid(UUID.randomUUID().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(final ThemeFile file) {
|
||||||
|
|
||||||
|
if (file instanceof DataFile) {
|
||||||
|
final DataFile dataFile = (DataFile) file;
|
||||||
|
dataFile.setLastModified(new Date());
|
||||||
|
try (final InputStream inputStream = new BufferedInputStream(
|
||||||
|
new ByteArrayInputStream(dataFile.getData()))) {
|
||||||
|
|
||||||
|
final String mimeType = URLConnection
|
||||||
|
.guessContentTypeFromStream(inputStream);
|
||||||
|
dataFile.setType(mimeType);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UnexpectedErrorException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.save(file);
|
||||||
|
}
|
||||||
|
|
||||||
public Optional<ThemeFile> findByUuid(final String uuid,
|
public Optional<ThemeFile> findByUuid(final String uuid,
|
||||||
final ThemeVersion version) {
|
final ThemeVersion version) {
|
||||||
|
|
||||||
|
|
@ -70,7 +106,8 @@ public class ThemeFileRepository extends AbstractEntityRepository<Long, ThemeFil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ThemeFile> findByPath(final String path,
|
public Optional<ThemeFile> findByPath(final Theme theme,
|
||||||
|
final String path,
|
||||||
final ThemeVersion version) {
|
final ThemeVersion version) {
|
||||||
|
|
||||||
final TypedQuery<ThemeFile> query = getEntityManager()
|
final TypedQuery<ThemeFile> query = getEntityManager()
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,18 @@
|
||||||
package org.libreccm.theming.db;
|
package org.libreccm.theming.db;
|
||||||
|
|
||||||
import org.libreccm.security.RequiresPrivilege;
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
import org.libreccm.theming.ThemeConstants;
|
||||||
|
import org.libreccm.theming.ThemeVersion;
|
||||||
import org.libreccm.theming.ThemingPrivileges;
|
import org.libreccm.theming.ThemingPrivileges;
|
||||||
import org.libreccm.theming.manifest.ThemeManifest;
|
import org.libreccm.theming.manifest.ThemeManifest;
|
||||||
|
import org.libreccm.theming.manifest.ThemeManifestUtil;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides methods for managing themes stored in the database.
|
* Provides methods for managing themes stored in the database.
|
||||||
|
|
@ -33,9 +40,15 @@ import javax.inject.Inject;
|
||||||
@RequestScoped
|
@RequestScoped
|
||||||
public class ThemeManager {
|
public class ThemeManager {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ThemeManifestUtil manifestUtil;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ThemeRepository themeRepository;
|
private ThemeRepository themeRepository;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ThemeFileRepository themeFileRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new theme, including the root directory and a theme manifest
|
* Creates a new theme, including the root directory and a theme manifest
|
||||||
* file.
|
* file.
|
||||||
|
|
@ -47,8 +60,44 @@ public class ThemeManager {
|
||||||
* @return The new theme.
|
* @return The new theme.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public Theme createTheme(final String name) {
|
public Theme createTheme(final String name) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(name);
|
||||||
|
|
||||||
|
if (name.matches("\\s*")) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"The name of a theme can't be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Theme theme = new Theme();
|
||||||
|
theme.setName(name);
|
||||||
|
theme.setVersion(ThemeVersion.DRAFT);
|
||||||
|
|
||||||
|
final Directory root = new Directory();
|
||||||
|
root.setName(name);
|
||||||
|
root.setPath("/");
|
||||||
|
root.setTheme(theme);
|
||||||
|
|
||||||
|
final ThemeManifest manifest = new ThemeManifest();
|
||||||
|
manifest.setName(name);
|
||||||
|
|
||||||
|
final DataFile manifestFile = new DataFile();
|
||||||
|
manifestFile.setName(ThemeConstants.THEME_MANIFEST_JSON);
|
||||||
|
manifestFile.setPath(String.format("/%s",
|
||||||
|
ThemeConstants.THEME_MANIFEST_JSON));
|
||||||
|
manifestFile.setTheme(theme);
|
||||||
|
|
||||||
|
final String manifestData = manifestUtil
|
||||||
|
.serializeManifest(manifest, ThemeConstants.THEME_MANIFEST_JSON);
|
||||||
|
manifestFile.setData(manifestData.getBytes());
|
||||||
|
root.addFile(manifestFile);
|
||||||
|
|
||||||
|
themeRepository.save(theme);
|
||||||
|
themeFileRepository.save(root);
|
||||||
|
themeFileRepository.save(manifestFile);
|
||||||
|
|
||||||
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -57,8 +106,71 @@ public class ThemeManager {
|
||||||
* @param theme The theme to delete.
|
* @param theme The theme to delete.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void deleteTheme(final Theme theme) {
|
public void deleteTheme(final Theme theme) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(theme);
|
||||||
|
|
||||||
|
if (isLive(theme)) {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("The theme \"%s\" is live and can't be deleted.",
|
||||||
|
theme.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
themeRepository.delete(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a theme has a live version.
|
||||||
|
*
|
||||||
|
* @param theme The theme.
|
||||||
|
*
|
||||||
|
* @return {@code true} if there is a live version of the provided theme,
|
||||||
|
* {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public boolean isLive(final Theme theme) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(theme);
|
||||||
|
|
||||||
|
return themeRepository
|
||||||
|
.findThemeByUuid(theme.getUuid(), ThemeVersion.LIVE)
|
||||||
|
.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public Theme getDraftTheme(final Theme theme) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(theme);
|
||||||
|
|
||||||
|
if (theme.getVersion() == ThemeVersion.DRAFT) {
|
||||||
|
return theme;
|
||||||
|
} else {
|
||||||
|
return themeRepository
|
||||||
|
.findThemeByUuid(theme.getUuid(), ThemeVersion.DRAFT)
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException(String
|
||||||
|
.format("No draft theme with UUID \"%s\" in the database.",
|
||||||
|
theme.getUuid())));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the live version of a theme.
|
||||||
|
*
|
||||||
|
* @param theme The theme.
|
||||||
|
*
|
||||||
|
* @return An {@link Optional} containing the live version of the provided
|
||||||
|
* theme or an empty {@link Optional} if the theme has no live
|
||||||
|
* version.
|
||||||
|
*/
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
public Optional<Theme> getLiveTheme(final Theme theme) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(theme);
|
||||||
|
|
||||||
|
return themeRepository
|
||||||
|
.findThemeByUuid(theme.getUuid(), ThemeVersion.LIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -71,18 +183,149 @@ public class ThemeManager {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void publishTheme(final Theme theme) {
|
public void publishTheme(final Theme theme) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(theme);
|
||||||
|
|
||||||
|
final Theme draftTheme;
|
||||||
|
if (theme.getVersion() == ThemeVersion.DRAFT) {
|
||||||
|
draftTheme = theme;
|
||||||
|
} else {
|
||||||
|
draftTheme = getDraftTheme(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLive(draftTheme)) {
|
||||||
|
unpublishTheme(draftTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Theme liveTheme = new Theme();
|
||||||
|
liveTheme.setName(draftTheme.getName());
|
||||||
|
liveTheme.setUuid(draftTheme.getUuid());
|
||||||
|
liveTheme.setVersion(ThemeVersion.LIVE);
|
||||||
|
|
||||||
|
final Directory liveRoot = new Directory();
|
||||||
|
liveRoot.setName(draftTheme.getRootDirectory().getName());
|
||||||
|
liveRoot.setPath(draftTheme.getRootDirectory().getPath());
|
||||||
|
liveRoot.setUuid(draftTheme.getRootDirectory().getUuid());
|
||||||
|
liveRoot.setTheme(theme);
|
||||||
|
liveRoot.setVersion(ThemeVersion.LIVE);
|
||||||
|
|
||||||
|
themeRepository.save(liveTheme);
|
||||||
|
themeFileRepository.save(liveRoot);
|
||||||
|
|
||||||
|
draftTheme
|
||||||
|
.getRootDirectory()
|
||||||
|
.getFiles()
|
||||||
|
.forEach(file -> publishFile(liveTheme, liveRoot, file));
|
||||||
|
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void publishFile(final Theme liveTheme,
|
||||||
|
final Directory liveParent,
|
||||||
|
final ThemeFile draftFile) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(liveParent);
|
||||||
|
Objects.requireNonNull(draftFile);
|
||||||
|
|
||||||
|
if (liveParent.getVersion() != ThemeVersion.LIVE) {
|
||||||
|
throw new IllegalArgumentException("Parent directory is not live.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (draftFile.getVersion() != ThemeVersion.DRAFT) {
|
||||||
|
throw new IllegalArgumentException("File to publish is not draft.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (draftFile instanceof Directory) {
|
||||||
|
|
||||||
|
final Directory draftDirectory = (Directory) draftFile;
|
||||||
|
|
||||||
|
final Directory liveDirectory = new Directory();
|
||||||
|
liveDirectory.setName(draftDirectory.getName());
|
||||||
|
liveDirectory.setPath(draftDirectory.getPath());
|
||||||
|
liveDirectory.setParent(liveParent);
|
||||||
|
liveDirectory.setUuid(draftDirectory.getUuid());
|
||||||
|
liveDirectory.setVersion(ThemeVersion.LIVE);
|
||||||
|
liveDirectory.setTheme(liveTheme);
|
||||||
|
|
||||||
|
themeFileRepository.save(liveDirectory);
|
||||||
|
|
||||||
|
draftDirectory
|
||||||
|
.getFiles()
|
||||||
|
.forEach(file -> publishFile(liveTheme, liveDirectory, file));
|
||||||
|
|
||||||
|
} else if (draftFile instanceof DataFile) {
|
||||||
|
|
||||||
|
final DataFile draftDataFile = (DataFile) draftFile;
|
||||||
|
|
||||||
|
final DataFile liveDataFile = new DataFile();
|
||||||
|
liveDataFile.setCreationDate(draftDataFile.getCreationDate());
|
||||||
|
liveDataFile.setData(draftDataFile.getData());
|
||||||
|
liveDataFile.setLastModified(draftDataFile.getLastModified());
|
||||||
|
liveDataFile.setName(draftDataFile.getName());
|
||||||
|
liveDataFile.setParent(liveParent);
|
||||||
|
liveDataFile.setPath(draftDataFile.getPath());
|
||||||
|
liveDataFile.setSize(draftDataFile.getSize());
|
||||||
|
liveDataFile.setType(draftDataFile.getType());
|
||||||
|
liveDataFile.setUuid(draftDataFile.getUuid());
|
||||||
|
liveDataFile.setTheme(liveTheme);
|
||||||
|
liveDataFile.setVersion(ThemeVersion.LIVE);
|
||||||
|
|
||||||
|
themeFileRepository.save(liveDataFile);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("Don't know how handle file type \"%s\".",
|
||||||
|
draftFile.getClass().getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unpublishes a theme by deleting the live version of the theme.
|
* Unpublishes a theme by deleting the live version of the theme. If the
|
||||||
|
* theme is not published the method will return without doing anything.
|
||||||
*
|
*
|
||||||
* @param theme The theme to unpublish.
|
* @param theme The theme to unpublish.
|
||||||
*/
|
*/
|
||||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||||
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
public void unpublishTheme(final Theme theme) {
|
public void unpublishTheme(final Theme theme) {
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
|
Objects.requireNonNull(theme);
|
||||||
|
|
||||||
|
if (!isLive(theme)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Theme liveTheme = getLiveTheme(theme).get();
|
||||||
|
final Directory liveRoot = liveTheme.getRootDirectory();
|
||||||
|
|
||||||
|
liveRoot
|
||||||
|
.getFiles()
|
||||||
|
.forEach(file -> unpublishFile(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unpublishFile(final ThemeFile themeFile) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(themeFile);
|
||||||
|
if (themeFile.getVersion() != ThemeVersion.LIVE) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Only live files can be unpublished.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (themeFile instanceof DataFile) {
|
||||||
|
themeFileRepository.delete(themeFile);
|
||||||
|
} else if (themeFile instanceof Directory) {
|
||||||
|
final Directory directory = (Directory) themeFile;
|
||||||
|
directory
|
||||||
|
.getFiles()
|
||||||
|
.forEach(file -> unpublishFile(file));
|
||||||
|
themeFileRepository.delete(themeFile);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String
|
||||||
|
.format("Don't know how handle file type \"%s\".",
|
||||||
|
themeFile.getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,13 @@
|
||||||
package org.libreccm.theming.db;
|
package org.libreccm.theming.db;
|
||||||
|
|
||||||
import org.libreccm.core.AbstractEntityRepository;
|
import org.libreccm.core.AbstractEntityRepository;
|
||||||
|
import org.libreccm.security.RequiresPrivilege;
|
||||||
|
import org.libreccm.theming.ThemeVersion;
|
||||||
|
import org.libreccm.theming.ThemingPrivileges;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.enterprise.context.RequestScoped;
|
import javax.enterprise.context.RequestScoped;
|
||||||
import javax.persistence.NoResultException;
|
import javax.persistence.NoResultException;
|
||||||
|
|
@ -57,8 +62,25 @@ public class ThemeRepository extends AbstractEntityRepository<Long, Theme> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public void initNewEntity(final Theme theme) {
|
||||||
|
if (theme.getUuid() == null || theme.getUuid().isEmpty()) {
|
||||||
|
theme.setUuid(UUID.randomUUID().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||||
public void save(final Theme theme) {
|
public void save(final Theme theme) {
|
||||||
super.save(theme);;
|
super.save(theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Theme> findAll(final ThemeVersion version) {
|
||||||
|
|
||||||
|
final TypedQuery<Theme> query = getEntityManager()
|
||||||
|
.createNamedQuery("Theme.findAllForVersion", Theme.class);
|
||||||
|
query.setParameter("version", version);
|
||||||
|
|
||||||
|
return query.getResultList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(Transactional.TxType.REQUIRED)
|
@Transactional(Transactional.TxType.REQUIRED)
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
|
||||||
*/
|
|
||||||
public enum ThemeVersion {
|
|
||||||
|
|
||||||
DRAFT,
|
|
||||||
LIVE
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -32,6 +32,7 @@ import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
@ -60,7 +61,6 @@ public class ThemeManifestUtil implements Serializable {
|
||||||
public ThemeManifest loadManifest(final Path path) {
|
public ThemeManifest loadManifest(final Path path) {
|
||||||
|
|
||||||
// final String pathStr = path.toString().toLowerCase(Locale.ROOT);
|
// final String pathStr = path.toString().toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
final BufferedReader reader;
|
final BufferedReader reader;
|
||||||
try {
|
try {
|
||||||
reader = Files.newBufferedReader(path, Charset.forName("UTF-8"));
|
reader = Files.newBufferedReader(path, Charset.forName("UTF-8"));
|
||||||
|
|
@ -130,7 +130,38 @@ public class ThemeManifestUtil implements Serializable {
|
||||||
// return manifest;
|
// return manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ThemeManifest parseManifest(final Reader reader, final String path) {
|
public String serializeManifest(final ThemeManifest manifest,
|
||||||
|
final String format) {
|
||||||
|
|
||||||
|
final ObjectMapper mapper;
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case THEME_MANIFEST_JSON:
|
||||||
|
mapper = new ObjectMapper();
|
||||||
|
break;
|
||||||
|
case THEME_MANIFEST_XML:
|
||||||
|
final JacksonXmlModule xmlModule = new JacksonXmlModule();
|
||||||
|
mapper = new XmlMapper(xmlModule);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unsupported format for ThemeManifest");
|
||||||
|
}
|
||||||
|
|
||||||
|
mapper.registerModule(new JaxbAnnotationModule());
|
||||||
|
|
||||||
|
final StringWriter writer = new StringWriter();
|
||||||
|
try {
|
||||||
|
mapper.writeValue(writer, manifest);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new UnexpectedErrorException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ThemeManifest parseManifest(final Reader reader,
|
||||||
|
final String path) {
|
||||||
|
|
||||||
final String pathStr = path.toLowerCase(Locale.ROOT);
|
final String pathStr = path.toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue