diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java index 26b896f96..b424d4dd7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java @@ -131,48 +131,50 @@ public class PlayerPageExporter extends FileExporter { private void exportRequiredResources(ExportPaths exportPaths, Path toDirectory) throws IOException { // Style exportResources(exportPaths, toDirectory, - "img/Flaticon_circle.png", - "css/sb-admin-2.css", - "css/style.css", - "vendor/jquery/jquery.min.js", - "vendor/bootstrap/js/bootstrap.bundle.min.js", - "vendor/jquery-easing/jquery.easing.min.js", - "vendor/datatables/jquery.dataTables.min.js", - "vendor/datatables/dataTables.bootstrap4.min.js", - "vendor/highcharts/highstock.js", - "vendor/highcharts/map.js", - "vendor/highcharts/world.js", - "vendor/highcharts/drilldown.js", - "vendor/highcharts/highcharts-more.js", - "vendor/highcharts/no-data-to-display.js", - "vendor/fullcalendar/fullcalendar.min.css", - "vendor/momentjs/moment.js", - "vendor/fullcalendar/fullcalendar.min.js", - "vendor/fontawesome-free/css/all.min.css", - "vendor/fontawesome-free/webfonts/fa-brands-400.eot", - "vendor/fontawesome-free/webfonts/fa-brands-400.ttf", - "vendor/fontawesome-free/webfonts/fa-brands-400.woff", - "vendor/fontawesome-free/webfonts/fa-brands-400.woff2", - "vendor/fontawesome-free/webfonts/fa-regular-400.eot", - "vendor/fontawesome-free/webfonts/fa-regular-400.ttf", - "vendor/fontawesome-free/webfonts/fa-regular-400.woff", - "vendor/fontawesome-free/webfonts/fa-regular-400.woff2", - "vendor/fontawesome-free/webfonts/fa-solid-900.eot", - "vendor/fontawesome-free/webfonts/fa-solid-900.ttf", - "vendor/fontawesome-free/webfonts/fa-solid-900.woff", - "vendor/fontawesome-free/webfonts/fa-solid-900.woff2", - "js/sb-admin-2.js", - "js/xmlhttprequests.js", - "js/color-selector.js", - "js/sessionAccordion.js", - "js/graphs.js", - "js/player-values.js" + "../img/Flaticon_circle.png", + "../css/sb-admin-2.css", + "../css/style.css", + "../vendor/jquery/jquery.min.js", + "../vendor/bootstrap/js/bootstrap.bundle.min.js", + "../vendor/jquery-easing/jquery.easing.min.js", + "../vendor/datatables/jquery.dataTables.min.js", + "../vendor/datatables/dataTables.bootstrap4.min.js", + "../vendor/highcharts/highstock.js", + "../vendor/highcharts/map.js", + "../vendor/highcharts/world.js", + "../vendor/highcharts/drilldown.js", + "../vendor/highcharts/highcharts-more.js", + "../vendor/highcharts/no-data-to-display.js", + "../vendor/fullcalendar/fullcalendar.min.css", + "../vendor/momentjs/moment.js", + "../vendor/fullcalendar/fullcalendar.min.js", + "../vendor/fontawesome-free/css/all.min.css", + "../vendor/fontawesome-free/webfonts/fa-brands-400.eot", + "../vendor/fontawesome-free/webfonts/fa-brands-400.ttf", + "../vendor/fontawesome-free/webfonts/fa-brands-400.woff", + "../vendor/fontawesome-free/webfonts/fa-brands-400.woff2", + "../vendor/fontawesome-free/webfonts/fa-regular-400.eot", + "../vendor/fontawesome-free/webfonts/fa-regular-400.ttf", + "../vendor/fontawesome-free/webfonts/fa-regular-400.woff", + "../vendor/fontawesome-free/webfonts/fa-regular-400.woff2", + "../vendor/fontawesome-free/webfonts/fa-solid-900.eot", + "../vendor/fontawesome-free/webfonts/fa-solid-900.ttf", + "../vendor/fontawesome-free/webfonts/fa-solid-900.woff", + "../vendor/fontawesome-free/webfonts/fa-solid-900.woff2", + "../js/sb-admin-2.js", + "../js/xmlhttprequests.js", + "../js/color-selector.js", + "../js/sessionAccordion.js", + "../js/graphs.js", + "../js/player-values.js" ); } private void exportResources(ExportPaths exportPaths, Path toDirectory, String... resourceNames) throws IOException { for (String resourceName : resourceNames) { - exportResource(exportPaths, toDirectory, resourceName); + String nonRelativePath = toNonRelativePath(resourceName); + exportResource(exportPaths, toDirectory, nonRelativePath); + exportPaths.put(resourceName, toRelativePathFromRoot(nonRelativePath)); } } @@ -187,8 +189,6 @@ public class PlayerPageExporter extends FileExporter { } else { export(to, resource); } - - exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); } private String toRelativePathFromRoot(String resourceName) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java index 323e3090e..87c40add1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java @@ -174,14 +174,10 @@ public class PageFactory { } } - public Page errorPage(String title, String error) { - try { - return new ErrorMessagePage( - getResource("web/error.html"), title, error, - versionCheckSystem.get()); - } catch (IOException noParse) { - return internalErrorPage("Failed to create error message page for error: '" + error + "'", noParse); - } + public Page errorPage(String title, String error) throws IOException { + return new ErrorMessagePage( + getResource("web/error.html"), title, error, + versionCheckSystem.get()); } public String getResource(String name) throws IOException { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/ResolverSvc.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/ResolverSvc.java index 476def004..f16aec7f8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/ResolverSvc.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/web/ResolverSvc.java @@ -20,9 +20,7 @@ import com.djrapitops.plan.delivery.web.resolver.Resolver; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Optional; +import java.util.*; import java.util.function.Predicate; import java.util.regex.Pattern; @@ -34,8 +32,8 @@ import java.util.regex.Pattern; @Singleton public class ResolverSvc implements ResolverService { - private final Collection basicResolvers; - private final Collection regexResolvers; + private final List basicResolvers; + private final List regexResolvers; @Inject public ResolverSvc() { @@ -49,12 +47,14 @@ public class ResolverSvc implements ResolverService { @Override public void registerResolver(String pluginName, String start, Resolver resolver) { - basicResolvers.add(new Container(pluginName, checking -> checking.startsWith(start), resolver)); + basicResolvers.add(new Container(pluginName, checking -> checking.startsWith(start), resolver, start)); + Collections.sort(basicResolvers); } @Override public void registerResolverForMatches(String pluginName, Pattern pattern, Resolver resolver) { - regexResolvers.add(new Container(pluginName, pattern.asPredicate(), resolver)); + regexResolvers.add(new Container(pluginName, pattern.asPredicate(), resolver, pattern.pattern())); + Collections.sort(regexResolvers); } @Override @@ -78,15 +78,39 @@ public class ResolverSvc implements ResolverService { return Optional.empty(); } - private static class Container { + private static class Container implements Comparable { final String plugin; final Predicate matcher; final Resolver resolver; + final String sortBy; - public Container(String plugin, Predicate matcher, Resolver resolver) { + public Container(String plugin, Predicate matcher, Resolver resolver, String sortBy) { this.plugin = plugin; this.matcher = matcher; this.resolver = resolver; + this.sortBy = sortBy; + } + + @Override + public int compareTo(Container o) { + // Longest first + return Integer.compare(o.sortBy.length(), this.sortBy.length()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Container)) return false; + Container container = (Container) o; + return Objects.equals(plugin, container.plugin) && + Objects.equals(matcher, container.matcher) && + Objects.equals(resolver, container.resolver) && + Objects.equals(sortBy, container.sortBy); + } + + @Override + public int hashCode() { + return Objects.hash(plugin, matcher, resolver, sortBy); } } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseResolver.java index ce68f9885..645582e32 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseResolver.java @@ -93,7 +93,7 @@ public class ResponseResolver extends CompositePageResolver { public void registerPages() { resolverService.registerResolver("Plan", "/debug", debugPageResolver); resolverService.registerResolver("Plan", "/players", playersPageResolver); - registerPage("player", playerPageResolver); + resolverService.registerResolver("Plan", "/player", playerPageResolver); registerPage("network", serverPageResolver); registerPage("server", serverPageResolver); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayerPageResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayerPageResolver.java index 16371a4b5..bf31b05c2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayerPageResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayerPageResolver.java @@ -16,21 +16,13 @@ */ package com.djrapitops.plan.delivery.webserver.pages; -import com.djrapitops.plan.delivery.domain.WebUser_old; -import com.djrapitops.plan.delivery.webserver.Request; -import com.djrapitops.plan.delivery.webserver.RequestTarget; -import com.djrapitops.plan.delivery.webserver.auth.Authentication; +import com.djrapitops.plan.delivery.web.resolver.*; import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; -import com.djrapitops.plan.delivery.webserver.response.Response_old; -import com.djrapitops.plan.exceptions.WebUserAuthException; -import com.djrapitops.plan.exceptions.connection.ForbiddenException; -import com.djrapitops.plan.exceptions.connection.WebException; import com.djrapitops.plan.identification.UUIDUtility; -import com.djrapitops.plan.storage.database.DBSystem; -import com.djrapitops.plan.storage.database.Database; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.Optional; import java.util.UUID; /** @@ -39,50 +31,39 @@ import java.util.UUID; * @author Rsl1122 */ @Singleton -public class PlayerPageResolver implements PageResolver { +public class PlayerPageResolver implements Resolver { private final ResponseFactory responseFactory; - private final DBSystem dbSystem; private final UUIDUtility uuidUtility; @Inject public PlayerPageResolver( ResponseFactory responseFactory, - DBSystem dbSystem, UUIDUtility uuidUtility ) { this.responseFactory = responseFactory; - this.dbSystem = dbSystem; this.uuidUtility = uuidUtility; } @Override - public Response_old resolve(Request request, RequestTarget target) throws WebException { - if (target.isEmpty()) { - return responseFactory.pageNotFound404_old(); - } - - String playerName = target.get(0); - UUID playerUUID = uuidUtility.getUUIDOf(playerName); - - boolean raw = target.size() >= 2 && target.get(1).equalsIgnoreCase("raw"); - - if (playerUUID == null) { - return responseFactory.uuidNotFound404_old(); - } - Database.State dbState = dbSystem.getDatabase().getState(); - if (dbState != Database.State.OPEN) { - throw new ForbiddenException("Database is " + dbState.name() + " - Please try again later. You can check database status with /plan info"); - } - if (raw) { - return responseFactory.rawPlayerPageResponse_old(playerUUID); - } - return responseFactory.playerPageResponse_old(playerUUID); + public boolean canAccess(WebUser user, URIPath target, URIQuery query) { + boolean isOwnPage = target.getPart(1).map(user.getName()::equalsIgnoreCase).orElse(true); + return user.hasPermission("page.player.other") || (user.hasPermission("page.player.self") && isOwnPage); } @Override - public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { - WebUser_old webUser = auth.getWebUser(); - return webUser.getPermLevel() <= 1 || webUser.getName().equalsIgnoreCase(target.get(target.size() - 1)); + public Optional resolve(URIPath target, URIQuery query) { + Optional part = target.getPart(1); + if (!part.isPresent()) return Optional.empty(); + + String playerName = part.get(); + UUID playerUUID = uuidUtility.getUUIDOf(playerName); + if (playerUUID == null) return Optional.of(responseFactory.uuidNotFound404()); + + boolean raw = target.getPart(2).map("raw"::equalsIgnoreCase).orElse(false); + return Optional.of( + raw ? responseFactory.rawPlayerPageResponse(playerUUID) + : responseFactory.playerPageResponse(playerUUID) + ); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseFactory.java index cb7d64e26..612f04ca5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseFactory.java @@ -16,6 +16,7 @@ */ package com.djrapitops.plan.delivery.webserver.response; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; import com.djrapitops.plan.delivery.rendering.pages.Page; import com.djrapitops.plan.delivery.rendering.pages.PageFactory; import com.djrapitops.plan.delivery.web.resolver.MimeType; @@ -77,15 +78,17 @@ public class ResponseFactory { } } - public Response forPage(Page page) throws IOException { - return Response.builder().setContent(page.toHtml()) + private Response forPage(Page page) { + return Response.builder() .setMimeType(MimeType.HTML) + .setContent(page.toHtml()) .build(); } - public Response forInternalError(String cause, Throwable error) { - return Response.builder().setContent(pageFactory.internalErrorPage(cause, error).toHtml()) + private Response forInternalError(String cause, Throwable error) { + return Response.builder() .setMimeType(MimeType.HTML) + .setContent(pageFactory.internalErrorPage(cause, error).toHtml()) .setStatus(500) .build(); } @@ -103,16 +106,26 @@ public class ResponseFactory { private Optional checkIfDBIsOpen() { Database.State dbState = dbSystem.getDatabase().getState(); if (dbState != Database.State.OPEN) { - return Optional.of(Response.builder().setContent(pageFactory.errorPage( - "503 Resources Unavailable", - "Database is " + dbState.name() + " - Please try again later. You can check database status with /plan info" - ).toHtml()).setMimeType(MimeType.HTML) - .setStatus(503) - .build()); + try { + return Optional.of(buildDBNotOpenResponse(dbState)); + } catch (IOException e) { + return Optional.of(forInternalError("Database was not open, additionally failed to generate error page for that", e)); + } } return Optional.empty(); } + private Response buildDBNotOpenResponse(Database.State dbState) throws IOException { + return Response.builder() + .setMimeType(MimeType.HTML) + .setContent(pageFactory.errorPage( + "503 Resources Unavailable", + "Database is " + dbState.name() + " - Please try again later. You can check database status with /plan info" + ).toHtml()) + .setStatus(503) + .build(); + } + @Deprecated public ErrorResponse internalErrorResponse_old(Throwable e, String s) { try { @@ -149,6 +162,14 @@ public class ResponseFactory { return new RawPlayerDataResponse(dbSystem.getDatabase().query(ContainerFetchQueries.fetchPlayerContainer(uuid))); } + public Response rawPlayerPageResponse(UUID playerUUID) { + PlayerContainer player = dbSystem.getDatabase().query(ContainerFetchQueries.fetchPlayerContainer(playerUUID)); + return Response.builder() + .setMimeType(MimeType.JSON) + .setJSONContent(RawDataResponse.mapToNormalMap(player)) + .build(); + } + @Deprecated public Response_old javaScriptResponse_old(String fileName) { try { @@ -213,11 +234,19 @@ public class ResponseFactory { return notFound404_old(locale.getString(ErrorPageLang.UUID_404)); } + public Response uuidNotFound404() { + return notFound404(locale.getString(ErrorPageLang.UUID_404)); + } + @Deprecated public ErrorResponse playerNotFound404_old() { return notFound404_old(locale.getString(ErrorPageLang.NOT_PLAYED_404)); } + public Response playerNotFound404() { + return notFound404(locale.getString(ErrorPageLang.NOT_PLAYED_404)); + } + @Deprecated public ErrorResponse notFound404_old(String message) { try { @@ -227,6 +256,19 @@ public class ResponseFactory { } } + public Response notFound404(String message) { + + try { + return Response.builder() + .setMimeType(MimeType.HTML) + .setContent(pageFactory.errorPage("404 " + message, message).toHtml()) + .setStatus(404) + .build(); + } catch (IOException e) { + return forInternalError("Failed to generate 404 page with message '" + message + "'", e); + } + } + @Deprecated public ErrorResponse basicAuthFail_old(WebUserAuthException e) { try { @@ -265,6 +307,16 @@ public class ResponseFactory { return new BadRequestResponse(errorMessage + " (when requesting '" + target + "')"); } + public Response playerPageResponse(UUID playerUUID) { + try { + return forPage(pageFactory.playerPage(playerUUID)); + } catch (IllegalStateException e) { + return playerNotFound404(); + } catch (IOException e) { + return forInternalError("Failed to generate player page", e); + } + } + @Deprecated public Response_old playerPageResponse_old(UUID playerUUID) { try { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawDataResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawDataResponse.java index a23794722..458178b53 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawDataResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawDataResponse.java @@ -38,7 +38,7 @@ public class RawDataResponse extends JSONResponse { super(mapToNormalMap(dataContainer)); } - private static Map mapToNormalMap(DataContainer player) { + public static Map mapToNormalMap(DataContainer player) { Map values = new HashMap<>(); player.getMap().forEach((key, value) -> { diff --git a/Plan/common/src/main/resources/assets/plan/web/player.html b/Plan/common/src/main/resources/assets/plan/web/player.html index 2f2df616a..82166387e 100644 --- a/Plan/common/src/main/resources/assets/plan/web/player.html +++ b/Plan/common/src/main/resources/assets/plan/web/player.html @@ -13,14 +13,14 @@ Plan | ${playerName} - + - - + + @@ -41,7 +41,7 @@ - + @@ -699,36 +699,37 @@ - - + + - + - - - - - - - - - + + + + + + + + + - - - + + + - - - + + +