Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bdb741f
Prototype /theme-editor
AuroraLS3 Jun 8, 2025
0c9c6e5
Further implementation
AuroraLS3 Jul 4, 2025
7674c77
Move Example section to the bottom of the screen
AuroraLS3 Jul 4, 2025
ef73b73
Make forms follow the theme
AuroraLS3 Jul 5, 2025
7d0e629
Refactoring
AuroraLS3 Jul 5, 2025
d0451d4
Add Graph examples
AuroraLS3 Jul 5, 2025
c931047
HighCharts graph color configuration
AuroraLS3 Jul 16, 2025
9f27144
Reduce the amount of necessary colors
AuroraLS3 Jul 17, 2025
5351844
Storage logic for Undo/Redo
AuroraLS3 Jul 17, 2025
da757f0
Change management and prepare for storage
AuroraLS3 Jul 18, 2025
c83d3ab
Table use cases
AuroraLS3 Jul 18, 2025
79d0f9c
More buttons
AuroraLS3 Jul 18, 2025
796c746
Use react-select for MultiSelect
AuroraLS3 Jul 19, 2025
9c5ebe4
Add some multi-color options
AuroraLS3 Jul 28, 2025
8496c08
Move array selectors to use cases
AuroraLS3 Jul 28, 2025
a18ec0d
Improve width reactivity of dropdowns
AuroraLS3 Jul 28, 2025
5cd490f
Various fixes to things
AuroraLS3 Jul 31, 2025
bde2567
Slight cleanup
AuroraLS3 Jul 31, 2025
e1eca77
Translate time formats
AuroraLS3 Aug 1, 2025
dc8e9ef
A couple of time amount formatting fixed
AuroraLS3 Aug 1, 2025
1de6f11
Fixes & more translations (Calendar)
AuroraLS3 Aug 1, 2025
e4b1462
Theme fetching + Format extension dates/times on frontend
AuroraLS3 Aug 2, 2025
0c6c839
Fix Plugin History series appearing in HTTP-mode
AuroraLS3 Aug 2, 2025
7089044
Add endpoint to store themes
AuroraLS3 Aug 2, 2025
e7b233c
Theme select and theme navigation
AuroraLS3 Aug 3, 2025
9a87ce4
localStorage theme storage
AuroraLS3 Aug 3, 2025
e251c74
Match cookies to IP addresses for validity
AuroraLS3 Aug 8, 2025
c8a243d
JSON based forbidden response
AuroraLS3 Aug 9, 2025
d6849d8
Theme saving logic
AuroraLS3 Aug 9, 2025
bf3e574
Implement changing theme color option
AuroraLS3 Aug 9, 2025
0f2a8b1
Fix night mode on page reload
AuroraLS3 Aug 9, 2025
0354cb6
Fix coming out of night mode
AuroraLS3 Aug 9, 2025
a4596bc
Fix theme editor not switching if navigating from sidebar
AuroraLS3 Aug 9, 2025
5157532
Make it clear which color is selected
AuroraLS3 Aug 9, 2025
423b5c6
Fix tab, action button and outline button color application
AuroraLS3 Aug 9, 2025
5c2f8bb
Fix tests
AuroraLS3 Aug 9, 2025
7436f6f
Theme deleting functionality
AuroraLS3 Aug 9, 2025
7b39b0b
Theme delete endpoint
AuroraLS3 Aug 10, 2025
9850627
Deal with some edge cases
AuroraLS3 Aug 10, 2025
828423a
Fix checkstye issue
AuroraLS3 Aug 10, 2025
58f50e2
Fix issues with https only pages
AuroraLS3 Aug 10, 2025
789dc0d
Translate more things (Like country names)
AuroraLS3 Aug 11, 2025
4ae3d79
Update locale files
AuroraLS3 Aug 11, 2025
c100a02
Fix tests
AuroraLS3 Aug 12, 2025
f88aaa9
Fix bugs caught by sonar
AuroraLS3 Aug 12, 2025
f46801e
Fix empty session average on server page
AuroraLS3 Aug 12, 2025
f41aee3
Remove most direct usages of Theme in Java code
AuroraLS3 Aug 12, 2025
10c422e
Delete theme.yml file if it's all defaults
AuroraLS3 Aug 13, 2025
968c52a
Add missing setting to bungeeconfig.yml
AuroraLS3 Aug 13, 2025
3a7ef9a
Merge branch 'master' into 4114/theme-overhaul
AuroraLS3 Aug 13, 2025
8830626
Fix issues found in review
AuroraLS3 Aug 16, 2025
f9fe67a
Fix AssetVersionsTest
AuroraLS3 Aug 17, 2025
3e53a53
Refactor buttons
AuroraLS3 Aug 17, 2025
39b598e
Add missing example sections
AuroraLS3 Aug 17, 2025
1b8bb96
Fix AssetVersionsTest
AuroraLS3 Aug 17, 2025
b360665
Close example section when editing colors
AuroraLS3 Aug 17, 2025
79da0ad
Fix small things found by Sonar
AuroraLS3 Aug 17, 2025
5daa85e
Translate hardcoded 'Example' values in theme examples
AuroraLS3 Aug 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.web.resolver.exception;

