mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-01-15 20:51:21 +01:00
Made it possible to share query results via url
This commit is contained in:
parent
c7ed844c76
commit
7baf1d7556
@ -22,6 +22,7 @@ import org.apache.commons.text.TextStringBuilder;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -144,6 +145,17 @@ public enum Html {
|
|||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String decodeFromURL(String string) {
|
||||||
|
try {
|
||||||
|
return StringUtils.replace(
|
||||||
|
URLDecoder.decode(string, "UTF-8"),
|
||||||
|
" ", "+" // Decoding replaces + with spaces
|
||||||
|
);
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The HTML String
|
* @return The HTML String
|
||||||
*/
|
*/
|
||||||
|
@ -34,6 +34,7 @@ import com.djrapitops.plan.storage.database.queries.filter.Filter;
|
|||||||
import com.djrapitops.plan.storage.database.queries.filter.FilterQuery;
|
import com.djrapitops.plan.storage.database.queries.filter.FilterQuery;
|
||||||
import com.djrapitops.plan.storage.database.queries.filter.QueryFilters;
|
import com.djrapitops.plan.storage.database.queries.filter.QueryFilters;
|
||||||
import com.djrapitops.plan.storage.database.queries.objects.playertable.QueryTablePlayersQuery;
|
import com.djrapitops.plan.storage.database.queries.objects.playertable.QueryTablePlayersQuery;
|
||||||
|
import com.djrapitops.plan.storage.json.JSONStorage;
|
||||||
import com.djrapitops.plan.utilities.java.Maps;
|
import com.djrapitops.plan.utilities.java.Maps;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
@ -52,6 +53,7 @@ public class QueryJSONResolver implements Resolver {
|
|||||||
|
|
||||||
private final PlanConfig config;
|
private final PlanConfig config;
|
||||||
private final DBSystem dbSystem;
|
private final DBSystem dbSystem;
|
||||||
|
private final JSONStorage jsonStorage;
|
||||||
private final Locale locale;
|
private final Locale locale;
|
||||||
private final Formatters formatters;
|
private final Formatters formatters;
|
||||||
|
|
||||||
@ -60,12 +62,14 @@ public class QueryJSONResolver implements Resolver {
|
|||||||
QueryFilters filters,
|
QueryFilters filters,
|
||||||
PlanConfig config,
|
PlanConfig config,
|
||||||
DBSystem dbSystem,
|
DBSystem dbSystem,
|
||||||
|
JSONStorage jsonStorage,
|
||||||
Locale locale,
|
Locale locale,
|
||||||
Formatters formatters
|
Formatters formatters
|
||||||
) {
|
) {
|
||||||
this.filters = filters;
|
this.filters = filters;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.dbSystem = dbSystem;
|
this.dbSystem = dbSystem;
|
||||||
|
this.jsonStorage = jsonStorage;
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
this.formatters = formatters;
|
this.formatters = formatters;
|
||||||
}
|
}
|
||||||
@ -82,9 +86,25 @@ public class QueryJSONResolver implements Resolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Response getResponse(Request request) {
|
private Response getResponse(Request request) {
|
||||||
|
// Attempt to find previously created result
|
||||||
|
try {
|
||||||
|
Optional<JSONStorage.StoredJSON> previousResults = request.getQuery().get("timestamp")
|
||||||
|
.flatMap(queryTimestamp -> jsonStorage.fetchExactJson("query", Long.parseLong(queryTimestamp)));
|
||||||
|
if (previousResults.isPresent()) {
|
||||||
|
return Response.builder()
|
||||||
|
.setMimeType(MimeType.JSON)
|
||||||
|
.setJSONContent(previousResults.get().json)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new BadRequestException("Could not parse 'timestamp' into a number. Remove parameter or fix it.");
|
||||||
|
}
|
||||||
|
|
||||||
String q = request.getQuery().get("q").orElseThrow(() -> new BadRequestException("'q' parameter not set (expecting json array)"));
|
String q = request.getQuery().get("q").orElseThrow(() -> new BadRequestException("'q' parameter not set (expecting json array)"));
|
||||||
String view = request.getQuery().get("view").orElseThrow(() -> new BadRequestException("'view' parameter not set (expecting json object {afterDate, afterTime, beforeDate, beforeTime})"));
|
String view = request.getQuery().get("view").orElseThrow(() -> new BadRequestException("'view' parameter not set (expecting json object {afterDate, afterTime, beforeDate, beforeTime})"));
|
||||||
|
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
q = URLDecoder.decode(q, "UTF-8");
|
q = URLDecoder.decode(q, "UTF-8");
|
||||||
List<FilterQuery> queries = FilterQuery.parse(q);
|
List<FilterQuery> queries = FilterQuery.parse(q);
|
||||||
@ -92,15 +112,19 @@ public class QueryJSONResolver implements Resolver {
|
|||||||
|
|
||||||
Map<String, Object> json = Maps.builder(String.class, Object.class)
|
Map<String, Object> json = Maps.builder(String.class, Object.class)
|
||||||
.put("path", result.getResultPath())
|
.put("path", result.getResultPath())
|
||||||
|
.put("view", new Gson().fromJson(view, FiltersJSONResolver.ViewJSON.class))
|
||||||
|
.put("timestamp", timestamp)
|
||||||
.build();
|
.build();
|
||||||
if (!result.isEmpty()) {
|
if (!result.isEmpty()) {
|
||||||
json.put("data", getDataFor(result.getResultUUIDs(), view));
|
json.put("data", getDataFor(result.getResultUUIDs(), view));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSONStorage.StoredJSON stored = jsonStorage.storeJson("query", json, timestamp);
|
||||||
|
|
||||||
return Response.builder()
|
return Response.builder()
|
||||||
.setMimeType(MimeType.JSON)
|
.setMimeType(MimeType.JSON)
|
||||||
.setJSONContent(json)
|
.setJSONContent(stored.json)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
throw new BadRequestException("'view' date format was incorrect (expecting afterDate dd/mm/yyyy, afterTime hh:mm, beforeDate dd/mm/yyyy, beforeTime hh:mm}): " + e.getMessage());
|
throw new BadRequestException("'view' date format was incorrect (expecting afterDate dd/mm/yyyy, afterTime hh:mm, beforeDate dd/mm/yyyy, beforeTime hh:mm}): " + e.getMessage());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -25,6 +25,8 @@ import com.djrapitops.plan.settings.config.PlanConfig;
|
|||||||
import com.djrapitops.plan.settings.locale.Locale;
|
import com.djrapitops.plan.settings.locale.Locale;
|
||||||
import com.djrapitops.plan.settings.locale.LocaleSystem;
|
import com.djrapitops.plan.settings.locale.LocaleSystem;
|
||||||
import com.djrapitops.plan.storage.file.JarResource;
|
import com.djrapitops.plan.storage.file.JarResource;
|
||||||
|
import com.djrapitops.plan.storage.json.JSONFileStorage;
|
||||||
|
import com.djrapitops.plan.storage.json.JSONStorage;
|
||||||
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
import com.djrapitops.plan.utilities.logging.ErrorLogger;
|
||||||
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
|
import com.djrapitops.plan.utilities.logging.PluginErrorLogger;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@ -96,4 +98,10 @@ public class SystemObjectProvidingModule {
|
|||||||
return dataService;
|
return dataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
JSONStorage provideJSONStorage(JSONFileStorage jsonFileStorage) {
|
||||||
|
return jsonFileStorage;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -149,4 +149,8 @@ public class PlanFiles implements SubSystem {
|
|||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Path getJSONStorageDirectory() {
|
||||||
|
return getDataDirectory().resolve("cached_json");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage.json;
|
||||||
|
|
||||||
|
import com.djrapitops.plan.storage.file.PlanFiles;
|
||||||
|
import com.djrapitops.plugin.logging.console.PluginLogger;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In charge of storing json files on disk for later retrieval.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class JSONFileStorage implements JSONStorage {
|
||||||
|
|
||||||
|
private final PluginLogger logger;
|
||||||
|
|
||||||
|
private final Path jsonDirectory;
|
||||||
|
private final Pattern timestampRegex = Pattern.compile(".*-([0-9]*).json");
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public JSONFileStorage(PlanFiles files, PluginLogger logger) {
|
||||||
|
this.logger = logger;
|
||||||
|
|
||||||
|
jsonDirectory = files.getJSONStorageDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoredJSON storeJson(String identifier, String json, long timestamp) {
|
||||||
|
Path writingTo = jsonDirectory.resolve(identifier + '-' + timestamp + ".json");
|
||||||
|
try {
|
||||||
|
Files.createDirectories(jsonDirectory);
|
||||||
|
Files.write(writingTo, json.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn("Could not write a file to " + writingTo.toFile().getAbsolutePath() + ": " + e.getMessage());
|
||||||
|
}
|
||||||
|
return new StoredJSON(json, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<StoredJSON> fetchJSON(String identifier) {
|
||||||
|
File[] stored = jsonDirectory.toFile().listFiles();
|
||||||
|
if (stored == null) return Optional.empty();
|
||||||
|
for (File file : stored) {
|
||||||
|
String fileName = file.getName();
|
||||||
|
if (fileName.endsWith(".json") && fileName.startsWith(identifier)) {
|
||||||
|
return Optional.ofNullable(readStoredJSON(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private StoredJSON readStoredJSON(File from) {
|
||||||
|
Matcher timestampMatch = timestampRegex.matcher(from.getName());
|
||||||
|
if (timestampMatch.find()) {
|
||||||
|
try (Stream<String> lines = Files.lines(from.toPath())) {
|
||||||
|
long timestamp = Long.parseLong(timestampMatch.group(1));
|
||||||
|
StringBuilder json = new StringBuilder();
|
||||||
|
lines.forEach(json::append);
|
||||||
|
return new StoredJSON(json.toString(), timestamp);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " file '" + from.getName() + "' could not be read: " + e.getMessage());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " contained a file '" + from.getName() + "' with improperly formatted -timestamp (could not parse number). This file was not placed there by Plan!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.warn(jsonDirectory.toFile().getAbsolutePath() + " contained a file '" + from.getName() + "' that has no -timestamp. This file was not placed there by Plan!");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<StoredJSON> fetchExactJson(String identifier, long timestamp) {
|
||||||
|
File found = jsonDirectory.resolve(identifier + "-" + timestamp + ".json").toFile();
|
||||||
|
if (!found.exists()) return Optional.empty();
|
||||||
|
return Optional.ofNullable(readStoredJSON(found));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<StoredJSON> fetchJsonMadeBefore(String identifier, long timestamp) {
|
||||||
|
return fetchJSONWithTimestamp(identifier, timestamp, (timestampMatch, time) -> Long.parseLong(timestampMatch.group(1)) < time);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<StoredJSON> fetchJsonMadeAfter(String identifier, long timestamp) {
|
||||||
|
return fetchJSONWithTimestamp(identifier, timestamp, (timestampMatch, time) -> Long.parseLong(timestampMatch.group(1)) > time);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<StoredJSON> fetchJSONWithTimestamp(String identifier, long timestamp, BiPredicate<Matcher, Long> timestampComparator) {
|
||||||
|
File[] stored = jsonDirectory.toFile().listFiles();
|
||||||
|
if (stored == null) return Optional.empty();
|
||||||
|
for (File file : stored) {
|
||||||
|
try {
|
||||||
|
String fileName = file.getName();
|
||||||
|
if (fileName.endsWith(".json") && fileName.startsWith(identifier)) {
|
||||||
|
Matcher timestampMatch = timestampRegex.matcher(fileName);
|
||||||
|
if (timestampMatch.find() && timestampComparator.test(timestampMatch, timestamp)) {
|
||||||
|
return Optional.ofNullable(readStoredJSON(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Ignore this file, malformed timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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.storage.json;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In charge of storing json somewhere for later retrieval.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public interface JSONStorage {
|
||||||
|
|
||||||
|
default StoredJSON storeJson(String identifier, String json) {
|
||||||
|
return storeJson(identifier, json, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
default StoredJSON storeJson(String identifier, Object json) {
|
||||||
|
return storeJson(identifier, new Gson().toJson(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
StoredJSON storeJson(String identifier, String json, long timestamp);
|
||||||
|
|
||||||
|
default StoredJSON storeJson(String identifier, Object json, long timestamp) {
|
||||||
|
return storeJson(identifier, new Gson().toJson(json), timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<StoredJSON> fetchJSON(String identifier);
|
||||||
|
|
||||||
|
Optional<StoredJSON> fetchExactJson(String identifier, long timestamp);
|
||||||
|
|
||||||
|
Optional<StoredJSON> fetchJsonMadeBefore(String identifier, long timestamp);
|
||||||
|
|
||||||
|
Optional<StoredJSON> fetchJsonMadeAfter(String identifier, long timestamp);
|
||||||
|
|
||||||
|
final class StoredJSON {
|
||||||
|
public final String json;
|
||||||
|
public final long timestamp;
|
||||||
|
|
||||||
|
public StoredJSON(String json, long timestamp) {
|
||||||
|
this.json = json;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ let filterCount = 0;
|
|||||||
id: "DOM id",
|
id: "DOM id",
|
||||||
options...
|
options...
|
||||||
}*/
|
}*/
|
||||||
|
let timestamp = undefined;
|
||||||
let filterView = {
|
let filterView = {
|
||||||
afterDate: null,
|
afterDate: null,
|
||||||
afterTime: null,
|
afterTime: null,
|
||||||
@ -53,7 +54,7 @@ class MultipleChoiceFilter extends Filter {
|
|||||||
|
|
||||||
toObject() {
|
toObject() {
|
||||||
let selected = [];
|
let selected = [];
|
||||||
for (let option of document.querySelector('#' + filter.id + " select").selectedOptions) {
|
for (let option of document.querySelector('#' + this.id + " select").selectedOptions) {
|
||||||
selected.push(option.text);
|
selected.push(option.text);
|
||||||
}
|
}
|
||||||
selected = JSON.stringify(selected);
|
selected = JSON.stringify(selected);
|
||||||
@ -257,24 +258,50 @@ function setFilterOption(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let query = [];
|
||||||
|
|
||||||
function performQuery() {
|
function performQuery() {
|
||||||
|
for (let filter of filterQuery) {
|
||||||
|
query.push(filter.toObject());
|
||||||
|
}
|
||||||
|
runQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueryAddress() {
|
||||||
|
if (timestamp) return `./v1/query?timestamp=${timestamp}`;
|
||||||
|
|
||||||
|
const encodedQuery = encodeURIComponent(JSON.stringify(query));
|
||||||
|
const encodedView = encodeURIComponent(JSON.stringify(filterView));
|
||||||
|
return `./v1/query?q=${encodedQuery}&view=${encodedView}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function runQuery() {
|
||||||
const queryButton = document.querySelector('#query-button');
|
const queryButton = document.querySelector('#query-button');
|
||||||
queryButton.setAttribute('disabled', 'true');
|
queryButton.setAttribute('disabled', 'true');
|
||||||
queryButton.classList.add('disabled');
|
queryButton.classList.add('disabled');
|
||||||
|
|
||||||
const query = [];
|
document.querySelector('#content .tab').innerHTML =
|
||||||
for (filter of filterQuery) {
|
`<div class="page-loader">
|
||||||
query.push(filter.toObject());
|
<span class="loader"></span>
|
||||||
}
|
<p class="loader-text">Loading..</p>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
const encodedQuery = encodeURIComponent(JSON.stringify(query));
|
const navButton = document.querySelector('.navbar-nav .nav-item');
|
||||||
const encodedView = encodeURIComponent(JSON.stringify(filterView));
|
navButton.insertAdjacentElement('beforebegin',
|
||||||
jsonRequest(`./v1/query?q=${encodedQuery}&view=${encodedView}`, function (json, error) {
|
`<li class="nav-item nav-button"><a class="nav-link" href="./query">
|
||||||
console.log(filterQuery);
|
<i class="far fa-fw fa-undo"></i>
|
||||||
if (json) console.log(json);
|
<span>Make another query</span>
|
||||||
if (error) console.error(error);
|
</a></li>`);
|
||||||
|
|
||||||
renderDataResultScreen(json.data.players.data.length);
|
jsonRequest(getQueryAddress(), function (json, error) {
|
||||||
|
if (!json.data) {
|
||||||
|
window.history.replaceState({}, '', `${location.pathname}?error=${error ? error : 'Query result expired'}`);
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDataResultScreen(json.data.players.data.length, json.view ? json.view : {});
|
||||||
|
|
||||||
|
window.history.replaceState({}, '', `${location.pathname}?timestamp=${json.timestamp}`);
|
||||||
|
|
||||||
$('.player-table').DataTable({
|
$('.player-table').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
@ -291,7 +318,11 @@ function performQuery() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDataResultScreen(resultCount) {
|
function renderDataResultScreen(resultCount, view) {
|
||||||
|
const afterDate = filterView.afterDate ? filterView.afterDate : view.afterDate;
|
||||||
|
const beforeDate = filterView.beforeDate ? filterView.beforeDate : view.beforeDate;
|
||||||
|
const afterTime = filterView.afterTime ? filterView.afterTime : view.afterTime;
|
||||||
|
const beforeTime = filterView.beforeTime ? filterView.beforeTime : view.beforeTime;
|
||||||
document.querySelector('#content .tab').innerHTML =
|
document.querySelector('#content .tab').innerHTML =
|
||||||
`<div class="container-fluid mt-4">
|
`<div class="container-fluid mt-4">
|
||||||
<!-- Page Heading -->
|
<!-- Page Heading -->
|
||||||
@ -301,12 +332,12 @@ function renderDataResultScreen(resultCount) {
|
|||||||
<p class="mb-0 text-gray-800">(matched ${resultCount} players)</p>
|
<p class="mb-0 text-gray-800">(matched ${resultCount} players)</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 col-sm-12 col-lg-11">
|
<div class="col-xs-12 col-sm-12 col-lg-12">
|
||||||
<div class="card shadow mb-4">
|
<div class="card shadow mb-4">
|
||||||
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
||||||
<h6 class="m-0 font-weight-bold col-black" title=" ${filterView.afterDate} ${filterView.afterTime} - ${filterView.beforeDate} ${filterView.beforeTime}"><i
|
<h6 class="m-0 font-weight-bold col-black" title=" ${afterDate} ${afterTime} - ${beforeDate} ${beforeTime}"><i
|
||||||
class="fas fa-fw fa-users col-black"></i>
|
class="fas fa-fw fa-users col-black"></i>
|
||||||
View: ${filterView.afterDate} - ${filterView.beforeDate}</h6>
|
View: ${afterDate} - ${beforeDate}</h6>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-bordered table-striped table-hover player-table dataTable">
|
<table class="table table-bordered table-striped table-hover player-table dataTable">
|
||||||
|
@ -78,7 +78,7 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Card -->
|
<!-- Card -->
|
||||||
<div class="col-xs-12 col-sm-12 col-lg-11">
|
<div class="col-xs-12 col-sm-12 col-lg-12">
|
||||||
<div class="card shadow mb-4">
|
<div class="card shadow mb-4">
|
||||||
<div class="card-body" id="data_player_info">
|
<div class="card-body" id="data_player_info">
|
||||||
<label class="mt-2 mb-0" for="viewFromDateField">Show a view</label>
|
<label class="mt-2 mb-0" for="viewFromDateField">Show a view</label>
|
||||||
@ -322,22 +322,40 @@
|
|||||||
|
|
||||||
<script id="mainScript">
|
<script id="mainScript">
|
||||||
const filters = [];
|
const filters = [];
|
||||||
jsonRequest("./v1/filters", function (json, error) {
|
|
||||||
filters.push(...json.filters);
|
|
||||||
|
|
||||||
filterView = json.view;
|
if (location.search.includes("error=")) {
|
||||||
|
const placeBefore = document.querySelector('.tab .row div');
|
||||||
|
placeBefore.insertAdjacentElement('beforebegin',
|
||||||
|
`<alert class="alert alert-danger alert-dismissible show">
|
||||||
|
${new URLSearchParams(location.search).get("error")}
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</alert>`);
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('viewFromDateField').setAttribute('placeholder', json.view.afterDate);
|
if (location.search.includes('timestamp=')) {
|
||||||
document.getElementById('viewFromTimeField').setAttribute('placeholder', json.view.afterTime);
|
const parameters = new URLSearchParams(location.search);
|
||||||
document.getElementById('viewToDateField').setAttribute('placeholder', json.view.beforeDate);
|
timestamp = parameters.get('timestamp');
|
||||||
document.getElementById('viewToTimeField').setAttribute('placeholder', json.view.beforeTime);
|
runQuery();
|
||||||
|
} else {
|
||||||
|
jsonRequest("./v1/filters", function (json, error) {
|
||||||
|
filters.push(...json.filters);
|
||||||
|
|
||||||
let filterElements = '';
|
filterView = json.view;
|
||||||
for (let i = 0; i < filters.length; i++) {
|
|
||||||
filterElements += createFilterSelector('#filters', i, filters[i]);
|
document.getElementById('viewFromDateField').setAttribute('placeholder', json.view.afterDate);
|
||||||
}
|
document.getElementById('viewFromTimeField').setAttribute('placeholder', json.view.afterTime);
|
||||||
document.getElementById('filter-dropdown').innerHTML = filterElements;
|
document.getElementById('viewToDateField').setAttribute('placeholder', json.view.beforeDate);
|
||||||
});
|
document.getElementById('viewToTimeField').setAttribute('placeholder', json.view.beforeTime);
|
||||||
|
|
||||||
|
let filterElements = '';
|
||||||
|
for (let i = 0; i < filters.length; i++) {
|
||||||
|
filterElements += createFilterSelector('#filters', i, filters[i]);
|
||||||
|
}
|
||||||
|
document.getElementById('filter-dropdown').innerHTML = filterElements;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
@ -18,6 +18,7 @@ package com.djrapitops.plan.storage.database;
|
|||||||
|
|
||||||
import com.djrapitops.plan.delivery.DeliveryUtilities;
|
import com.djrapitops.plan.delivery.DeliveryUtilities;
|
||||||
import com.djrapitops.plan.identification.ServerInfo;
|
import com.djrapitops.plan.identification.ServerInfo;
|
||||||
|
import com.djrapitops.plan.modules.FiltersModule;
|
||||||
import com.djrapitops.plan.settings.ConfigSystem;
|
import com.djrapitops.plan.settings.ConfigSystem;
|
||||||
import com.djrapitops.plan.settings.config.PlanConfig;
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
||||||
import com.djrapitops.plan.storage.file.PlanFiles;
|
import com.djrapitops.plan.storage.file.PlanFiles;
|
||||||
@ -34,6 +35,7 @@ import java.nio.file.Path;
|
|||||||
@Component(modules = {
|
@Component(modules = {
|
||||||
DBSystemModule.class,
|
DBSystemModule.class,
|
||||||
TestSystemObjectProvidingModule.class,
|
TestSystemObjectProvidingModule.class,
|
||||||
|
FiltersModule.class,
|
||||||
|
|
||||||
TestAPFModule.class,
|
TestAPFModule.class,
|
||||||
PlanPluginModule.class,
|
PlanPluginModule.class,
|
||||||
|
@ -20,6 +20,8 @@ import com.djrapitops.plan.identification.ServerInfo;
|
|||||||
import com.djrapitops.plan.identification.ServerServerInfo;
|
import com.djrapitops.plan.identification.ServerServerInfo;
|
||||||
import com.djrapitops.plan.settings.BukkitConfigSystem;
|
import com.djrapitops.plan.settings.BukkitConfigSystem;
|
||||||
import com.djrapitops.plan.settings.ConfigSystem;
|
import com.djrapitops.plan.settings.ConfigSystem;
|
||||||
|
import com.djrapitops.plan.storage.json.JSONFileStorage;
|
||||||
|
import com.djrapitops.plan.storage.json.JSONStorage;
|
||||||
import dagger.Binds;
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
|
|
||||||
@ -37,4 +39,7 @@ public interface PlanPluginModule {
|
|||||||
@Binds
|
@Binds
|
||||||
ServerInfo bindServerInfo(ServerServerInfo serverServerInfo);
|
ServerInfo bindServerInfo(ServerServerInfo serverServerInfo);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
JSONStorage bindJSONStorage(JSONFileStorage jsonFileStorage);
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user