From 227b6268216664dfe4bfaefb9c642d820db46f3e Mon Sep 17 00:00:00 2001 From: jensp Date: Wed, 16 May 2018 19:23:54 +0000 Subject: [PATCH] CCM NG: Part of implementing access to themes via WebDAV git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5434 8810af33-2d31-482b-a856-94f89814c4df Former-commit-id: e09fa0df21f5eebc78fc63d5c4b4077094f983ff --- .../libreccm/theming/StaticThemeProvider.java | 74 ++++++- .../org/libreccm/theming/ThemeFilesDav.java | 106 --------- .../java/org/libreccm/theming/Themes.java | 31 +++ .../org/libreccm/theming/ThemesService.java | 1 - .../libreccm/theming/webdav/ThemeFiles.java | 203 ++++++++++++++++++ .../org/libreccm/theming/webdav/WebDAV.java | 43 ++++ .../org/libreccm/webdav/WebDAVAuthFilter.java | 172 +++++++++++++++ .../webdav/xml/elements/MultiStatus.java | 10 +- .../{Response.java => WebDavResponse.java} | 16 +- .../resources/themes/ccm/theme-index.json | 62 ++++++ ccm-pagemodelseditor/package.json | 2 +- ccm-pagemodelseditor/pom.xml | 24 +++ ccm-pagemodelseditor/tsconfig.json | 1 + ccm-pagemodelseditor/webpack.config.js | 3 +- pom.xml | 4 +- 15 files changed, 625 insertions(+), 127 deletions(-) delete mode 100644 ccm-core/src/main/java/org/libreccm/theming/ThemeFilesDav.java create mode 100644 ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFiles.java create mode 100644 ccm-core/src/main/java/org/libreccm/theming/webdav/WebDAV.java create mode 100644 ccm-core/src/main/java/org/libreccm/webdav/WebDAVAuthFilter.java rename ccm-core/src/main/java/org/libreccm/webdav/xml/elements/{Response.java => WebDavResponse.java} (94%) create mode 100644 ccm-core/src/main/resources/themes/ccm/theme-index.json diff --git a/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java b/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java index 468363d3d..42248ed45 100644 --- a/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java +++ b/ccm-core/src/main/java/org/libreccm/theming/StaticThemeProvider.java @@ -28,21 +28,32 @@ import org.reflections.scanners.ResourcesScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.URI; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; +import java.util.Enumeration; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.logging.Level; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonReader; /** * @@ -67,6 +78,9 @@ public class StaticThemeProvider implements ThemeProvider { @Inject private ThemeManifestUtil manifestUtil; + @Inject + private ThemeFileInfoUtil themeFileInfoUtil; + @Override public List getThemes() { @@ -248,7 +262,30 @@ public class StaticThemeProvider implements ThemeProvider { final ThemeVersion version, final String path) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + Objects.requireNonNull(theme); + Objects.requireNonNull(version); + Objects.requireNonNull(path); + + if (theme.isEmpty() || theme.matches("\\s*")) { + throw new IllegalArgumentException( + "The name of the theme can't be empty."); + } + + final String[] pathTokens = path.split("/"); + + final String indexFilePath = String.format("/" + + THEMES_PACKAGE + + "/%s/theme-index.json", + theme); + final InputStream stream = getClass() + .getResourceAsStream(indexFilePath); + final JsonReader reader = Json.createReader(stream); + final JsonObject indexObj = reader.readObject(); + final JsonArray filesArray = indexObj.getJsonArray("files"); + filesArray + .forEach(value -> LOGGER.warn(value.toString())); + + throw new UnsupportedOperationException(); } @Override @@ -289,12 +326,12 @@ public class StaticThemeProvider implements ThemeProvider { } @Override - public OutputStream getOutputStreamForThemeFile(final String theme, + public OutputStream getOutputStreamForThemeFile(final String theme, final String path) { - + throw new UnsupportedOperationException(String .format("This implementation of %s interface does not support " - + "changes to the theme files.", + + "changes to the theme files.", ThemeProvider.class.getName())); } @@ -313,4 +350,33 @@ public class StaticThemeProvider implements ThemeProvider { //No op in this implementation. } + private URI getJarUri() { + + LOGGER.debug("Getting URI of JAR..."); + + final String themesUrl = getClass().getResource(THEMES_DIR).toString(); + LOGGER.debug("Full URL of " + THEMES_DIR + " directory: {}", themesUrl); + + final int index = themesUrl.indexOf('!'); + final String pathToJar = themesUrl.substring(0, index); + + final URI uri = URI.create(pathToJar); + LOGGER.debug("URI to JAR is \"%s\".", uri.toString()); + return uri; + } + + private boolean isTheme(final Path path) { + + Objects.requireNonNull(path); + + if (!Files.isDirectory(path)) { + return false; + } + + final Path manifestPathJson = path.resolve(THEME_MANIFEST_JSON); + final Path manifestPathXml = path.resolve(THEME_MANIFEST_XML); + + return Files.exists(manifestPathJson) || Files.exists(manifestPathXml); + } + } diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemeFilesDav.java b/ccm-core/src/main/java/org/libreccm/theming/ThemeFilesDav.java deleted file mode 100644 index 8a3f5f07d..000000000 --- a/ccm-core/src/main/java/org/libreccm/theming/ThemeFilesDav.java +++ /dev/null @@ -1,106 +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; - -import org.libreccm.webdav.methods.PROPFIND; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -import javax.enterprise.context.RequestScoped; -import javax.inject.Inject; -import javax.ws.rs.GET; -import javax.ws.rs.NotFoundException; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -/** - * - * @author Jens Pelzetter - */ -@RequestScoped -@Path("/dav/{theme}") -public class ThemeFilesDav { - - @Inject - private Themes themes; - - @GET - @Path("/{path}") - public Response getFile(@PathParam("theme") final String theme, - @PathParam("path") final String path) { - - final ThemeInfo info = themes - .getTheme(theme, ThemeVersion.LIVE) - .orElseThrow(() -> new NotFoundException(String - .format("Theme \"%s\" does not exist.", theme))); - - final InputStream inputStream = themes - .getFileFromTheme(info, path) - .orElseThrow(() -> new NotFoundException(String - .format("The file \"%s\" does exist in the theme \"%s\".", - path, - theme))); - - final MediaType mediaType = getMediaTypeFromPath(path); - - final BufferedReader reader = new BufferedReader( - new InputStreamReader(inputStream)); - final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try { - int value = reader.read(); - while (value != -1) { - outputStream.write(value); - value = reader.read(); - } - } catch (IOException ex) { - throw new WebApplicationException(ex); - } - - final byte[] data = outputStream.toByteArray(); - return Response.ok(data, mediaType).build(); - - } - - @PROPFIND - @Path("/{path}") - public Response propfind(@PathParam("theme") final String theme, - @PathParam("path") final String path) { - - throw new UnsupportedOperationException(); - - } - - private MediaType getMediaTypeFromPath(final String path) { - - if (path.endsWith(".css")) { - return new MediaType("text", "css"); - } else { - return MediaType.WILDCARD_TYPE; - } - - } - -} diff --git a/ccm-core/src/main/java/org/libreccm/theming/Themes.java b/ccm-core/src/main/java/org/libreccm/theming/Themes.java index 5c92be058..11364262f 100644 --- a/ccm-core/src/main/java/org/libreccm/theming/Themes.java +++ b/ccm-core/src/main/java/org/libreccm/theming/Themes.java @@ -194,5 +194,36 @@ public class Themes implements Serializable { } } } + + /** + * List all files in a theme at the specified path + * + * @param theme The theme from which the file is retrieved. + * @param path The path of the file relative to the root directory of the + * theme. + * @return A list of all files in the provided directory. If there is no + * such path in the theme the list is empty. If the path is the path + * of a file and not a directory the list should have one element, + * the data about the file itself. + */ + public List listThemesFiles(final ThemeInfo theme, + final String path) { + + final Instance forTheme = providers.select( + theme.getProvider()); + + if (forTheme.isUnsatisfied()) { + LOGGER.error("ThemeProvider \"{}\" not found.", + theme.getProvider().getName()); + throw new UnexpectedErrorException(String.format( + "ThemeProvider \"%s\" not found.", + theme.getProvider().getName())); + } + + final ThemeProvider provider = forTheme.get(); + return provider.listThemeFiles(theme.getName(), + theme.getVersion(), + path); + } } diff --git a/ccm-core/src/main/java/org/libreccm/theming/ThemesService.java b/ccm-core/src/main/java/org/libreccm/theming/ThemesService.java index 22587f0d3..20a178880 100644 --- a/ccm-core/src/main/java/org/libreccm/theming/ThemesService.java +++ b/ccm-core/src/main/java/org/libreccm/theming/ThemesService.java @@ -36,7 +36,6 @@ public class ThemesService extends Application { final Set> classes = new HashSet<>(); classes.add(ThemeFiles.class); - classes.add(ThemeFilesDav.class); return classes; } diff --git a/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFiles.java b/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFiles.java new file mode 100644 index 000000000..c9a2e0866 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFiles.java @@ -0,0 +1,203 @@ +/* + * 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.webdav; + +import org.libreccm.theming.ThemeFileInfo; +import org.libreccm.webdav.methods.PROPFIND; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.libreccm.theming.ThemeInfo; +import org.libreccm.theming.ThemeVersion; +import org.libreccm.theming.Themes; +import org.libreccm.webdav.ResponseStatus; +import org.libreccm.webdav.xml.elements.HRef; +import org.libreccm.webdav.xml.elements.MultiStatus; +import org.libreccm.webdav.xml.elements.Prop; +import org.libreccm.webdav.xml.elements.PropStat; +import org.libreccm.webdav.xml.elements.Status; +import org.libreccm.webdav.xml.elements.WebDavResponse; +import org.libreccm.webdav.xml.properties.DisplayName; +import org.libreccm.webdav.xml.properties.GetContentLength; +import org.libreccm.webdav.xml.properties.GetContentType; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import javax.ws.rs.OPTIONS; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.UriInfo; +import javax.ws.rs.ext.Providers; + +/** + * + * @author Jens Pelzetter + */ +@RequestScoped +@Path("/{theme}") +public class ThemeFiles { + + @Inject + private Themes themes; + + @GET + @Path("/{path}") + public Response getFile(@PathParam("theme") final String theme, + @PathParam("path") final String path) { + + final ThemeInfo info = themes + .getTheme(theme, ThemeVersion.LIVE) + .orElseThrow(() -> new NotFoundException(String + .format("Theme \"%s\" does not exist.", theme))); + + final InputStream inputStream = themes + .getFileFromTheme(info, path) + .orElseThrow(() -> new NotFoundException(String + .format("The file \"%s\" does exist in the theme \"%s\".", + path, + theme))); + + final MediaType mediaType = getMediaTypeFromPath(path); + + final BufferedReader reader = new BufferedReader( + new InputStreamReader(inputStream)); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + int value = reader.read(); + while (value != -1) { + outputStream.write(value); + value = reader.read(); + } + } catch (IOException ex) { + throw new WebApplicationException(ex); + } + + final byte[] data = outputStream.toByteArray(); + return Response.ok(data, mediaType).build(); + + } + + @OPTIONS + public Response options() { + + return Response + .noContent() + .header("DAV", "1, 2") + .header("Allow", + "GET,DELETE,MOVE,COPY,PROPFIND,OPTIONS,HEAD,PUT,PROPPATCH," + + "LOCK,UNLOCK") + .build(); + } + + @PROPFIND + @Path("/{path}") + public Response propfind(@PathParam("theme") final String theme, + @PathParam("path") final String path, + @Context final UriInfo uriInfo, + @Context final Providers providers) { + + final ThemeInfo themeInfo = themes + .getTheme(theme, ThemeVersion.DRAFT) + .orElseThrow(() -> new NotFoundException(String.format( + "No theme with name \"%s\" exists.", + theme))); + + final List fileInfos = themes.listThemesFiles(themeInfo, + path); + final MultiStatus result; + if (fileInfos.size() == 1) { + + final ThemeFileInfo fileInfo = fileInfos.get(1); + + result = new MultiStatus(buildWebDavResponse(fileInfo, uriInfo)); + + } else { + final WebDavResponse folder = new WebDavResponse( + new HRef(uriInfo.getRequestUri()), + null, + null, + null, + new PropStat(new Prop(new DisplayName(path), + org.libreccm.webdav.xml.elements.Collection.COLLECTION), + new Status(javax.ws.rs.core.Response.Status.OK))); + + final List responses = new LinkedList<>(); + responses.add(folder); + + final List fileResponses = fileInfos + .stream() + .map(fileInfo -> buildWebDavResponse(fileInfo, uriInfo)) + .collect(Collectors.toList()); + responses.addAll(fileResponses); + + result = new MultiStatus(responses.toArray(new WebDavResponse[]{})); + } + + return Response + .status(ResponseStatus.MULTI_STATUS) + .entity(result) + .build(); + } + + private WebDavResponse buildWebDavResponse(final ThemeFileInfo fileInfo, + final UriInfo uriInfo) { + + final PropStat propStat = new PropStat( + new Prop(new DisplayName(fileInfo.getName()), + new GetContentLength(fileInfo.getSize()), + new GetContentType(fileInfo.getMimeType())), + new Status(javax.ws.rs.core.Response.Status.OK)); + + final WebDavResponse response = new WebDavResponse( + new HRef(uriInfo.getRequestUri()), + null, + null, + null, + propStat); + + return response; + } + + private MediaType getMediaTypeFromPath(final String path) { + + if (path.endsWith(".css")) { + return new MediaType("text", "css"); + } else { + return MediaType.WILDCARD_TYPE; + } + + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/theming/webdav/WebDAV.java b/ccm-core/src/main/java/org/libreccm/theming/webdav/WebDAV.java new file mode 100644 index 000000000..f79b2a846 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/webdav/WebDAV.java @@ -0,0 +1,43 @@ +/* + * 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.webdav; + +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.ApplicationPath; +import javax.ws.rs.core.Application; + +/** + * + * @author Jens Pelzetter + */ +@ApplicationPath("/DAV/themes") +public class WebDAV extends Application { + + @Override + public Set> getClasses() { + + final Set> classes = new HashSet<>(); + classes.add(ThemeFiles.class); + + return classes; + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/webdav/WebDAVAuthFilter.java b/ccm-core/src/main/java/org/libreccm/webdav/WebDAVAuthFilter.java new file mode 100644 index 000000000..0780849f2 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/webdav/WebDAVAuthFilter.java @@ -0,0 +1,172 @@ +/* + * 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.webdav; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.subject.Subject; + +import java.io.IOException; +import java.util.Base64; +import java.util.Optional; +import java.util.StringTokenizer; + +import javax.inject.Inject; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author Jens Pelzetter + */ +@WebFilter(filterName = "WebDAVAuthFilter", urlPatterns = {"/DAV/*"}) +public class WebDAVAuthFilter implements Filter { + + private static final Logger LOGGER = LogManager + .getLogger(WebDAVAuthFilter.class); + + @Inject + private Subject subject; + + @Override + public void init(final FilterConfig filterConfig) throws ServletException { + // Nothing + } + + @Override + public void doFilter(final ServletRequest request, + final ServletResponse response, + final FilterChain chain) throws IOException, + ServletException { + + LOGGER.warn("Filtering"); + + if (subject.isAuthenticated()) { + chain.doFilter(request, response); + } else { + + final HttpServletRequest httpRequest = (HttpServletRequest) request; + final HttpServletResponse httpResponse + = (HttpServletResponse) response; + + final String authHeader = httpRequest.getHeader("Authorization"); + if (authHeader == null) { + + sendUnauthorizedResponse(httpResponse, "Authorization required"); + + } else { + + final StringTokenizer tokenizer + = new StringTokenizer(authHeader); + if (tokenizer.hasMoreTokens()) { + + final String authMethod = tokenizer.nextToken(); + if ("basic".equalsIgnoreCase(authMethod)) { + + authenticate(httpRequest, + httpResponse, + chain, + tokenizer.nextToken()); + } else { + sendUnauthorizedResponse(httpResponse, + "Unsupported authentication method"); + } + + } else { + sendUnauthorizedResponse(httpResponse, + "Failed to read authentication header"); + } + } + } + } + + private void authenticate(final HttpServletRequest request, + final HttpServletResponse response, + final FilterChain chain, + final String credentials) + throws IOException, ServletException { + + final Optional usernamePasswordToken + = readCredentials( + credentials); + if (usernamePasswordToken.isPresent()) { + try { + subject.login(usernamePasswordToken.get()); + chain.doFilter(request, response); + } catch (AuthenticationException ex) { + LOGGER.warn("Authentication failed for " + + "subject \"{}\"", + usernamePasswordToken + .get() + .getUsername()); + sendUnauthorizedResponse(response, + "Authentication failed"); + } + } else { + sendUnauthorizedResponse(response, + "Invalid authentication token"); + } + } + + private Optional readCredentials( + final String credentialsBase64) { + + final String credentials = new String( + Base64 + .getDecoder() + .decode(credentialsBase64)); + final int pos = credentials.indexOf(":"); + if (pos == -1) { + return Optional.empty(); + } else { + final String username = credentials + .substring(0, pos) + .trim(); + final String password = credentials + .substring(pos + 1); + + return Optional.of(new UsernamePasswordToken(username, password)); + } + } + + private void sendUnauthorizedResponse(final HttpServletResponse response, + final String message) + throws IOException { + + response.setHeader("WWW-Authenticate", + "Basic realm=\"LibreCCM_WebDAV\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, + message); + } + + @Override + public void destroy() { + // Nothing + } + +} diff --git a/ccm-core/src/main/java/org/libreccm/webdav/xml/elements/MultiStatus.java b/ccm-core/src/main/java/org/libreccm/webdav/xml/elements/MultiStatus.java index eb0307edb..dd8c91caa 100644 --- a/ccm-core/src/main/java/org/libreccm/webdav/xml/elements/MultiStatus.java +++ b/ccm-core/src/main/java/org/libreccm/webdav/xml/elements/MultiStatus.java @@ -50,7 +50,7 @@ import javax.xml.bind.annotation.XmlType; public final class MultiStatus { @XmlElement(name = "response") - private final List responses; + private final List responses; @XmlElement(name = "responsedescription") private final ResponseDescription responseDescription; @@ -61,7 +61,7 @@ public final class MultiStatus { } public MultiStatus(final ResponseDescription responseDescription, - final Response... responses) { + final WebDavResponse... responses) { if (responses == null || responses.length == 0) { this.responses = Collections.emptyList(); @@ -72,15 +72,15 @@ public final class MultiStatus { this.responseDescription = responseDescription; } - public MultiStatus(final Response... responses) { + public MultiStatus(final WebDavResponse... responses) { this(null, responses); } public MultiStatus(final ResponseDescription responseDescription) { - this(responseDescription, (Response[]) null); + this(responseDescription, (WebDavResponse[]) null); } - public final List getResponses() { + public final List getResponses() { return Collections.unmodifiableList(responses); } diff --git a/ccm-core/src/main/java/org/libreccm/webdav/xml/elements/Response.java b/ccm-core/src/main/java/org/libreccm/webdav/xml/elements/WebDavResponse.java similarity index 94% rename from ccm-core/src/main/java/org/libreccm/webdav/xml/elements/Response.java rename to ccm-core/src/main/java/org/libreccm/webdav/xml/elements/WebDavResponse.java index f10589b65..635492abc 100644 --- a/ccm-core/src/main/java/org/libreccm/webdav/xml/elements/Response.java +++ b/ccm-core/src/main/java/org/libreccm/webdav/xml/elements/WebDavResponse.java @@ -51,8 +51,8 @@ import static javax.xml.bind.annotation.XmlAccessType.*; @XmlAccessorType(FIELD) @XmlType(propOrder = {"hRefs", "status", "propStats", "error", "responseDescription", "location"}) -@XmlRootElement -public final class Response { +@XmlRootElement(name = "response") +public final class WebDavResponse { @XmlElement(name = "href") private final List hRefs; @@ -70,7 +70,7 @@ public final class Response { private Location location; @SuppressWarnings("unused") - private Response() { + private WebDavResponse() { this(new LinkedList(), null, new LinkedList(), @@ -79,7 +79,7 @@ public final class Response { null); } - private Response(final List hRefs, + private WebDavResponse(final List hRefs, final Status status, final List propStats, final Error error, @@ -93,7 +93,7 @@ public final class Response { this.location = location; } - public Response(final HRef hRef, + public WebDavResponse(final HRef hRef, final Error error, final ResponseDescription responseDescription, final Location location, @@ -107,7 +107,7 @@ public final class Response { responseDescription, location); } - public Response(final Status status, + public WebDavResponse(final Status status, final Error error, final ResponseDescription responseDescription, final Location location, @@ -175,10 +175,10 @@ public final class Response { if (obj == null) { return false; } - if (!(obj instanceof Response)) { + if (!(obj instanceof WebDavResponse)) { return false; } - final Response other = (Response) obj; + final WebDavResponse other = (WebDavResponse) obj; if (!Objects.equals(hRefs, other.getHRefs())) { return false; } diff --git a/ccm-core/src/main/resources/themes/ccm/theme-index.json b/ccm-core/src/main/resources/themes/ccm/theme-index.json new file mode 100644 index 000000000..f798c9435 --- /dev/null +++ b/ccm-core/src/main/resources/themes/ccm/theme-index.json @@ -0,0 +1,62 @@ +{ + "files": [ + { + "name": "foo", + "isDirectory": true, + "files": [ + { + "name": "bar", + "isDirectory": true, + "files": [ + { + "name": "theme.json", + "isDirectory": false, + "mimeType": "application/json" + } + ] + }, + { + "name": "theme.xml", + "isDirectory": false, + "mimeType": "text/xml" + } + ] + }, + { + "name": "category-page.xsl", + "isDirectory": false, + "mimeType": "application/xml" + }, + { + "name": "footer.xsl", + "isDirectory": false, + "mimeType": "application/xml" + }, + { + "name": "settings.properties", + "isDirectory": false, + "mimeType": "text/plain" + }, + { + "name": "style.css", + "isDirectory": false, + "mimeType": "text/css" + }, + { + "name": "theme-bundle.properties", + "isDirectory": false, + "mimeType": "text/plain" + }, + { + "name": "texts", + "isDirectory": true, + "files": [ + { + "name": "labels.properties", + "isDirectory": false, + "mimeType": "text/x-java-properties" + } + ] + } + ] +} diff --git a/ccm-pagemodelseditor/package.json b/ccm-pagemodelseditor/package.json index 9620dfc56..74909e78c 100644 --- a/ccm-pagemodelseditor/package.json +++ b/ccm-pagemodelseditor/package.json @@ -9,7 +9,7 @@ "author": "Jens Pelzetter", "license": "GPL-3.0", "scripts": { - "build": "webpack", + "build": "tsc", "tslint": "tslint --project ." }, "dependencies": { diff --git a/ccm-pagemodelseditor/pom.xml b/ccm-pagemodelseditor/pom.xml index 27b192480..7b4ea1bda 100644 --- a/ccm-pagemodelseditor/pom.xml +++ b/ccm-pagemodelseditor/pom.xml @@ -39,9 +39,33 @@ src/main/typescript + + ${project.build.directory}/generated-resources + + + + org.apache.maven.plugins + maven-remote-resources-plugin + 1.5 + + + + bundle + + + + + src/main/typescript + + **/*.ts + **/*.tsx + + + + com.github.eirslett frontend-maven-plugin diff --git a/ccm-pagemodelseditor/tsconfig.json b/ccm-pagemodelseditor/tsconfig.json index d0f172027..9553f095e 100644 --- a/ccm-pagemodelseditor/tsconfig.json +++ b/ccm-pagemodelseditor/tsconfig.json @@ -3,6 +3,7 @@ "jsx": "React", "module": "amd", "moduleResolution": "node", + "outDir": "target/generated-resources/dist", "sourceMap": true, "target": "es6" }, diff --git a/ccm-pagemodelseditor/webpack.config.js b/ccm-pagemodelseditor/webpack.config.js index 3ea6ebbfd..4a9814765 100644 --- a/ccm-pagemodelseditor/webpack.config.js +++ b/ccm-pagemodelseditor/webpack.config.js @@ -9,7 +9,8 @@ module.exports = { }, output: { - path: path.resolve(__dirname, "src/main/resources/dist"), + //path: path.resolve(__dirname, "src/main/resources/dist"), + path: path.resolve(__dirname, "target/generated-resources/dist"), filename: "ccm-pagemodelseditor.js" }, diff --git a/pom.xml b/pom.xml index b3f9426de..aa0d091bf 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,8 @@ ccm-archetype-module ccm-cms-archetype-contenttype + + @@ -777,4 +779,4 @@ - + \ No newline at end of file