import com.djrapitops.plan.delivery.web.resolver.request.Request;

/**
* Throw this exception when a Resolver gets invalid {@link Request#getMethod()}.
* <p>
* Plan will construct error json automatically.
* Note that you might need to handle the error page, which is json: {@code {"status": 405, "error": "message"}}
*
* @author AuroraLS3
*/
public class MethodNotAllowedException extends IllegalStateException {

private final String[] allowedMethods;

/**
* Default constructor.
*
* @param allowedMethods POST, GET, etc. - avoid including any input incoming in the request to prevent XSS.
*/
public MethodNotAllowedException(String... allowedMethods) {
super("Method not allowed");
this.allowedMethods = allowedMethods;
}

public String[] getAllowedMethods() {
return allowedMethods;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public final class Request {
private final WebUser user;
private final Map<String, String> headers;
private final byte[] requestBody;
private final String accessIpAddress;

/**
* Constructor.
Expand All @@ -45,25 +46,58 @@ public final class Request {
* @param user Web user doing the request (if authenticated)
* @param headers Request headers <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers">Documentation</a>
* @param requestBody Raw body as bytes, if present
* @deprecated Use newer constructor with IP address.
*/
@Deprecated
public Request(String method, URIPath path, URIQuery query, WebUser user, Map<String, String> headers, byte[] requestBody) {
this(method, path, query, user, headers, requestBody, null);
}

/**
* Constructor.
*
* @param method HTTP method, GET, PUT, POST, etc
* @param path Requested path /example/target
* @param query Request parameters ?param=value etc
* @param user Web user doing the request (if authenticated)
* @param headers Request headers <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers">Documentation</a>
* @param requestBody Raw body as bytes, if present
* @param accessIpAddress IP address this request is coming from.
*/
public Request(String method, URIPath path, URIQuery query, WebUser user, Map<String, String> headers, byte[] requestBody, String accessIpAddress) {
this.method = method;
this.path = path;
this.query = query;
this.user = user;
this.headers = headers;
this.requestBody = requestBody;
this.accessIpAddress = accessIpAddress;
}

/**
* Special constructor that figures out URIPath and URIQuery from "/path/and?query=params" and has no request body.
*
* @param method HTTP requst method
* @param method HTTP request method
* @param target The requested path and query, e.g. "/path/and?query=params"
* @param user User that made the request
* @param headers HTTP request headers
* @deprecated Use newer constructor with IP address.
*/
@Deprecated
public Request(String method, String target, WebUser user, Map<String, String> headers) {
this(method, target, user, headers, null);
}

/**
* Special constructor that figures out URIPath and URIQuery from "/path/and?query=params" and has no request body.
*
* @param method HTTP request method
* @param target The requested path and query, e.g. "/path/and?query=params"
* @param user User that made the request
* @param headers HTTP request headers
* @param accessIpAddress IP address this request is coming from.
*/
public Request(String method, String target, WebUser user, Map<String, String> headers, String accessIpAddress) {
this.method = method;
if (target.contains("?")) {
String[] halves = StringUtils.split(target, "?", 2);
Expand All @@ -76,6 +110,7 @@ public Request(String method, String target, WebUser user, Map<String, String> h
this.user = user;
this.headers = headers;
this.requestBody = new byte[0];
this.accessIpAddress = accessIpAddress;
}

/**
Expand Down Expand Up @@ -134,7 +169,11 @@ public Optional<String> getHeader(String key) {
}

public Request omitFirstInPath() {
return new Request(method, path.omitFirst(), query, user, headers, requestBody);
return new Request(method, path.omitFirst(), query, user, headers, requestBody, accessIpAddress);
}

public String getAccessIpAddress() {
return accessIpAddress;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

public final class URIPath {

// Example: /target/path/url
private final String path;

public URIPath(String path) {
Expand Down Expand Up @@ -99,6 +100,8 @@ public boolean endsWith(String suffix) {
return path.endsWith(suffix);
}

public boolean startsWith(String prefix) {return path.startsWith(prefix);}

/**
* Immutable modification, removes first part of the path string.
* <p>
Expand Down
17 changes: 17 additions & 0 deletions Plan/common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ tasks.register("determineAssetModifications") {
inputs.files(fileTree("$rootDir/react/dashboard/build"))
inputs.files(fileTree(dir: "src/main/resources/assets/plan/web"))
inputs.files(fileTree(dir: "src/main/resources/assets/plan/locale"))
inputs.files(fileTree(dir: "src/main/resources/assets/plan/themes"))
outputs.file("build/resources/main/assets/plan/AssetVersion.yml")

doLast {
Expand Down Expand Up @@ -228,6 +229,22 @@ tasks.register("determineAssetModifications") {
)
}

tree = fileTree(dir: "src/main/resources/assets/plan/themes")
tree.forEach { File f ->
def gitModified = new ByteArrayOutputStream()
exec {
commandLine "git", "log", "-1", "--pretty=%ct", f.toString()
standardOutput = gitModified
}
def gitModifiedAsString = gitModified.toString().strip()
// git returns UNIX time in seconds, but most things in Java use UNIX time in milliseconds
def modified = gitModifiedAsString.isEmpty() ? System.currentTimeMillis() : Long.parseLong(gitModifiedAsString) * 1000
def relativePath = tree.getDir().toPath().relativize(f.toPath()) // File path relative to the tree
versionFile.text += String.format(
"themes/%s: %s\n", relativePath.toString().replace(".", ",").replace("\\", "/"), modified
)
}

tree = fileTree("$rootDir/react/dashboard/build")
tree.forEach { File f ->
if (f.getName().endsWith(".map")) return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,11 @@ public enum WebPermission implements Supplier<String>, Lang {
ACCESS_QUERY("Allows accessing /query and Query results pages"),
ACCESS_ERRORS("Allows accessing /errors page"),
ACCESS_DOCS("Allows accessing /docs page"),
ACCESS_THEME_EDITOR("Allows accessing /theme-editor page"),

MANAGE_GROUPS("Allows modifying group permissions & Access to /manage/groups page"),
MANAGE_USERS("Allows modifying what users belong to what group");
MANAGE_USERS("Allows modifying what users belong to what group"),
MANAGE_THEMES("Allows saving or deleting themes via theme-editor for everyone");

private final String description;
private final boolean deprecated;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.domain.datatransfer;

import java.util.Map;
import java.util.Objects;

/**
* @author AuroraLS3
*/
public class ThemeDto {

private String name;
private Map<String, String> colors;
private Map<String, String> nightColors;
private Map<String, Object> useCases;
private Map<String, Object> nightModeUseCases;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Map<String, String> getColors() {
return colors;
}

public void setColors(Map<String, String> colors) {
this.colors = colors;
}

public Map<String, String> getNightColors() {
return nightColors;
}

public void setNightColors(Map<String, String> nightColors) {
this.nightColors = nightColors;
}

public Map<String, Object> getUseCases() {
return useCases;
}

public void setUseCases(Map<String, Object> useCases) {
this.useCases = useCases;
}

public Map<String, Object> getNightModeUseCases() {
return nightModeUseCases;
}

public void setNightModeUseCases(Map<String, Object> nightModeUseCases) {
this.nightModeUseCases = nightModeUseCases;
}

@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
ThemeDto themeDto = (ThemeDto) o;
return Objects.equals(getColors(), themeDto.getColors()) && Objects.equals(getNightColors(), themeDto.getNightColors()) && Objects.equals(getUseCases(), themeDto.getUseCases()) && Objects.equals(getNightModeUseCases(), themeDto.getNightModeUseCases());
}

@Override
public int hashCode() {
return Objects.hash(getColors(), getNightColors(), getUseCases(), getNightModeUseCases());
}

@Override
public String toString() {
return "ThemeDto{" +
"colors=" + colors +
", nightColors=" + nightColors +
", useCases=" + useCases +
", nightModeUseCases=" + nightModeUseCases +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static Optional<ExtensionValueDataDto> mapToValue(ExtensionTabData tabDat
if (doubleValue.isPresent()) return doubleValue;
Optional<ExtensionValueDataDto> percentage = tabData.getPercentage(key).map(data -> new ExtensionValueDataDto(data.getDescription(), "PERCENTAGE", data.getFormattedValue(formatters.percentage())));
if (percentage.isPresent()) return percentage;
Optional<ExtensionValueDataDto> number = tabData.getNumber(key).map(data -> new ExtensionValueDataDto(data.getDescription(), data.getFormatType() == FormatType.NONE ? "NUMBER" : data.getFormatType().name(), data.getFormattedValue(formatters.getNumberFormatter(data.getFormatType()))));
Optional<ExtensionValueDataDto> number = tabData.getNumber(key).map(data -> new ExtensionValueDataDto(data.getDescription(), data.getFormatType() == FormatType.NONE ? "NUMBER" : data.getFormatType().name(), data.getRawValue()));
if (number.isPresent()) return number;
Optional<ExtensionValueDataDto> string = tabData.getString(key).map(data -> new ExtensionValueDataDto(data.getDescription(), data.isPlayerName() ? "LINK" : "STRING", data.getFormattedValue()));
if (string.isPresent()) return string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
package com.djrapitops.plan.delivery.domain.datatransfer.extension;

import org.jetbrains.annotations.Nullable;
import com.djrapitops.plan.extension.table.TableColumnFormat;

import java.util.Objects;

Expand All @@ -25,47 +25,39 @@
*/
public class TableCellDto {

private final String value;
@Nullable
private final Object valueUnformatted;
private final Object value;
private final TableColumnFormat format;

public TableCellDto(String value) {
public TableCellDto(Object value, TableColumnFormat format) {
this.value = value;
this.valueUnformatted = null;
this.format = format;
}

public TableCellDto(String value, @Nullable Object valueUnformatted) {
this.value = value;
this.valueUnformatted = valueUnformatted;
}

public String getValue() {
public Object getValue() {
return value;
}

@Nullable
public Object getValueUnformatted() {
return valueUnformatted;
public TableColumnFormat getFormat() {
return format;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TableCellDto that = (TableCellDto) o;
return Objects.equals(getValue(), that.getValue()) && Objects.equals(getValueUnformatted(), that.getValueUnformatted());
return Objects.equals(getValue(), that.getValue()) && getFormat() == that.getFormat();
}

@Override
public int hashCode() {
return Objects.hash(getValue(), getValueUnformatted());
return Objects.hash(getValue(), getFormat());
}

@Override
public String toString() {
return "TableCellDto{" +
"value='" + value + '\'' +
", valueUnformatted=" + valueUnformatted +
"value=" + value +
", format=" + format +
'}';
}
}
Loading