CCM NG: Second part of api for theme manager app

git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5692 8810af33-2d31-482b-a856-94f89814c4df
ccm-docs
jensp 2018-09-14 18:01:57 +00:00
parent ac86ccab91
commit 93938b1a11
6 changed files with 153 additions and 42 deletions

View File

@ -44,7 +44,6 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.rmi.UnexpectedException;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -234,14 +233,16 @@ public class NIOFileSystemAdapter implements FileSystemAdapter {
final String targetPath, final String targetPath,
boolean recursive) throws FileAccessException { boolean recursive) throws FileAccessException {
final Path nioSourcePath = Paths.get(sourcePath); final Path nioSourcePath = Paths
final Path nioTargetPath = Paths.get(targetPath); .get(String.join("/", dataPath, sourcePath));
final Path nioTargetPath = Paths
.get(String.join("/", dataPath, targetPath));
if (recursive) { if (recursive) {
try { try {
Files.walkFileTree( Files.walkFileTree(
nioTargetPath, nioSourcePath,
new FileVisitor<Path>() { new FileVisitor<Path>() {
@Override @Override
@ -262,7 +263,7 @@ public class NIOFileSystemAdapter implements FileSystemAdapter {
file, file,
nioTargetPath nioTargetPath
.resolve(nioSourcePath.relativize(file)), .resolve(nioSourcePath.relativize(file)),
StandardCopyOption.ATOMIC_MOVE, // StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.REPLACE_EXISTING,
LinkOption.NOFOLLOW_LINKS); LinkOption.NOFOLLOW_LINKS);
@ -311,7 +312,7 @@ public class NIOFileSystemAdapter implements FileSystemAdapter {
try { try {
Files.copy(nioSourcePath, Files.copy(nioSourcePath,
nioTargetPath, nioTargetPath,
StandardCopyOption.ATOMIC_MOVE, // StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.REPLACE_EXISTING,
LinkOption.NOFOLLOW_LINKS); LinkOption.NOFOLLOW_LINKS);
@ -325,14 +326,16 @@ public class NIOFileSystemAdapter implements FileSystemAdapter {
public void move(final String sourcePath, final String targetPath) public void move(final String sourcePath, final String targetPath)
throws FileAccessException { throws FileAccessException {
final Path nioSourcePath = Paths.get(sourcePath); final Path nioSourcePath = Paths.get(
final Path nioTargetPath = Paths.get(targetPath); String.join("/", dataPath, sourcePath));
final Path nioTargetPath = Paths.get(
String.join("/", dataPath, targetPath));
try { try {
Files.move(nioSourcePath, Files.move(nioSourcePath,
nioTargetPath, nioTargetPath,
StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.COPY_ATTRIBUTES, // StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.REPLACE_EXISTING,
LinkOption.NOFOLLOW_LINKS); LinkOption.NOFOLLOW_LINKS);
} catch(IOException ex) { } catch(IOException ex) {

View File

@ -54,8 +54,8 @@ public class FileSystemThemeProvider implements ThemeProvider {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String BASE_PATH = "/themes"; private static final String BASE_PATH = "/themes";
private static final String DRAFT_THEMES_PATH = "/themes" + "/draft"; private static final String DRAFT_THEMES_PATH = BASE_PATH + "/draft";
private static final String LIVE_THEMES_PATH = "/themes" + "/live"; private static final String LIVE_THEMES_PATH = BASE_PATH + "/live";
private static final String THEME_JSON = "%s/theme.json"; private static final String THEME_JSON = "%s/theme.json";
private static final String THEME_XML = "%s/theme.xml"; private static final String THEME_XML = "%s/theme.xml";
@ -175,7 +175,7 @@ public class FileSystemThemeProvider implements ThemeProvider {
throw new UnexpectedErrorException(ex); throw new UnexpectedErrorException(ex);
} }
try(final OutputStreamWriter writer = new OutputStreamWriter( try (final OutputStreamWriter writer = new OutputStreamWriter(
outputStream, StandardCharsets.UTF_8)) { outputStream, StandardCharsets.UTF_8)) {
writer writer
.append(manifestUtil .append(manifestUtil
@ -215,9 +215,9 @@ public class FileSystemThemeProvider implements ThemeProvider {
themeName), themeName),
true); true);
} catch (FileAccessException } catch (FileAccessException
| FileDoesNotExistException | FileDoesNotExistException
| DirectoryNotEmptyException | DirectoryNotEmptyException
| InsufficientPermissionsException ex) { | InsufficientPermissionsException ex) {
throw new UnexpectedErrorException(ex); throw new UnexpectedErrorException(ex);
} }
} }
@ -320,20 +320,50 @@ public class FileSystemThemeProvider implements ThemeProvider {
ThemeVersion.DRAFT); ThemeVersion.DRAFT);
final String liveThemePath = createThemePath(theme, final String liveThemePath = createThemePath(theme,
ThemeVersion.LIVE); ThemeVersion.LIVE);
final String liveThemePathTmp = String.format("%_tmp", liveThemePath); final String liveThemePathTmp = String.format("%s_tmp", liveThemePath);
try { try {
if (!ccmFiles.existsFile(LIVE_THEMES_PATH)) {
ccmFiles.createDirectory(LIVE_THEMES_PATH);
}
} catch (FileAccessException
| InsufficientPermissionsException
| FileAlreadyExistsException ex) {
throw new UnexpectedErrorException(ex);
}
try {
ccmFiles.createDirectory(liveThemePathTmp);
ccmFiles.copyFile(draftThemePath, liveThemePathTmp, true); ccmFiles.copyFile(draftThemePath, liveThemePathTmp, true);
if (ccmFiles.existsFile(liveThemePath)) { if (ccmFiles.existsFile(liveThemePath)) {
ccmFiles.deleteFile(liveThemePath, true); ccmFiles.deleteFile(liveThemePath, true);
} }
ccmFiles.moveFile(liveThemePathTmp, liveThemePath); ccmFiles.moveFile(liveThemePathTmp, liveThemePath);
} catch (DirectoryNotEmptyException
| FileAccessException
| FileAlreadyExistsException
| FileDoesNotExistException
| InsufficientPermissionsException ex) {
throw new UnexpectedErrorException(ex);
}
}
@Override
public void unpublishTheme(final String theme) {
final String liveThemePath = createThemePath(theme,
ThemeVersion.LIVE);
try {
ccmFiles.deleteFile(liveThemePath, true);
} catch (DirectoryNotEmptyException } catch (DirectoryNotEmptyException
| FileAccessException | FileAccessException
| FileDoesNotExistException | FileDoesNotExistException
| InsufficientPermissionsException ex) { | InsufficientPermissionsException ex) {
throw new UnexpectedErrorException(); throw new UnexpectedErrorException(ex);
} }
} }
@ -357,15 +387,19 @@ public class FileSystemThemeProvider implements ThemeProvider {
final ThemeManifest manifest; final ThemeManifest manifest;
try { try {
final InputStream inputStream = ccmFiles
.createInputStream(String.format(THEME_JSON,
themePath));
if (ccmFiles.existsFile(String.format(THEME_JSON,
themePath))) {
final String jsonPath = String.format(
DRAFT_THEMES_PATH + "/" + THEME_JSON, themePath);
final String xmlPath = String.format(
DRAFT_THEMES_PATH + "/" + THEME_XML, themePath);
if (ccmFiles.existsFile(jsonPath)) {
final InputStream inputStream = ccmFiles
.createInputStream(jsonPath);
manifest = manifestUtil.loadManifest(inputStream, "theme.json"); manifest = manifestUtil.loadManifest(inputStream, "theme.json");
} else if (ccmFiles.existsFile(String.format(THEME_XML, } else if (ccmFiles.existsFile(xmlPath)) {
themePath))) { final InputStream inputStream = ccmFiles
.createInputStream(xmlPath);
manifest = manifestUtil.loadManifest(inputStream, "theme.xml"); manifest = manifestUtil.loadManifest(inputStream, "theme.xml");
} else { } else {
return Optional.empty(); return Optional.empty();

View File

@ -391,6 +391,11 @@ public class StaticThemeProvider implements ThemeProvider {
//No op in this implementation. //No op in this implementation.
} }
@Override
public void unpublishTheme(final String theme) {
//No op in this implementation.
}
private URI getJarUri() { private URI getJarUri() {
LOGGER.debug("Getting URI of JAR..."); LOGGER.debug("Getting URI of JAR...");

View File

@ -209,4 +209,13 @@ public interface ThemeProvider extends Serializable {
*/ */
void publishTheme(String theme); void publishTheme(String theme);
/**
* Unpublishes (deletes) the live version of a theme. For
* implementations which do not support draft/live themes the implementation
* of this method should be a noop, but not throw an exception.
*
* @param theme The theme to publish.
*/
void unpublishTheme(String theme);
} }

View File

@ -326,6 +326,14 @@ public class DatabaseThemeProvider implements ThemeProvider {
.ifPresent(themeManager::publishTheme); .ifPresent(themeManager::publishTheme);
} }
@Override
public void unpublishTheme(final String themeName) {
themeRepository
.findThemeByName(themeName, ThemeVersion.LIVE)
.ifPresent(themeManager::unpublishTheme);
}
private ThemeInfo createThemeInfo(final Theme theme) { private ThemeInfo createThemeInfo(final Theme theme) {
Objects.requireNonNull(theme); Objects.requireNonNull(theme);

View File

@ -20,9 +20,11 @@ package org.libreccm.theming.manager;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.libreccm.security.RequiresPrivilege;
import org.libreccm.theming.ThemeInfo; import org.libreccm.theming.ThemeInfo;
import org.libreccm.theming.ThemeProvider; import org.libreccm.theming.ThemeProvider;
import org.libreccm.theming.ThemeVersion; import org.libreccm.theming.ThemeVersion;
import org.libreccm.theming.ThemingPrivileges;
import java.io.Serializable; import java.io.Serializable;
import java.io.StringWriter; import java.io.StringWriter;
@ -40,6 +42,7 @@ import javax.json.JsonArrayBuilder;
import javax.json.JsonWriter; import javax.json.JsonWriter;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
@ -49,8 +52,6 @@ import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import static javassist.CtClass.*;
import static org.reflections.util.Utils.*;
/** /**
* *
@ -74,6 +75,8 @@ public class Themes implements Serializable {
@GET @GET
@Path("/providers") @Path("/providers")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
//@AuthorizationRequired
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
public String getThemeProviders() { public String getThemeProviders() {
final List<ThemeProvider> providersList = new ArrayList<>(); final List<ThemeProvider> providersList = new ArrayList<>();
@ -100,6 +103,8 @@ public class Themes implements Serializable {
@GET @GET
@Path("/themes") @Path("/themes")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
//@AuthorizationRequired
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
public List<ThemeInfo> getAvailableThemes() { public List<ThemeInfo> getAvailableThemes() {
final List<ThemeInfo> availableThemes = new ArrayList<>(); final List<ThemeInfo> availableThemes = new ArrayList<>();
@ -115,6 +120,8 @@ public class Themes implements Serializable {
@GET @GET
@Path("/themes/{theme}") @Path("/themes/{theme}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
//@AuthorizationRequired
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
public ThemeInfo getTheme(@PathParam("theme") final String themeName) { public ThemeInfo getTheme(@PathParam("theme") final String themeName) {
for (final ThemeProvider provider : providers) { for (final ThemeProvider provider : providers) {
@ -133,6 +140,8 @@ public class Themes implements Serializable {
@Path("/themes/{theme}") @Path("/themes/{theme}")
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
//@AuthorizationRequired
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
public ThemeInfo createTheme( public ThemeInfo createTheme(
@PathParam("theme") final String themeName, @PathParam("theme") final String themeName,
@QueryParam("provider") final String providerName) { @QueryParam("provider") final String providerName) {
@ -167,19 +176,13 @@ public class Themes implements Serializable {
@DELETE @DELETE
@Path("/themes/{theme}") @Path("/themes/{theme}")
//@AuthorizationRequired
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
public void deleteTheme(@PathParam("theme") final String themeName) { public void deleteTheme(@PathParam("theme") final String themeName) {
Objects.requireNonNull(themeName); Objects.requireNonNull(themeName);
final List<ThemeProvider> providersList = new ArrayList<>(); final Optional<ThemeProvider> provider = findProvider(themeName);
providers
.forEach(provider -> providersList.add(provider));
final Optional<ThemeProvider> provider = providersList
.stream()
.filter(current -> current.providesTheme(themeName,
ThemeVersion.DRAFT))
.findAny();
if (provider.isPresent()) { if (provider.isPresent()) {
@ -190,6 +193,42 @@ public class Themes implements Serializable {
} }
} }
@POST
@Path("/themes/{theme}/live")
//@AuthorizationRequired
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
public void publishTheme(@PathParam("theme") final String themeName) {
final Optional<ThemeProvider> provider = findProvider(themeName);
if (provider.isPresent()) {
provider.get().publishTheme(themeName);
} else {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
}
@DELETE
@Path("/themes/{theme}/live")
//@AuthorizationRequired
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
public void unPublishTheme(@PathParam("theme") final String themeName) {
final Optional<ThemeProvider> provider = findProvider(themeName);
if (provider.isPresent()) {
provider.get().unpublishTheme(themeName);
} else {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
}
private String getProviderName(final ThemeProvider provider) { private String getProviderName(final ThemeProvider provider) {
if (provider if (provider
@ -207,4 +246,17 @@ public class Themes implements Serializable {
} }
private Optional<ThemeProvider> findProvider(final String forTheme) {
final List<ThemeProvider> providersList = new ArrayList<>();
providers
.forEach(provider -> providersList.add(provider));
return providersList
.stream()
.filter(current -> current.providesTheme(forTheme,
ThemeVersion.DRAFT))
.findAny();
}
} }