parent
30cd9bae25
commit
de29bb6a3a
|
|
@ -0,0 +1,595 @@
|
|||
/*
|
||||
* 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.api.themes;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.libreccm.security.AuthorizationRequired;
|
||||
import org.libreccm.security.RequiresPrivilege;
|
||||
import org.libreccm.theming.ThemeFileInfo;
|
||||
import org.libreccm.theming.ThemeInfo;
|
||||
import org.libreccm.theming.ThemeProvider;
|
||||
import org.libreccm.theming.ThemeVersion;
|
||||
import org.libreccm.theming.ThemingPrivileges;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.enterprise.inject.Any;
|
||||
import javax.enterprise.inject.Instance;
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@RequestScoped
|
||||
@Path("/")
|
||||
public class Themes implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(Themes.class);
|
||||
|
||||
@Inject
|
||||
@Any
|
||||
private Instance<ThemeProvider> providers;
|
||||
|
||||
@GET
|
||||
@Path("/providers")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public List<String> getThemeProviders() {
|
||||
|
||||
return providers
|
||||
.stream()
|
||||
.filter(
|
||||
provider -> provider.supportsChanges()
|
||||
&& provider.supportsDraftThemes()
|
||||
)
|
||||
.map(this::getProviderName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public List<ThemeInfo> getAvailableThemes() {
|
||||
return providers
|
||||
.stream()
|
||||
.filter(provider -> provider.supportsChanges()
|
||||
&& provider.supportsDraftThemes()
|
||||
)
|
||||
.map(ThemeProvider::getThemes)
|
||||
.flatMap(themes -> themes.stream())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public ThemeInfo getTheme(@PathParam("theme") final String themeName) {
|
||||
return providers
|
||||
.stream()
|
||||
.filter(
|
||||
provider -> provider.providesTheme(
|
||||
themeName, ThemeVersion.DRAFT
|
||||
)
|
||||
)
|
||||
.findAny()
|
||||
.map(
|
||||
provider -> provider.getThemeInfo(
|
||||
themeName, ThemeVersion.DRAFT
|
||||
)
|
||||
)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found.", themeName
|
||||
)
|
||||
)
|
||||
)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found.", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/themes/{theme}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@SuppressWarnings("unchecked")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public ThemeInfo createTheme(
|
||||
@PathParam("theme") final String themeName,
|
||||
@QueryParam("provider") final String providerName
|
||||
) {
|
||||
Objects.requireNonNull(themeName);
|
||||
Objects.requireNonNull(providerName);
|
||||
|
||||
if (themeName.isEmpty() || themeName.matches("\\s*")) {
|
||||
throw new BadRequestException("No name for new theme provided.");
|
||||
}
|
||||
|
||||
if (providerName.isEmpty() || providerName.matches("\\s*")) {
|
||||
throw new BadRequestException(
|
||||
"No provider for new theme provided."
|
||||
);
|
||||
}
|
||||
|
||||
final Class<ThemeProvider> providerClass;
|
||||
try {
|
||||
providerClass = (Class<ThemeProvider>) Class.forName(providerName);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new WebApplicationException(
|
||||
String.format("No provider with name \"%s\" available.",
|
||||
providerName),
|
||||
Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
final ThemeProvider provider = providers.select(providerClass).get();
|
||||
|
||||
return provider.createTheme(themeName);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/themes/{theme}")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public Response deleteTheme(@PathParam("theme") final String themeName) {
|
||||
Objects.requireNonNull(themeName);
|
||||
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
provider.deleteTheme(themeName);
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/themes/{theme}/live")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public Response publishTheme(@PathParam("theme") final String themeName) {
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
provider.publishTheme(themeName);
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/themes/{theme}/live")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public Response unPublishTheme(@PathParam("theme") final String themeName) {
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
provider.unpublishTheme(themeName);
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}/files/")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public Response getThemeRootDir(@PathParam("theme") final String themeName) {
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
final ThemeFileInfo fileInfo = provider
|
||||
.getThemeFileInfo(themeName, ThemeVersion.DRAFT, "/")
|
||||
.orElseThrow(
|
||||
() -> new WebApplicationException(
|
||||
String.format(
|
||||
"File \"/\" in theme %s is not a directory.",
|
||||
themeName),
|
||||
Response.Status.INTERNAL_SERVER_ERROR
|
||||
)
|
||||
);
|
||||
|
||||
if (fileInfo.isDirectory()) {
|
||||
return Response
|
||||
.ok(
|
||||
provider.listThemeFiles(
|
||||
themeName, ThemeVersion.DRAFT, "/")
|
||||
)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.build();
|
||||
} else {
|
||||
throw new WebApplicationException(
|
||||
String.format(
|
||||
"File \"/\" in theme %s is not a directory.",
|
||||
themeName),
|
||||
Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}/files/{path:.+}")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public Response getThemeFile(
|
||||
@PathParam("theme") final String themeName,
|
||||
@PathParam("path") final String path
|
||||
) {
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme \"%s\" does not exist.", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
final ThemeFileInfo fileInfo = provider
|
||||
.getThemeFileInfo(themeName, ThemeVersion.DRAFT, path)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"File \"%s\" does not exist in theme %s.",
|
||||
path,
|
||||
themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (fileInfo.isDirectory()) {
|
||||
return Response
|
||||
.ok(
|
||||
provider.listThemeFiles(themeName, ThemeVersion.DRAFT, path)
|
||||
)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.build();
|
||||
} else {
|
||||
final InputStream inputStream = provider
|
||||
.getThemeFileAsStream(themeName, ThemeVersion.DRAFT, path)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"File \"%s\" does not exist in theme %s.",
|
||||
path,
|
||||
themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return Response
|
||||
.ok(inputStream)
|
||||
.type(fileInfo.getMimeType())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/themes/{theme}/files/{path:.+}")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public Response createOrUpdateThemeFile(
|
||||
@PathParam("theme") final String themeName,
|
||||
@PathParam("path") final String path,
|
||||
final byte[] data
|
||||
) {
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
final Optional<ThemeFileInfo> fileInfo = provider
|
||||
.getThemeFileInfo(themeName, ThemeVersion.DRAFT, "/");
|
||||
|
||||
if (fileInfo.isPresent() && fileInfo.get().isDirectory()) {
|
||||
throw new BadRequestException(
|
||||
String.format(
|
||||
"File %s already exists in theme %s and is a directory.",
|
||||
path,
|
||||
themeName
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
final OutputStream outputStream = provider.getOutputStreamForThemeFile(
|
||||
themeName, path
|
||||
);
|
||||
|
||||
try {
|
||||
outputStream.write(data);
|
||||
} catch (IOException ex) {
|
||||
throw new WebApplicationException(
|
||||
String.format(
|
||||
"Failed to create/update file %s in theme %s.",
|
||||
path,
|
||||
themeName
|
||||
),
|
||||
Response.Status.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/themes/{theme}/files/{path:.+}")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public Response deleteThemeFile(
|
||||
@PathParam("theme") final String themeName,
|
||||
@PathParam("path") final String path,
|
||||
@QueryParam("recursive") final boolean recursive
|
||||
) {
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme \"%s\" does not exist.", themeName
|
||||
)
|
||||
));
|
||||
|
||||
final ThemeFileInfo fileInfo = provider
|
||||
.getThemeFileInfo(themeName, ThemeVersion.DRAFT, path)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"File \"%s\" does not exist in theme %s.",
|
||||
path,
|
||||
themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (fileInfo.isDirectory()) {
|
||||
final List<ThemeFileInfo> files = provider
|
||||
.listThemeFiles(themeName, ThemeVersion.DRAFT, path);
|
||||
if (files.isEmpty()) {
|
||||
provider.deleteThemeFile(themeName, path);
|
||||
return Response.ok().build();
|
||||
} else if (recursive) {
|
||||
for (final ThemeFileInfo file : files) {
|
||||
provider.deleteThemeFile(
|
||||
themeName, String.format("%s/%s", path, file.getName())
|
||||
);
|
||||
}
|
||||
provider.deleteThemeFile(themeName, path);
|
||||
return Response.ok().build();
|
||||
} else {
|
||||
throw new BadRequestException(
|
||||
String.format(
|
||||
"Directory %s of theme %s is not empty.",
|
||||
path, themeName
|
||||
)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
provider.deleteThemeFile(themeName, path);
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}/@download")
|
||||
@Produces("application/zip")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public Response downloadTheme(final String themeName) {
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ZipOutputStream zos = new ZipOutputStream(baos)) {
|
||||
addFilesInDirectoryToZip(provider, themeName, "/", zos);
|
||||
|
||||
final byte[] result = baos.toByteArray();
|
||||
|
||||
return Response
|
||||
.ok(result)
|
||||
.build();
|
||||
} catch (IOException ex) {
|
||||
throw new WebApplicationException(
|
||||
String.format(
|
||||
"Error zipping theme %s.", themeName
|
||||
),
|
||||
ex,
|
||||
Response.Status.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/themes/{theme}/@update")
|
||||
@Consumes("application/zip")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public Response updateTheme(
|
||||
final String themeName, final byte[] updatedTheme
|
||||
) {
|
||||
final ThemeProvider provider = findProvider(themeName)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"Theme %s not found", themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// Read every file from ZIP
|
||||
// Try to find file in theme
|
||||
// Create or update file in theme
|
||||
// remove files not in ZIP from theme
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private String getProviderName(final ThemeProvider provider) {
|
||||
if (provider
|
||||
.getClass()
|
||||
.getCanonicalName()
|
||||
.toLowerCase()
|
||||
.contains("$proxy")) {
|
||||
|
||||
final String name = provider.getClass().getCanonicalName();
|
||||
return name.substring(0, name.toLowerCase().indexOf("$proxy"));
|
||||
|
||||
} else {
|
||||
return provider.getClass().getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
private void addFilesInDirectoryToZip(
|
||||
final ThemeProvider provider,
|
||||
final String themeName,
|
||||
final String directoryPath,
|
||||
final ZipOutputStream zipOutputStream
|
||||
) throws IOException {
|
||||
final List<ThemeFileInfo> files = provider
|
||||
.listThemeFiles(themeName, ThemeVersion.DRAFT, directoryPath
|
||||
);
|
||||
for (ThemeFileInfo file : files) {
|
||||
if (file.isDirectory()) {
|
||||
final String dirPath = String.format(
|
||||
"%s/%s", directoryPath, file.getName()
|
||||
);
|
||||
final ZipEntry entry = new ZipEntry(dirPath);
|
||||
zipOutputStream.putNextEntry(entry);
|
||||
addFilesInDirectoryToZip(
|
||||
provider, themeName, dirPath, zipOutputStream
|
||||
);
|
||||
} else {
|
||||
final String filePath = String.format(
|
||||
"%s/%s", directoryPath, file.getName()
|
||||
);
|
||||
final ZipEntry entry = new ZipEntry(filePath);
|
||||
zipOutputStream.putNextEntry(entry);
|
||||
|
||||
try (InputStream inputStream = getFileInputStream(
|
||||
provider, themeName, filePath
|
||||
)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int length = inputStream.read(buffer);
|
||||
while (length != -1) {
|
||||
zipOutputStream.write(buffer);
|
||||
length = inputStream.read(buffer);
|
||||
}
|
||||
}
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream getFileInputStream(
|
||||
final ThemeProvider provider, final String themeName,
|
||||
final String filePath
|
||||
) {
|
||||
return provider
|
||||
.getThemeFileAsStream(themeName, ThemeVersion.DRAFT, filePath)
|
||||
.orElseThrow(
|
||||
() -> new NotFoundException(
|
||||
String.format(
|
||||
"File %s not found in theme %s.",
|
||||
filePath,
|
||||
themeName
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,7 +16,11 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
package org.libreccm.theming.manager;
|
||||
package org.libreccm.api.themes;
|
||||
|
||||
import org.libreccm.api.CorsFilter;
|
||||
import org.libreccm.api.DefaultResponseHeaders;
|
||||
import org.libreccm.api.PreflightRequestFilter;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
|
@ -26,19 +30,20 @@ import javax.ws.rs.core.Application;
|
|||
|
||||
/**
|
||||
* JAX-RS application for managing themes.
|
||||
*
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@ApplicationPath("/thememanager")
|
||||
public class ThemeManager extends Application {
|
||||
|
||||
@ApplicationPath("/api/themes")
|
||||
public class ThemesApi extends Application {
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> getClasses() {
|
||||
|
||||
final Set<Class<?>> classes = new HashSet<>();
|
||||
classes.add(CorsFilter.class);
|
||||
classes.add(DefaultResponseHeaders.class);
|
||||
classes.add(PreflightRequestFilter.class);
|
||||
classes.add(Themes.class);
|
||||
return classes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,392 +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.manager;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.libreccm.security.AuthorizationRequired;
|
||||
import org.libreccm.security.RequiresPrivilege;
|
||||
import org.libreccm.theming.ThemeFileInfo;
|
||||
import org.libreccm.theming.ThemeInfo;
|
||||
import org.libreccm.theming.ThemeProvider;
|
||||
import org.libreccm.theming.ThemeVersion;
|
||||
import org.libreccm.theming.ThemingPrivileges;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.enterprise.context.RequestScoped;
|
||||
import javax.enterprise.inject.Any;
|
||||
import javax.enterprise.inject.Instance;
|
||||
import javax.inject.Inject;
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonArrayBuilder;
|
||||
import javax.json.JsonWriter;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
@RequestScoped
|
||||
@Path("/")
|
||||
public class Themes implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(Themes.class);
|
||||
|
||||
@Inject
|
||||
@Any
|
||||
private Instance<ThemeProvider> providers;
|
||||
|
||||
@Inject
|
||||
private Themes themes;
|
||||
|
||||
@GET
|
||||
@Path("/providers")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public String getThemeProviders() {
|
||||
|
||||
final List<ThemeProvider> providersList = new ArrayList<>();
|
||||
providers
|
||||
.forEach(provider -> providersList.add(provider));
|
||||
|
||||
final JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder();
|
||||
|
||||
providersList
|
||||
.stream()
|
||||
.filter(provider -> provider.supportsChanges()
|
||||
&& provider.supportsDraftThemes())
|
||||
.map(this::getProviderName)
|
||||
.forEach(jsonArrayBuilder::add);
|
||||
|
||||
final StringWriter writer = new StringWriter();
|
||||
final JsonWriter jsonWriter = Json.createWriter(writer);
|
||||
|
||||
jsonWriter.writeArray(jsonArrayBuilder.build());
|
||||
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public List<ThemeInfo> getAvailableThemes() {
|
||||
|
||||
final List<ThemeInfo> availableThemes = new ArrayList<>();
|
||||
for (final ThemeProvider provider : providers) {
|
||||
if (provider.supportsChanges() && provider.supportsDraftThemes()) {
|
||||
availableThemes.addAll(provider.getThemes());
|
||||
}
|
||||
}
|
||||
|
||||
return availableThemes;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.EDIT_THEME)
|
||||
public ThemeInfo getTheme(@PathParam("theme") final String themeName) {
|
||||
|
||||
for (final ThemeProvider provider : providers) {
|
||||
if (provider.providesTheme(themeName, ThemeVersion.DRAFT)) {
|
||||
return provider
|
||||
.getThemeInfo(themeName, ThemeVersion.DRAFT)
|
||||
.orElseThrow(() -> new WebApplicationException(
|
||||
Response.Status.NOT_FOUND));
|
||||
}
|
||||
}
|
||||
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/themes/{theme}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@SuppressWarnings("unchecked")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public ThemeInfo createTheme(
|
||||
@PathParam("theme") final String themeName,
|
||||
@QueryParam("provider") final String providerName) {
|
||||
|
||||
Objects.requireNonNull(themeName);
|
||||
Objects.requireNonNull(providerName);
|
||||
|
||||
if (themeName.isEmpty() || themeName.matches("\\s*")) {
|
||||
throw new WebApplicationException("No name for new theme provided.",
|
||||
Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (providerName.isEmpty() || providerName.matches("\\s*")) {
|
||||
throw new WebApplicationException(
|
||||
"No provider for new theme provided.",
|
||||
Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
final Class<ThemeProvider> providerClass;
|
||||
try {
|
||||
providerClass = (Class<ThemeProvider>) Class.forName(providerName);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new WebApplicationException(
|
||||
String.format("No provider with name \"%s\" available.",
|
||||
providerName),
|
||||
Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
final ThemeProvider provider = providers.select(providerClass).get();
|
||||
|
||||
return provider.createTheme(themeName);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/themes/{theme}")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public void deleteTheme(@PathParam("theme") final String themeName) {
|
||||
|
||||
Objects.requireNonNull(themeName);
|
||||
|
||||
final Optional<ThemeProvider> provider = findProvider(themeName);
|
||||
|
||||
if (provider.isPresent()) {
|
||||
|
||||
provider.get().deleteTheme(themeName);
|
||||
|
||||
} else {
|
||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}/files/")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public Response getThemeRootDir(@PathParam("theme") final String themeName) {
|
||||
|
||||
final Optional<ThemeProvider> provider = findProvider(themeName);
|
||||
|
||||
if (provider.isPresent()) {
|
||||
|
||||
final Optional<ThemeFileInfo> fileInfo = provider
|
||||
.get()
|
||||
.getThemeFileInfo(themeName, ThemeVersion.DRAFT, "/");
|
||||
|
||||
if (fileInfo.isPresent()) {
|
||||
|
||||
if (fileInfo.get().isDirectory()) {
|
||||
|
||||
final List<ThemeFileInfo> fileInfos = provider
|
||||
.get()
|
||||
.listThemeFiles(themeName, ThemeVersion.DRAFT, "/");
|
||||
|
||||
return Response
|
||||
.ok(fileInfos)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.build();
|
||||
|
||||
} else {
|
||||
|
||||
throw new WebApplicationException(
|
||||
String.format(
|
||||
"File \"/\" in theme %s is not a directory.",
|
||||
themeName),
|
||||
Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format(
|
||||
"File \"/\" does not exist in theme %s.",
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format("Theme \"%s\" does not exist.",
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/themes/{theme}/files/{path:.+}")
|
||||
@AuthorizationRequired
|
||||
@RequiresPrivilege(ThemingPrivileges.ADMINISTER_THEMES)
|
||||
public Response getThemeFile(@PathParam("theme") final String themeName,
|
||||
@PathParam("path") final String path) {
|
||||
|
||||
final Optional<ThemeProvider> provider = findProvider(themeName);
|
||||
|
||||
if (provider.isPresent()) {
|
||||
|
||||
final Optional<ThemeFileInfo> fileInfo = provider
|
||||
.get()
|
||||
.getThemeFileInfo(themeName, ThemeVersion.DRAFT, path);
|
||||
|
||||
if (fileInfo.isPresent()) {
|
||||
|
||||
final ThemeFileInfo themeFileInfo = fileInfo.get();
|
||||
|
||||
if (themeFileInfo.isDirectory()) {
|
||||
|
||||
final List<ThemeFileInfo> fileInfos = provider
|
||||
.get()
|
||||
.listThemeFiles(themeName, ThemeVersion.DRAFT, path);
|
||||
|
||||
return Response
|
||||
.ok(fileInfos)
|
||||
.type(MediaType.APPLICATION_JSON)
|
||||
.build();
|
||||
|
||||
} else {
|
||||
final Optional<InputStream> inputStream = provider
|
||||
.get()
|
||||
.getThemeFileAsStream(themeName,
|
||||
ThemeVersion.DRAFT,
|
||||
path);
|
||||
|
||||
if (inputStream.isPresent()) {
|
||||
|
||||
final InputStream inStream = inputStream.get();
|
||||
return Response
|
||||
.ok(inStream)
|
||||
.type(themeFileInfo.getMimeType())
|
||||
.build();
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format(
|
||||
"File \"%s\" does not exist in theme %s.",
|
||||
path,
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format(
|
||||
"File \"%s\" does not exist in theme %s.",
|
||||
path,
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
|
||||
} else {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(String.format("Theme \"%s\" does not exist.",
|
||||
themeName))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private String getProviderName(final ThemeProvider provider) {
|
||||
|
||||
if (provider
|
||||
.getClass()
|
||||
.getCanonicalName()
|
||||
.toLowerCase()
|
||||
.contains("$proxy")) {
|
||||
|
||||
final String name = provider.getClass().getCanonicalName();
|
||||
return name.substring(0, name.toLowerCase().indexOf("$proxy"));
|
||||
|
||||
} else {
|
||||
return provider.getClass().getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue