diff --git a/Jenkinsfile b/Jenkinsfile index 5da2acb20..3b531497f 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -7,6 +7,11 @@ pipeline { dir("Plan") { script { sh 'rm -rf builds' + sh 'sed -i -e s/%buildNumber%/\$BUILD_NUMBER/g build.gradle' + sh 'sed -i -e s/%buildNumber%/\$BUILD_NUMBER/g common/src/main/resources/plugin.yml' + sh 'sed -i -e s/%buildNumber%/\$BUILD_NUMBER/g common/src/main/resources/bungee.yml' + sh 'sed -i -e s/%buildNumber%/\$BUILD_NUMBER/g sponge/src/main/java/com/djrapitops/plan/PlanSponge.java' + sh 'sed -i -e s/%buildNumber%/\$BUILD_NUMBER/g velocity/src/main/java/com/djrapitops/plan/PlanVelocity.java' sh './gradlew clean shadowJar --parallel' } } diff --git a/Plan/api/build.gradle b/Plan/api/build.gradle index 1181ceba1..786fe4d12 100644 --- a/Plan/api/build.gradle +++ b/Plan/api/build.gradle @@ -2,7 +2,11 @@ plugins { id "com.jfrog.bintray" version "1.8.4" } -ext.apiVersion = '0.0.6' +dependencies { + compileOnly group: 'org.apache.commons', name: 'commons-lang3', version: '3.9' +} + +ext.apiVersion = '5.0-R0.3' bintray { user = System.getenv('BINTRAY_USER') @@ -15,7 +19,7 @@ bintray { issueTrackerUrl = 'https://github.com/plan-player-analytics/Plan/issues' version { name = "$apiVersion" - desc = "Plan APIv5 version $apiVersion" + desc = "Plan API version $apiVersion" } publications = ['BintrayPublication'] } diff --git a/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java index 3e2ea0a90..0ee8c5d7f 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/capability/Capability.java @@ -41,6 +41,10 @@ enum Capability { * DataExtension API table package, TableProvider, Table and Table.Factory */ DATA_EXTENSION_TABLES, + /** + * DataExtension API groups, GroupProvider and Group parameter methods + */ + DATA_EXTENSION_GROUPS, /** * DataExtension API addition, allows throwing {@link com.djrapitops.plan.extension.NotReadyException} inside a Provider method when your API is not ready for a method call. */ @@ -52,9 +56,13 @@ enum Capability { */ DATA_EXTENSION_SHOW_IN_PLAYER_TABLE, /** - * + * QueryService and CommonQueries */ - QUERY_API; + QUERY_API, + /** + * SettingsService + */ + SETTINGS_API; static Optional getByName(String name) { if (name == null) { diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/BooleanProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/BooleanProvider.java index 8eb5d2da5..f0ddd1331 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/BooleanProvider.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/BooleanProvider.java @@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Method annotation to provide a boolean value about a Player. + * Method annotation to provide a boolean value. *

* Usage: {@code @BooleanProvider boolean method(UUID playerUUID)} *

diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/Conditional.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/Conditional.java index ecc87b070..cc5f0429a 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/Conditional.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/Conditional.java @@ -28,6 +28,9 @@ import java.lang.annotation.Target; * If {@link com.djrapitops.plan.extension.annotation.BooleanProvider} for the condition is not specified the * method tagged with this annotation will not be called, (Condition is assumed false). * + * Please note that Conditional does not cross method parameter boundaries - (Conditional on a player method does not + * take into account conditionals of server). + * * @author Rsl1122 */ @Retention(RetentionPolicy.RUNTIME) diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/DoubleProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/DoubleProvider.java index df004701e..d15abd77f 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/DoubleProvider.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/DoubleProvider.java @@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Method annotation to provide a double value about a Player. + * Method annotation to provide a double value. *

* Usage: {@code @DoubleProvider double method(UUID playerUUID)} * diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/GroupProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/GroupProvider.java new file mode 100644 index 000000000..83b319cd7 --- /dev/null +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/GroupProvider.java @@ -0,0 +1,80 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.annotation; + +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Family; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Method annotation to provide {@code String[]} array of Group names about a Player. + *

+ * This method annotation only works when used with {@code UUID} or {@code String} as a method parameter (for Players). + *

+ * For example: + * {@code @GroupProvider public String[] getJobs(UUID playerUUID) {}} + *

+ * Group data is parsed as Table for /server & /network page and similar to {@link StringProvider} for /player page. + *

+ * Requires Capability {@code DATA_EXTENSION_GROUPS} + * + * @author Rsl1122 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface GroupProvider { + + /** + * Text displayed before the value, limited to 50 characters. + *

+ * Should inform the user what the group names represent, for example + * "Town" or "Job" + * + * @return String of max 50 characters, remainder will be clipped. + */ + String text() default "Group"; + + /** + * Determine the color of the table header for this group. + * + * @return Preferred color. If none are specified defaults are used. + */ + Color groupColor() default Color.NONE; + + /** + * Name of Font Awesome icon. + *

+ * See https://fontawesome.com/icons (select 'free')) for icons and their {@link Family}. + * + * @return Name of the icon, if name is not valid no icon is shown. + */ + String iconName() default "circle"; + + /** + * Family of Font Awesome icon. + *

+ * See https://fontawesome.com/icons (select 'free')) for icons and their {@link Family}. + * + * @return Family that matches an icon, if there is no icon for this family no icon is shown. + */ + Family iconFamily() default Family.SOLID; + +} diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/NumberProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/NumberProvider.java index 6f1a57e1d..6fb4c455f 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/NumberProvider.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/NumberProvider.java @@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Method annotation to provide a long (64bit number) value about a Player. + * Method annotation to provide a long (64bit number) value. *

* If you want to return int values, use this provider with a long as * return type of the method. diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/PercentageProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/PercentageProvider.java index 68da86301..ab1f8e560 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/PercentageProvider.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/PercentageProvider.java @@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Method annotation to provide a double (Percentage) about a Player. + * Method annotation to provide a double (Percentage). *

* Usage: {@code @PercentageProvider double method(UUID playerUUID)} *

diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/StringProvider.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/StringProvider.java index 6b0cfd7d7..923719b46 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/StringProvider.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/annotation/StringProvider.java @@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Method annotation to provide a String value about a Player. + * Method annotation to provide a String value. *

* Usage: {@code @StringProvider String method(UUID playerUUID)} *

diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java index 4165f5ed3..a501ab834 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/ExtensionExtractor.java @@ -98,8 +98,10 @@ public final class ExtensionExtractor { for (Method method : getMethods()) { int modifiers = method.getModifiers(); - if (!Modifier.isPublic(modifiers) - || Modifier.isStatic(modifiers)) { + if (Modifier.isPrivate(modifiers) + || Modifier.isProtected(modifiers) + || Modifier.isStatic(modifiers) + || Modifier.isNative(modifiers)) { continue; } @@ -113,17 +115,30 @@ public final class ExtensionExtractor { MethodAnnotations.get(method, Tab.class).ifPresent(annotation -> methodAnnotations.put(method, Tab.class, annotation)); MethodAnnotations.get(method, TableProvider.class).ifPresent(annotation -> methodAnnotations.put(method, TableProvider.class, annotation)); + MethodAnnotations.get(method, GroupProvider.class).ifPresent(annotation -> methodAnnotations.put(method, GroupProvider.class, annotation)); } if (methodAnnotations.isEmpty()) { throw new IllegalArgumentException(extensionName + " class had no methods annotated with a Provider annotation"); } + + try { + methodAnnotations.makeMethodsAccessible(); + } catch (SecurityException failedToMakeAccessible) { + throw new IllegalArgumentException(extensionName + " has non accessible Provider method that could not be made accessible: " + + failedToMakeAccessible.getMessage(), failedToMakeAccessible); + } } private void validateReturnType(Method method, Class expectedType) { Class returnType = method.getReturnType(); if (!expectedType.isAssignableFrom(returnType)) { - throw new IllegalArgumentException(extensionName + "." + method.getName() + " has invalid return type. was: " + returnType.getName() + ", expected: " + expectedType.getName()); + String expectedName = expectedType.getName(); + throw new IllegalArgumentException(extensionName + "." + method.getName() + + " has invalid return type. was: " + + returnType.getName() + + ", expected: " + + (expectedName.startsWith("[L") ? expectedName + " (an array)" : expectedName)); } } @@ -178,6 +193,7 @@ public final class ExtensionExtractor { validatePercentageProviderAnnotations(); validateStringProviderAnnotations(); validateTableProviderAnnotations(); + validateGroupProviderAnnotations(); } private void validateBooleanProviderAnnotations() { @@ -215,9 +231,9 @@ public final class ExtensionExtractor { } private void validateDoubleProviderAnnotations() { - for (Map.Entry numberProvider : methodAnnotations.getMethodAnnotations(DoubleProvider.class).entrySet()) { - Method method = numberProvider.getKey(); - DoubleProvider annotation = numberProvider.getValue(); + for (Map.Entry doubleProvider : methodAnnotations.getMethodAnnotations(DoubleProvider.class).entrySet()) { + Method method = doubleProvider.getKey(); + DoubleProvider annotation = doubleProvider.getValue(); validateReturnType(method, double.class); validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method); @@ -227,9 +243,9 @@ public final class ExtensionExtractor { } private void validatePercentageProviderAnnotations() { - for (Map.Entry numberProvider : methodAnnotations.getMethodAnnotations(PercentageProvider.class).entrySet()) { - Method method = numberProvider.getKey(); - PercentageProvider annotation = numberProvider.getValue(); + for (Map.Entry percentageProvider : methodAnnotations.getMethodAnnotations(PercentageProvider.class).entrySet()) { + Method method = percentageProvider.getKey(); + PercentageProvider annotation = percentageProvider.getValue(); validateReturnType(method, double.class); validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method); @@ -239,9 +255,9 @@ public final class ExtensionExtractor { } private void validateStringProviderAnnotations() { - for (Map.Entry numberProvider : methodAnnotations.getMethodAnnotations(StringProvider.class).entrySet()) { - Method method = numberProvider.getKey(); - StringProvider annotation = numberProvider.getValue(); + for (Map.Entry stringProvider : methodAnnotations.getMethodAnnotations(StringProvider.class).entrySet()) { + Method method = stringProvider.getKey(); + StringProvider annotation = stringProvider.getValue(); validateReturnType(method, String.class); validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method); @@ -257,6 +273,17 @@ public final class ExtensionExtractor { } } + private void validateGroupProviderAnnotations() { + for (Map.Entry groupProvider : methodAnnotations.getMethodAnnotations(GroupProvider.class).entrySet()) { + Method method = groupProvider.getKey(); + GroupProvider annotation = groupProvider.getValue(); + + validateReturnType(method, String[].class); + validateMethodAnnotationPropertyLength(annotation.text(), "text", 50, method); + validateMethodArguments(method, true, UUID.class, String.class); + } + } + private void validateConditionals() { Collection conditionals = methodAnnotations.getAnnotations(Conditional.class); Collection conditionProviders = methodAnnotations.getAnnotations(BooleanProvider.class); @@ -280,7 +307,8 @@ public final class ExtensionExtractor { for (Method conditionalMethod : conditionalMethods) { if (!MethodAnnotations.hasAnyOf(conditionalMethod, BooleanProvider.class, DoubleProvider.class, NumberProvider.class, - PercentageProvider.class, StringProvider.class + PercentageProvider.class, StringProvider.class, TableProvider.class, + GroupProvider.class )) { throw new IllegalArgumentException(extensionName + "." + conditionalMethod.getName() + " did not have any associated Provider for Conditional."); } diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/MethodAnnotations.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/MethodAnnotations.java index 4b854ab15..d7dea0e08 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/MethodAnnotations.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/extractor/MethodAnnotations.java @@ -73,4 +73,13 @@ public class MethodAnnotations { public String toString() { return "MethodAnnotations{" + byAnnotationType + '}'; } + + void makeMethodsAccessible() { + byAnnotationType.values().stream() + .map(Map::keySet) + .flatMap(Collection::stream) + .distinct() + .filter(method -> !method.isAccessible()) + .forEach(method -> method.setAccessible(true)); + } } \ No newline at end of file diff --git a/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java b/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java index b082fcf1c..c8a1a6a72 100644 --- a/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java +++ b/Plan/api/src/main/java/com/djrapitops/plan/extension/table/Table.java @@ -19,6 +19,7 @@ package com.djrapitops.plan.extension.table; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Icon; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Arrays; @@ -109,7 +110,7 @@ public final class Table { } private Factory column(int indx, String columnName, Icon icon) { - building.columns[indx] = columnName; + building.columns[indx] = StringUtils.truncate(columnName, 50); building.icons[indx] = icon != null ? Icon.called(icon.getName()).of(icon.getFamily()).build() : Icon.called("question").build(); return this; } diff --git a/Plan/api/src/main/java/com/djrapitops/plan/settings/SettingsService.java b/Plan/api/src/main/java/com/djrapitops/plan/settings/SettingsService.java new file mode 100644 index 000000000..136ed9729 --- /dev/null +++ b/Plan/api/src/main/java/com/djrapitops/plan/settings/SettingsService.java @@ -0,0 +1,79 @@ +/* + * 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 . + */ +package com.djrapitops.plan.settings; + +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +/** + * Service for defining plugin specific settings to the Plan config. + *

+ * All given paths will be prepended with "Plugins." to place the settings in the plugins config section. + * It is recommended to use setting paths {@code ".Some_setting"} + *

+ * Requires Capability SETTINGS_API + * + * @author Rsl1122 + */ +public interface SettingsService { + + static SettingsService getInstance() { + return Optional.ofNullable(SettingsServiceHolder.service) + .orElseThrow(() -> new IllegalStateException("SettingsService has not been initialised yet.")); + } + + /** + * Get a String from the config or the default value. + * + * @param path Path in the config + * @param defaultValue Supplier for the default value, {@code () -> "Example"}. + * @return value in the config + */ + String getString(String path, Supplier defaultValue); + + /** + * Get a Integer from the config or the default value. + * + * @param path Path in the config + * @param defaultValue Supplier for the default value, {@code () -> 500}. + * @return value in the config + */ + Integer getInteger(String path, Supplier defaultValue); + + /** + * Get a String list from the config or the default value. + * + * @param path Path in the config + * @param defaultValue Supplier for the default value, {@code () -> Arrays.asList("Example", "Another")}. + * @return value in the config + */ + List getStringList(String path, Supplier> defaultValue); + + class SettingsServiceHolder { + static SettingsService service; + + private SettingsServiceHolder() { + /* Static variable holder */ + } + + static void set(SettingsService service) { + SettingsServiceHolder.service = service; + } + } + +} diff --git a/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java b/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java index c91bb62f8..74c036ba9 100644 --- a/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java +++ b/Plan/api/src/test/java/com/djrapitops/plan/extension/extractor/ExtensionExtractorTest.java @@ -17,6 +17,7 @@ package com.djrapitops.plan.extension.extractor; import com.djrapitops.plan.extension.DataExtension; +import com.djrapitops.plan.extension.Group; import com.djrapitops.plan.extension.annotation.*; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -180,6 +181,34 @@ class ExtensionExtractorTest { assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: com.djrapitops.plan.extension.table.Table", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage()); } + @Test + void groupProviderMustGroupArray() { + @PluginInfo(name = "Extension") + class Extension implements DataExtension { + @GroupProvider + public Double method(UUID playerUUID) { + return null; + } + } + + ExtensionExtractor underTest = new ExtensionExtractor(new Extension()); + assertEquals("Extension.method has invalid return type. was: java.lang.Double, expected: [Ljava.lang.String; (an array)", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage()); + } + + @Test + void groupProviderMustGroupArray2() { + @PluginInfo(name = "Extension") + class Extension implements DataExtension { + @GroupProvider + public Group method(UUID playerUUID) { + return null; + } + } + + ExtensionExtractor underTest = new ExtensionExtractor(new Extension()); + assertEquals("Extension.method has invalid return type. was: com.djrapitops.plan.extension.Group, expected: [Ljava.lang.String; (an array)", assertThrows(IllegalArgumentException.class, underTest::validateAnnotations).getMessage()); + } + @Test void booleanProviderCanNotSupplyItsOwnConditional() { @PluginInfo(name = "Extension") diff --git a/Plan/build.gradle b/Plan/build.gradle index dcc76113d..accbb25ae 100644 --- a/Plan/build.gradle +++ b/Plan/build.gradle @@ -22,7 +22,7 @@ allprojects { wrapper.gradleVersion = "5.5.1" group "com.djrapitops" - version "4.9.4" + version "5.0 RC build %buildNumber%" test { useJUnitPlatform() @@ -57,7 +57,6 @@ subprojects { ext.daggerCompilerVersion = "2.24" ext.abstractPluginFrameworkVersion = "3.4.2" - ext.planPluginBridgeVersion = "4.9.0-R0.3" ext.bukkitVersion = "1.13.2-R0.1-SNAPSHOT" ext.spigotVersion = "1.13.2-R0.1-SNAPSHOT" @@ -76,6 +75,7 @@ subprojects { ext.hikariVersion = "3.4.1" ext.slf4jVersion = "1.7.28" ext.geoIpVersion = "2.12.0" + ext.gsonVersion = "2.8.5" ext.guavaVersion = "28.0-jre" ext.bstatsVersion = "1.4" @@ -102,7 +102,7 @@ subprojects { maven { // bStats Repository url = "https://repo.codemc.org/repository/maven-public" } - maven { // PlanPluginBridge Repository + maven { // Plan Repository url = "https://dl.bintray.com/rsl1122/Plan-repository" } } diff --git a/Plan/bukkit/build.gradle b/Plan/bukkit/build.gradle index fe6752325..654966f0e 100644 --- a/Plan/bukkit/build.gradle +++ b/Plan/bukkit/build.gradle @@ -1,6 +1,7 @@ dependencies { compile project(path: ":common", configuration: 'shadow') - + compileOnly project(":api") + compile "com.djrapitops:AbstractPluginFramework-bukkit:$abstractPluginFrameworkVersion" compile "org.bstats:bstats-bukkit:$bstatsVersion" diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/BukkitServerShutdownSave.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/BukkitServerShutdownSave.java index c710c349d..fba9c0c1c 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/BukkitServerShutdownSave.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/BukkitServerShutdownSave.java @@ -16,8 +16,9 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; +import com.djrapitops.plan.gathering.ServerShutdownSave; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.utilities.java.Reflection; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/BukkitTaskSystem.java similarity index 60% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/BukkitTaskSystem.java index cd2d4ea14..59fe57daa 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/BukkitTaskSystem.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/BukkitTaskSystem.java @@ -14,21 +14,20 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks; +package com.djrapitops.plan; -import com.djrapitops.plan.Plan; -import com.djrapitops.plan.ShutdownHook; -import com.djrapitops.plan.db.tasks.DBCleanTask; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.system.tasks.bukkit.BukkitTPSCountTimer; -import com.djrapitops.plan.system.tasks.bukkit.PaperTPSCountTimer; -import com.djrapitops.plan.system.tasks.bukkit.PingCountTimerBukkit; -import com.djrapitops.plan.system.tasks.server.BootAnalysisTask; -import com.djrapitops.plan.system.tasks.server.ConfigStoreTask; -import com.djrapitops.plan.system.tasks.server.PeriodicAnalysisTask; +import com.djrapitops.plan.gathering.ShutdownHook; +import com.djrapitops.plan.gathering.timed.BukkitPingCounter; +import com.djrapitops.plan.gathering.timed.BukkitTPSCounter; +import com.djrapitops.plan.gathering.timed.PaperTPSCounter; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DataGatheringSettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.upkeep.ConfigStoreTask; +import com.djrapitops.plan.storage.upkeep.DBCleanTask; +import com.djrapitops.plan.storage.upkeep.LogsFolderCleanTask; import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.task.RunnableFactory; @@ -45,14 +44,18 @@ import java.util.concurrent.TimeUnit; * @author Rsl1122 */ @Singleton -public class BukkitTaskSystem extends ServerTaskSystem { +public class BukkitTaskSystem extends TaskSystem { private final Plan plugin; + private final PlanConfig config; private final ShutdownHook shutdownHook; - private final PingCountTimerBukkit pingCountTimer; + private final JSONCache.CleanTask jsonCacheCleanTask; + private final LogsFolderCleanTask logsFolderCleanTask; + private final BukkitPingCounter pingCounter; private final ConfigStoreTask configStoreTask; private final DBCleanTask dbCleanTask; private final ExtensionServerMethodCallerTask extensionServerMethodCallerTask; + private BukkitTPSCounter tpsCounter; @Inject public BukkitTaskSystem( @@ -60,61 +63,77 @@ public class BukkitTaskSystem extends ServerTaskSystem { PlanConfig config, ShutdownHook shutdownHook, RunnableFactory runnableFactory, - PaperTPSCountTimer paperTPSCountTimer, - BukkitTPSCountTimer bukkitTPSCountTimer, - BootAnalysisTask bootAnalysisTask, - PeriodicAnalysisTask periodicAnalysisTask, - PingCountTimerBukkit pingCountTimer, + + PaperTPSCounter paperTPSCountTimer, + BukkitTPSCounter bukkitTPSCountTimer, + BukkitPingCounter pingCounter, + ExtensionServerMethodCallerTask extensionServerMethodCallerTask, + LogsFolderCleanTask logsFolderCleanTask, - PlayersPageRefreshTask playersPageRefreshTask, ConfigStoreTask configStoreTask, DBCleanTask dbCleanTask, - ExtensionServerMethodCallerTask extensionServerMethodCallerTask + JSONCache.CleanTask jsonCacheCleanTask ) { - super( - runnableFactory, - Check.isPaperAvailable() ? paperTPSCountTimer : bukkitTPSCountTimer, - config, - bootAnalysisTask, - periodicAnalysisTask, - logsFolderCleanTask, - playersPageRefreshTask); + super(runnableFactory); this.plugin = plugin; + this.config = config; this.shutdownHook = shutdownHook; - this.pingCountTimer = pingCountTimer; + this.jsonCacheCleanTask = jsonCacheCleanTask; + + this.tpsCounter = Check.isPaperAvailable() ? paperTPSCountTimer : bukkitTPSCountTimer; + this.pingCounter = pingCounter; + this.extensionServerMethodCallerTask = extensionServerMethodCallerTask; + + this.logsFolderCleanTask = logsFolderCleanTask; this.configStoreTask = configStoreTask; this.dbCleanTask = dbCleanTask; - this.extensionServerMethodCallerTask = extensionServerMethodCallerTask; } @Override public void enable() { - super.enable(); - try { - Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY); - if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) { - plugin.registerListener(pingCountTimer); - long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS); - registerTask(pingCountTimer).runTaskTimer(startDelay, 40L); - } - } catch (ExceptionInInitializerError | NoClassDefFoundError ignore) { - // Running CraftBukkit - } + registerTPSCounter(); + registerPingCounter(); + registerExtensionDataGatheringTask(); + registerUpkeepTasks(); + shutdownHook.register(); + } + + private void registerUpkeepTasks() { // +40 ticks / 2 seconds so that update check task runs first. long storeDelay = TimeAmount.toTicks(config.get(TimeSettings.CONFIG_UPDATE_INTERVAL), TimeUnit.MILLISECONDS) + 40; registerTask(configStoreTask).runTaskLaterAsynchronously(storeDelay); - + registerTask(logsFolderCleanTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS)); registerTask(dbCleanTask).runTaskTimerAsynchronously( TimeAmount.toTicks(20, TimeUnit.SECONDS), TimeAmount.toTicks(config.get(TimeSettings.CLEAN_DATABASE_PERIOD), TimeUnit.MILLISECONDS) ); + long minute = TimeAmount.toTicks(1, TimeUnit.MINUTES); + registerTask(jsonCacheCleanTask).runTaskTimerAsynchronously(minute, minute); + } + private void registerTPSCounter() { + registerTask(tpsCounter).runTaskTimer(1000, TimeAmount.toTicks(1L, TimeUnit.SECONDS)); + } + + private void registerPingCounter() { + try { + Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY); + if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) { + plugin.registerListener(pingCounter); + long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS); + registerTask(pingCounter).runTaskTimer(startDelay, 40L); + } + } catch (ExceptionInInitializerError | NoClassDefFoundError ignore) { + // Running CraftBukkit + } + } + + private void registerExtensionDataGatheringTask() { long extensionRefreshPeriod = TimeAmount.toTicks(config.get(TimeSettings.EXTENSION_DATA_REFRESH_PERIOD), TimeUnit.MILLISECONDS); registerTask(extensionServerMethodCallerTask).runTaskTimerAsynchronously( TimeAmount.toTicks(30, TimeUnit.SECONDS), extensionRefreshPeriod ); - shutdownHook.register(); } @Override diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java index 32654b7b6..7631164ee 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java @@ -16,12 +16,12 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.command.PlanCommand; -import com.djrapitops.plan.system.PlanSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.settings.theme.PlanColorScheme; +import com.djrapitops.plan.commands.PlanCommand; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.gathering.ServerShutdownSave; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; +import com.djrapitops.plan.settings.theme.PlanColorScheme; import com.djrapitops.plugin.BukkitPlugin; import com.djrapitops.plugin.benchmarking.Benchmark; import com.djrapitops.plugin.command.ColorScheme; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/PlanBukkitComponent.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/PlanBukkitComponent.java index 60d1223aa..b42c4634e 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/PlanBukkitComponent.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/PlanBukkitComponent.java @@ -16,16 +16,14 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.command.PlanCommand; +import com.djrapitops.plan.commands.PlanCommand; +import com.djrapitops.plan.gathering.ServerShutdownSave; import com.djrapitops.plan.modules.APFModule; import com.djrapitops.plan.modules.FilesModule; -import com.djrapitops.plan.modules.ServerSuperClassBindingModule; import com.djrapitops.plan.modules.SystemObjectProvidingModule; import com.djrapitops.plan.modules.bukkit.BukkitPlanModule; import com.djrapitops.plan.modules.bukkit.BukkitServerPropertiesModule; import com.djrapitops.plan.modules.bukkit.BukkitSuperClassBindingModule; -import com.djrapitops.plan.system.PlanSystem; -import com.djrapitops.pluginbridge.plan.PluginBridgeModule; import dagger.BindsInstance; import dagger.Component; @@ -43,9 +41,7 @@ import javax.inject.Singleton; APFModule.class, FilesModule.class, BukkitServerPropertiesModule.class, - ServerSuperClassBindingModule.class, - BukkitSuperClassBindingModule.class, - PluginBridgeModule.Bukkit.class + BukkitSuperClassBindingModule.class }) public interface PlanBukkitComponent { diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/BukkitImportSystem.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/BukkitImportSystem.java similarity index 90% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/BukkitImportSystem.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/BukkitImportSystem.java index 554c6f10c..8affb2525 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/BukkitImportSystem.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/BukkitImportSystem.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing; +package com.djrapitops.plan.gathering.importing; -import com.djrapitops.plan.system.importing.importers.OfflinePlayerImporter; +import com.djrapitops.plan.gathering.importing.importers.OfflinePlayerImporter; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/data/BukkitUserImportRefiner.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/data/BukkitUserImportRefiner.java similarity index 98% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/data/BukkitUserImportRefiner.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/data/BukkitUserImportRefiner.java index 7afd4683e..7d840315b 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/data/BukkitUserImportRefiner.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/data/BukkitUserImportRefiner.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing.data; +package com.djrapitops.plan.gathering.importing.data; +import com.djrapitops.plan.DebugChannels; import com.djrapitops.plan.Plan; -import com.djrapitops.plan.system.DebugChannels; import com.djrapitops.plugin.api.utility.UUIDFetcher; import com.djrapitops.plugin.benchmarking.Timings; import org.bukkit.OfflinePlayer; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/importers/BukkitImporter.java similarity index 85% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/importers/BukkitImporter.java index 6bfb67a3c..ffdbed27b 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/BukkitImporter.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/importers/BukkitImporter.java @@ -14,25 +14,21 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing.importers; +package com.djrapitops.plan.gathering.importing.importers; import com.djrapitops.plan.Plan; -import com.djrapitops.plan.data.container.BaseUser; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.container.UserInfo; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.LargeStoreQueries; -import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.system.cache.GeolocationCache; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.importing.data.BukkitUserImportRefiner; -import com.djrapitops.plan.system.importing.data.ServerImportData; -import com.djrapitops.plan.system.importing.data.UserImportData; -import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.gathering.cache.GeolocationCache; +import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.gathering.importing.data.BukkitUserImportRefiner; +import com.djrapitops.plan.gathering.importing.data.ServerImportData; +import com.djrapitops.plan.gathering.importing.data.UserImportData; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.LargeStoreQueries; +import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries; +import com.djrapitops.plan.storage.database.transactions.Transaction; import com.djrapitops.plugin.utilities.Verify; import java.util.*; @@ -102,7 +98,6 @@ public abstract class BukkitImporter implements Importer { @Override protected void performOperations() { execute(LargeStoreQueries.storeAllTPSData(Collections.singletonMap(serverUUID.get(), serverImportData.getTpsData()))); - execute(LargeStoreQueries.storeAllCommandUsageData(Collections.singletonMap(serverUUID.get(), serverImportData.getCommandUsages()))); } }); } @@ -204,7 +199,7 @@ public abstract class BukkitImporter implements Importer { return userImportData.getIps().parallelStream() .map(ip -> { String geoLoc = geolocationCache.getCountry(ip); - return new GeoInfo(ip, geoLoc, date); + return new GeoInfo(geoLoc, date); }).collect(Collectors.toList()); } } diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/OfflinePlayerImporter.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/importers/OfflinePlayerImporter.java similarity index 86% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/OfflinePlayerImporter.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/importers/OfflinePlayerImporter.java index e57a0044e..e7987473f 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/importing/importers/OfflinePlayerImporter.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/importing/importers/OfflinePlayerImporter.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing.importers; +package com.djrapitops.plan.gathering.importing.importers; import com.djrapitops.plan.Plan; -import com.djrapitops.plan.system.cache.GeolocationCache; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.importing.data.ServerImportData; -import com.djrapitops.plan.system.importing.data.UserImportData; -import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.gathering.cache.GeolocationCache; +import com.djrapitops.plan.gathering.importing.data.ServerImportData; +import com.djrapitops.plan.gathering.importing.data.UserImportData; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/BukkitListenerSystem.java similarity index 87% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/BukkitListenerSystem.java index 6a00fe207..1d74aea70 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/BukkitListenerSystem.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/BukkitListenerSystem.java @@ -14,14 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners; +package com.djrapitops.plan.gathering.listeners; import com.djrapitops.plan.Plan; import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.api.events.PlanBukkitEnableEvent; import com.djrapitops.plan.capability.CapabilityServiceImplementation; -import com.djrapitops.plan.system.listeners.bukkit.*; -import com.djrapitops.plan.system.status.Status; +import com.djrapitops.plan.gathering.listeners.bukkit.*; import org.bukkit.Bukkit; import org.bukkit.event.HandlerList; @@ -36,9 +35,8 @@ public class BukkitListenerSystem extends ListenerSystem { private final ChatListener chatListener; private final GameModeChangeListener gamemodeChangeListener; private final WorldChangeListener worldChangeListener; - private final CommandListener commandListener; private final DeathEventListener deathEventListener; - private final AFKListener afkListener; + private final BukkitAFKListener afkListener; @Inject public BukkitListenerSystem(Plan plugin, @@ -47,9 +45,8 @@ public class BukkitListenerSystem extends ListenerSystem { ChatListener chatListener, GameModeChangeListener gamemodeChangeListener, WorldChangeListener worldChangeListener, - CommandListener commandListener, DeathEventListener deathEventListener, - AFKListener afkListener + BukkitAFKListener afkListener ) { this.plugin = plugin; this.status = status; @@ -58,7 +55,6 @@ public class BukkitListenerSystem extends ListenerSystem { this.chatListener = chatListener; this.gamemodeChangeListener = gamemodeChangeListener; this.worldChangeListener = worldChangeListener; - this.commandListener = commandListener; this.deathEventListener = deathEventListener; this.afkListener = afkListener; } @@ -70,7 +66,6 @@ public class BukkitListenerSystem extends ListenerSystem { chatListener, gamemodeChangeListener, worldChangeListener, - commandListener, deathEventListener, afkListener ); diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/BukkitAFKListener.java similarity index 89% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/BukkitAFKListener.java index 3ad918ca0..ced225e8a 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/AFKListener.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/BukkitAFKListener.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners.bukkit; +package com.djrapitops.plan.gathering.listeners.bukkit; -import com.djrapitops.plan.system.afk.AFKTracker; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.gathering.afk.AFKTracker; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.config.PlanConfig; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import org.bukkit.entity.Player; @@ -40,7 +40,7 @@ import java.util.UUID; * @author Rsl1122 * @see PlayerOnlineListener */ -public class AFKListener implements Listener { +public class BukkitAFKListener implements Listener { // Static so that /reload does not cause afk tracking to fail. static AFKTracker AFK_TRACKER; @@ -49,11 +49,11 @@ public class AFKListener implements Listener { private final ErrorHandler errorHandler; @Inject - public AFKListener(PlanConfig config, ErrorHandler errorHandler) { + public BukkitAFKListener(PlanConfig config, ErrorHandler errorHandler) { this.errorHandler = errorHandler; this.ignorePermissionInfo = new HashMap<>(); - AFKListener.assignAFKTracker(config); + BukkitAFKListener.assignAFKTracker(config); } private static void assignAFKTracker(PlanConfig config) { diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/ChatListener.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/ChatListener.java similarity index 87% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/ChatListener.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/ChatListener.java index 2cb5c0d1e..c52e1b2db 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/ChatListener.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/ChatListener.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners.bukkit; +package com.djrapitops.plan.gathering.listeners.bukkit; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.db.access.transactions.events.NicknameStoreTransaction; -import com.djrapitops.plan.system.cache.NicknameCache; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.gathering.cache.NicknameCache; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.transactions.events.NicknameStoreTransaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import org.bukkit.entity.Player; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/DeathEventListener.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/DeathEventListener.java similarity index 91% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/DeathEventListener.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/DeathEventListener.java index a6f0e773d..cfd9b27aa 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/DeathEventListener.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/DeathEventListener.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners.bukkit; +package com.djrapitops.plan.gathering.listeners.bukkit; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.processing.processors.player.MobKillProcessor; -import com.djrapitops.plan.system.processing.processors.player.PlayerKillProcessor; -import com.djrapitops.plan.utilities.formatting.EntityNameFormatter; -import com.djrapitops.plan.utilities.formatting.ItemNameFormatter; +import com.djrapitops.plan.delivery.formatting.EntityNameFormatter; +import com.djrapitops.plan.delivery.formatting.ItemNameFormatter; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.processing.processors.player.MobKillProcessor; +import com.djrapitops.plan.processing.processors.player.PlayerKillProcessor; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import org.bukkit.Material; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/GameModeChangeListener.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/GameModeChangeListener.java similarity index 86% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/GameModeChangeListener.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/GameModeChangeListener.java index 40ddc29d5..45b365c27 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/GameModeChangeListener.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/GameModeChangeListener.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners.bukkit; +package com.djrapitops.plan.gathering.listeners.bukkit; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.db.access.transactions.events.WorldNameStoreTransaction; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.WorldAliasSettings; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.WorldAliasSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import org.bukkit.entity.Player; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/PlayerOnlineListener.java similarity index 75% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/PlayerOnlineListener.java index e1e4ef45b..da63b98d9 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/PlayerOnlineListener.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/PlayerOnlineListener.java @@ -14,24 +14,28 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners.bukkit; +package com.djrapitops.plan.gathering.listeners.bukkit; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.events.*; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.export.Exporter; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; import com.djrapitops.plan.extension.CallEvents; import com.djrapitops.plan.extension.ExtensionServiceImplementation; -import com.djrapitops.plan.system.cache.GeolocationCache; -import com.djrapitops.plan.system.cache.NicknameCache; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.processing.processors.Processors; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; -import com.djrapitops.plan.system.status.Status; +import com.djrapitops.plan.gathering.cache.GeolocationCache; +import com.djrapitops.plan.gathering.cache.NicknameCache; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.listeners.Status; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DataGatheringSettings; +import com.djrapitops.plan.settings.config.paths.ExportSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.events.*; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import org.bukkit.entity.Player; @@ -55,11 +59,11 @@ import java.util.UUID; public class PlayerOnlineListener implements Listener { private final PlanConfig config; - private final Processors processors; private final Processing processing; private final ServerInfo serverInfo; private final DBSystem dbSystem; private final ExtensionServiceImplementation extensionService; + private final Exporter exporter; private final GeolocationCache geolocationCache; private final NicknameCache nicknameCache; private final SessionCache sessionCache; @@ -69,11 +73,11 @@ public class PlayerOnlineListener implements Listener { @Inject public PlayerOnlineListener( PlanConfig config, - Processors processors, Processing processing, ServerInfo serverInfo, DBSystem dbSystem, ExtensionServiceImplementation extensionService, + Exporter exporter, GeolocationCache geolocationCache, NicknameCache nicknameCache, SessionCache sessionCache, @@ -81,11 +85,11 @@ public class PlayerOnlineListener implements Listener { ErrorHandler errorHandler ) { this.config = config; - this.processors = processors; this.processing = processing; this.serverInfo = serverInfo; this.dbSystem = dbSystem; this.extensionService = extensionService; + this.exporter = exporter; this.geolocationCache = geolocationCache; this.nicknameCache = nicknameCache; this.sessionCache = sessionCache; @@ -122,7 +126,7 @@ public class PlayerOnlineListener implements Listener { return; } UUID uuid = event.getPlayer().getUniqueId(); - if (AFKListener.AFK_TRACKER.isAfk(uuid)) { + if (BukkitAFKListener.AFK_TRACKER.isAfk(uuid)) { return; } @@ -147,8 +151,10 @@ public class PlayerOnlineListener implements Listener { UUID playerUUID = player.getUniqueId(); UUID serverUUID = serverInfo.getServerUUID(); long time = System.currentTimeMillis(); + JSONCache.invalidate(DataID.SERVER_OVERVIEW, serverUUID); + JSONCache.invalidate(DataID.GRAPH_PERFORMANCE, serverUUID); - AFKListener.AFK_TRACKER.performedAction(playerUUID, time); + BukkitAFKListener.AFK_TRACKER.performedAction(playerUUID, time); String world = player.getWorld().getName(); String gm = player.getGameMode().name(); @@ -169,7 +175,10 @@ public class PlayerOnlineListener implements Listener { } database.executeTransaction(new PlayerServerRegisterTransaction(playerUUID, player::getFirstPlayed, playerName, serverUUID)); - sessionCache.cacheSession(playerUUID, new Session(playerUUID, serverUUID, time, world, gm)) + Session session = new Session(playerUUID, serverUUID, time, world, gm); + session.putRawData(SessionKeys.NAME, playerName); + session.putRawData(SessionKeys.SERVER_NAME, serverInfo.getServer().getIdentifiableName()); + sessionCache.cacheSession(playerUUID, session) .ifPresent(previousSession -> database.executeTransaction(new SessionEndTransaction(previousSession))); database.executeTransaction(new NicknameStoreTransaction( @@ -177,8 +186,10 @@ public class PlayerOnlineListener implements Listener { (uuid, name) -> name.equals(nicknameCache.getDisplayName(uuid)) )); - processing.submitNonCritical(processors.info().playerPageUpdateProcessor(playerUUID)); processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_JOIN)); + if (config.get(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) { + processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName)); + } } @EventHandler(priority = EventPriority.NORMAL) @@ -201,9 +212,13 @@ public class PlayerOnlineListener implements Listener { private void actOnQuitEvent(PlayerQuitEvent event) { long time = System.currentTimeMillis(); Player player = event.getPlayer(); + String playerName = player.getName(); UUID playerUUID = player.getUniqueId(); + UUID serverUUID = serverInfo.getServerUUID(); + JSONCache.invalidate(DataID.SERVER_OVERVIEW, serverUUID); + JSONCache.invalidate(DataID.GRAPH_PERFORMANCE, serverUUID); - AFKListener.AFK_TRACKER.loggedOut(playerUUID, time); + BukkitAFKListener.AFK_TRACKER.loggedOut(playerUUID, time); nicknameCache.removeDisplayName(playerUUID); @@ -212,6 +227,8 @@ public class PlayerOnlineListener implements Listener { sessionCache.endSession(playerUUID, time) .ifPresent(endedSession -> dbSystem.getDatabase().executeTransaction(new SessionEndTransaction(endedSession))); - processing.submit(processors.info().playerPageUpdateProcessor(playerUUID)); + if (config.get(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) { + processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName)); + } } } diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/WorldChangeListener.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/WorldChangeListener.java similarity index 86% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/WorldChangeListener.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/WorldChangeListener.java index 2f8c89af0..8d3d0e520 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/WorldChangeListener.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/listeners/bukkit/WorldChangeListener.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners.bukkit; +package com.djrapitops.plan.gathering.listeners.bukkit; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.db.access.transactions.events.WorldNameStoreTransaction; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.WorldAliasSettings; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.WorldAliasSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.transactions.events.WorldNameStoreTransaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import org.bukkit.entity.Player; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/BukkitPingCounter.java similarity index 93% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/BukkitPingCounter.java index 3d98d62ba..93de12942 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PingCountTimerBukkit.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/BukkitPingCounter.java @@ -21,14 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.djrapitops.plan.system.tasks.bukkit; +package com.djrapitops.plan.gathering.timed; -import com.djrapitops.plan.data.store.objects.DateObj; -import com.djrapitops.plan.db.access.transactions.events.PingStoreTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.transactions.events.PingStoreTransaction; import com.djrapitops.plan.utilities.java.Reflection; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.task.AbsRunnable; @@ -60,7 +60,7 @@ import java.util.logging.Logger; * @author games647 */ @Singleton -public class PingCountTimerBukkit extends AbsRunnable implements Listener { +public class BukkitPingCounter extends AbsRunnable implements Listener { //the server is pinging the client every 40 Ticks (2 sec) - so check it then //https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/PlayerConnection.java#L178 @@ -112,7 +112,7 @@ public class PingCountTimerBukkit extends AbsRunnable implements Listener { private final RunnableFactory runnableFactory; @Inject - public PingCountTimerBukkit( + public BukkitPingCounter( PlanConfig config, DBSystem dbSystem, ServerInfo serverInfo, diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/BukkitTPSCountTimer.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/BukkitTPSCounter.java similarity index 90% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/BukkitTPSCountTimer.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/BukkitTPSCounter.java index fd1b1206f..cab47b560 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/BukkitTPSCountTimer.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/BukkitTPSCounter.java @@ -14,15 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks.bukkit; +package com.djrapitops.plan.gathering.timed; import com.djrapitops.plan.Plan; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.builders.TPSBuilder; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.tasks.TPSCountTimer; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.gathering.domain.builders.TPSBuilder; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.identification.properties.ServerProperties; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; import org.bukkit.World; @@ -32,14 +31,14 @@ import javax.inject.Singleton; import java.util.concurrent.TimeUnit; @Singleton -public class BukkitTPSCountTimer extends TPSCountTimer { +public class BukkitTPSCounter extends TPSCounter { protected final Plan plugin; private ServerProperties serverProperties; private long lastCheckNano; @Inject - public BukkitTPSCountTimer( + public BukkitTPSCounter( Plan plugin, DBSystem dbSystem, ServerInfo serverInfo, diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PaperTPSCountTimer.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/PaperTPSCounter.java similarity index 86% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PaperTPSCountTimer.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/PaperTPSCounter.java index d0092fa3a..f0aa956be 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/tasks/bukkit/PaperTPSCountTimer.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/gathering/timed/PaperTPSCounter.java @@ -14,23 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks.bukkit; +package com.djrapitops.plan.gathering.timed; import com.djrapitops.plan.Plan; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.builders.TPSBuilder; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.gathering.domain.builders.TPSBuilder; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; import org.bukkit.World; import javax.inject.Inject; -public class PaperTPSCountTimer extends BukkitTPSCountTimer { +public class PaperTPSCounter extends BukkitTPSCounter { @Inject - public PaperTPSCountTimer( + public PaperTPSCounter( Plan plugin, DBSystem dbSystem, ServerInfo serverInfo, diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/identification/properties/BukkitServerProperties.java similarity index 95% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/identification/properties/BukkitServerProperties.java index 09bb86b44..6002acb2f 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/info/server/properties/BukkitServerProperties.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/identification/properties/BukkitServerProperties.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server.properties; +package com.djrapitops.plan.identification.properties; import org.bukkit.Server; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitPlanModule.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitPlanModule.java index 0aa85c20c..f92f59260 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitPlanModule.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitPlanModule.java @@ -18,7 +18,7 @@ package com.djrapitops.plan.modules.bukkit; import com.djrapitops.plan.Plan; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.command.PlanCommand; +import com.djrapitops.plan.commands.PlanCommand; import com.djrapitops.plugin.command.CommandNode; import dagger.Binds; import dagger.Module; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitServerPropertiesModule.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitServerPropertiesModule.java index 60b5fa43a..a50e8fbdc 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitServerPropertiesModule.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitServerPropertiesModule.java @@ -17,8 +17,8 @@ package com.djrapitops.plan.modules.bukkit; import com.djrapitops.plan.Plan; -import com.djrapitops.plan.system.info.server.properties.BukkitServerProperties; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; +import com.djrapitops.plan.identification.properties.BukkitServerProperties; +import com.djrapitops.plan.identification.properties.ServerProperties; import dagger.Module; import dagger.Provides; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitSuperClassBindingModule.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitSuperClassBindingModule.java index cab6c3125..7e85b46ad 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitSuperClassBindingModule.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/modules/bukkit/BukkitSuperClassBindingModule.java @@ -17,19 +17,19 @@ package com.djrapitops.plan.modules.bukkit; import com.djrapitops.plan.BukkitServerShutdownSave; -import com.djrapitops.plan.ServerShutdownSave; -import com.djrapitops.plan.system.database.BukkitDBSystem; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.importing.BukkitImportSystem; -import com.djrapitops.plan.system.importing.ImportSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.info.server.ServerServerInfo; -import com.djrapitops.plan.system.listeners.BukkitListenerSystem; -import com.djrapitops.plan.system.listeners.ListenerSystem; -import com.djrapitops.plan.system.settings.BukkitConfigSystem; -import com.djrapitops.plan.system.settings.ConfigSystem; -import com.djrapitops.plan.system.tasks.BukkitTaskSystem; -import com.djrapitops.plan.system.tasks.TaskSystem; +import com.djrapitops.plan.BukkitTaskSystem; +import com.djrapitops.plan.TaskSystem; +import com.djrapitops.plan.gathering.ServerShutdownSave; +import com.djrapitops.plan.gathering.importing.BukkitImportSystem; +import com.djrapitops.plan.gathering.importing.ImportSystem; +import com.djrapitops.plan.gathering.listeners.BukkitListenerSystem; +import com.djrapitops.plan.gathering.listeners.ListenerSystem; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.identification.ServerServerInfo; +import com.djrapitops.plan.settings.BukkitConfigSystem; +import com.djrapitops.plan.settings.ConfigSystem; +import com.djrapitops.plan.storage.database.BukkitDBSystem; +import com.djrapitops.plan.storage.database.DBSystem; import dagger.Binds; import dagger.Module; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/database/BukkitDBSystem.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/storage/database/BukkitDBSystem.java similarity index 82% rename from Plan/bukkit/src/main/java/com/djrapitops/plan/system/database/BukkitDBSystem.java rename to Plan/bukkit/src/main/java/com/djrapitops/plan/storage/database/BukkitDBSystem.java index 428a5916f..25eeb035e 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/database/BukkitDBSystem.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/storage/database/BukkitDBSystem.java @@ -14,15 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.database; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.db.H2DB; -import com.djrapitops.plan.db.MySQLDB; -import com.djrapitops.plan.db.SQLiteDB; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DatabaseSettings; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DatabaseSettings; +import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plugin.benchmarking.Timings; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/CommandListener.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/CommandListener.java deleted file mode 100644 index fe0931511..000000000 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/system/listeners/bukkit/CommandListener.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.listeners.bukkit; - -import com.djrapitops.plan.Plan; -import com.djrapitops.plan.db.access.transactions.events.CommandStoreTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.error.ErrorHandler; -import org.bukkit.command.Command; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerCommandPreprocessEvent; - -import javax.inject.Inject; - -/** - * Event Listener for PlayerCommandPreprocessEvents. - * - * @author Rsl1122 - */ -public class CommandListener implements Listener { - - private final Plan plugin; - private final PlanConfig config; - private final ServerInfo serverInfo; - private final DBSystem dbSystem; - private final ErrorHandler errorHandler; - - @Inject - public CommandListener( - Plan plugin, - PlanConfig config, - ServerInfo serverInfo, - DBSystem dbSystem, - ErrorHandler errorHandler - ) { - this.plugin = plugin; - this.config = config; - this.serverInfo = serverInfo; - this.dbSystem = dbSystem; - this.errorHandler = errorHandler; - } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerCommand(PlayerCommandPreprocessEvent event) { - boolean hasIgnorePermission = event.getPlayer().hasPermission(Permissions.IGNORE_COMMAND_USE.getPermission()); - if (event.isCancelled() || hasIgnorePermission) { - return; - } - - try { - actOnCommandEvent(event); - } catch (Exception e) { - errorHandler.log(L.ERROR, this.getClass(), e); - } - } - - private void actOnCommandEvent(PlayerCommandPreprocessEvent event) { - String commandName = event.getMessage().substring(1).split(" ")[0].toLowerCase(); - - boolean logUnknownCommands = config.isTrue(DataGatheringSettings.LOG_UNKNOWN_COMMANDS); - boolean combineCommandAliases = config.isTrue(DataGatheringSettings.COMBINE_COMMAND_ALIASES); - - if (!logUnknownCommands || combineCommandAliases) { - Command command = getBukkitCommand(commandName); - if (command == null) { - if (!logUnknownCommands) { - return; - } - } else if (combineCommandAliases) { - commandName = command.getName(); - } - } - dbSystem.getDatabase().executeTransaction(new CommandStoreTransaction(serverInfo.getServerUUID(), commandName)); - } - - private Command getBukkitCommand(String commandName) { - Command command = plugin.getServer().getPluginCommand(commandName); - if (command == null) { - try { - command = plugin.getServer().getCommandMap().getCommand(commandName); - } catch (NoSuchMethodError ignored) { - /* Ignored, Bukkit 1.8 has no such method. This method is from Paper */ - } - } - return command; - } -} diff --git a/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java b/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java index 9d9489f40..348d07094 100644 --- a/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java +++ b/Plan/bukkit/src/test/java/com/djrapitops/plan/BukkitSystemTest.java @@ -16,21 +16,19 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.system.PlanSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.settings.ConfigSettingKeyTest; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.WebserverSettings; -import com.djrapitops.plan.system.settings.paths.key.Setting; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.settings.ConfigSettingKeyTest; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.WebserverSettings; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -import utilities.OptionalAssert; import utilities.RandomData; import utilities.mocks.BukkitMockComponent; @@ -38,6 +36,7 @@ import java.nio.file.Path; import java.util.Collection; import java.util.Optional; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -78,7 +77,7 @@ public class BukkitSystemTest { Optional found = database.query(ServerQueries.fetchServerMatchingIdentifier(system.getServerInfo().getServerUUID())) .map(Server::getWebAddress); - OptionalAssert.equals(expectedAddress, found); + assertEquals(expectedAddress, found.orElse(null)); } finally { system.disable(); } diff --git a/Plan/bukkit/src/test/java/com/djrapitops/plan/system/listeners/AFKListenerTest.java b/Plan/bukkit/src/test/java/com/djrapitops/plan/gathering/listeners/BukkitAFKListenerTest.java similarity index 86% rename from Plan/bukkit/src/test/java/com/djrapitops/plan/system/listeners/AFKListenerTest.java rename to Plan/bukkit/src/test/java/com/djrapitops/plan/gathering/listeners/BukkitAFKListenerTest.java index 006f79796..5357c2bc8 100644 --- a/Plan/bukkit/src/test/java/com/djrapitops/plan/system/listeners/AFKListenerTest.java +++ b/Plan/bukkit/src/test/java/com/djrapitops/plan/gathering/listeners/BukkitAFKListenerTest.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners; +package com.djrapitops.plan.gathering.listeners; -import com.djrapitops.plan.system.listeners.bukkit.AFKListener; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.gathering.listeners.bukkit.BukkitAFKListener; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; import com.djrapitops.plugin.logging.console.TestPluginLogger; import com.djrapitops.plugin.logging.error.ConsoleErrorLogger; import org.bukkit.entity.Player; @@ -37,21 +37,21 @@ import java.util.concurrent.TimeUnit; import static org.mockito.Mockito.*; /** - * Test for {@link AFKListener} + * Test for {@link BukkitAFKListener} * * @author Rsl1122 */ @RunWith(JUnitPlatform.class) @ExtendWith(MockitoExtension.class) -public class AFKListenerTest { +public class BukkitAFKListenerTest { - private static AFKListener underTest; + private static BukkitAFKListener underTest; @BeforeAll static void setUp() { PlanConfig config = Mockito.mock(PlanConfig.class); when(config.get(TimeSettings.AFK_THRESHOLD)).thenReturn(TimeUnit.MINUTES.toMillis(3)); - underTest = new AFKListener(config, new ConsoleErrorLogger(new TestPluginLogger())); + underTest = new BukkitAFKListener(config, new ConsoleErrorLogger(new TestPluginLogger())); } @Test diff --git a/Plan/bukkit/src/test/java/utilities/mocks/BukkitMockComponent.java b/Plan/bukkit/src/test/java/utilities/mocks/BukkitMockComponent.java index bf4419161..199542aac 100644 --- a/Plan/bukkit/src/test/java/utilities/mocks/BukkitMockComponent.java +++ b/Plan/bukkit/src/test/java/utilities/mocks/BukkitMockComponent.java @@ -19,7 +19,7 @@ package utilities.mocks; import com.djrapitops.plan.DaggerPlanBukkitComponent; import com.djrapitops.plan.Plan; import com.djrapitops.plan.PlanBukkitComponent; -import com.djrapitops.plan.system.PlanSystem; +import com.djrapitops.plan.PlanSystem; import java.nio.file.Path; diff --git a/Plan/bukkit/src/test/java/utilities/mocks/PlanBukkitMocker.java b/Plan/bukkit/src/test/java/utilities/mocks/PlanBukkitMocker.java index a40faa1d2..67e6c8f0b 100644 --- a/Plan/bukkit/src/test/java/utilities/mocks/PlanBukkitMocker.java +++ b/Plan/bukkit/src/test/java/utilities/mocks/PlanBukkitMocker.java @@ -38,9 +38,9 @@ import utilities.mocks.objects.TestLogger; import utilities.mocks.objects.TestRunnableFactory; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import static org.mockito.Mockito.doReturn; @@ -90,11 +90,10 @@ public class PlanBukkitMocker extends Mocker { } PlanBukkitMocker withPluginDescription() { - try { - File pluginYml = getFile("/plugin.yml"); - PluginDescriptionFile description = new PluginDescriptionFile(new FileInputStream(pluginYml)); + try (InputStream in = Files.newInputStream(getFile("/plugin.yml").toPath())) { + PluginDescriptionFile description = new PluginDescriptionFile(in); doReturn(description).when(planMock).getDescription(); - } catch (FileNotFoundException | InvalidDescriptionException e) { + } catch (IOException | InvalidDescriptionException e) { System.out.println("Error while setting plugin description"); } return this; diff --git a/Plan/bungeecord/build.gradle b/Plan/bungeecord/build.gradle index 8c5548cc6..deeb42ad8 100644 --- a/Plan/bungeecord/build.gradle +++ b/Plan/bungeecord/build.gradle @@ -1,5 +1,6 @@ dependencies { compile project(path: ":common", configuration: 'shadow') + compileOnly project(":api") compile "com.djrapitops:AbstractPluginFramework-bungeecord:$abstractPluginFrameworkVersion" compile "org.bstats:bstats-bungeecord:$bstatsVersion" diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/BStatsBungee.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/BStatsBungee.java index 52d524a72..3662706f3 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/BStatsBungee.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/BStatsBungee.java @@ -16,24 +16,22 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; import org.bstats.bungeecord.Metrics; -import java.io.Serializable; +import java.util.function.Supplier; public class BStatsBungee { private final PlanBungee plugin; private final Database database; - private final ConnectionSystem connectionSystem; private Metrics metrics; - public BStatsBungee(PlanBungee plugin, Database database, ConnectionSystem connectionSystem) { + public BStatsBungee(PlanBungee plugin, Database database) { this.plugin = plugin; this.database = database; - this.connectionSystem = connectionSystem; } public void registerMetrics() { @@ -44,15 +42,12 @@ public class BStatsBungee { } private void registerConfigSettingGraphs() { - String serverType = plugin.getProxy().getName(); - String databaseType = database.getType().getName(); - - addStringSettingPie("server_type", serverType); - addStringSettingPie("database_type", databaseType); - addStringSettingPie("network_servers", connectionSystem.getDataServers().size()); + addStringSettingPie("server_type", () -> plugin.getProxy().getName()); + addStringSettingPie("database_type", () -> database.getType().getName()); + addStringSettingPie("network_servers", () -> String.valueOf(database.query(ServerQueries.fetchPlanServerInformationCollection()).size())); } - protected void addStringSettingPie(String id, Serializable setting) { - metrics.addCustomChart(new Metrics.SimplePie(id, setting::toString)); + protected void addStringSettingPie(String id, Supplier setting) { + metrics.addCustomChart(new Metrics.SimplePie(id, setting::get)); } } diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/BungeeTaskSystem.java similarity index 66% rename from Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java rename to Plan/bungeecord/src/main/java/com/djrapitops/plan/BungeeTaskSystem.java index dca6baaa5..63c4582d1 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/BungeeTaskSystem.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/BungeeTaskSystem.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks; +package com.djrapitops.plan; -import com.djrapitops.plan.PlanBungee; -import com.djrapitops.plan.db.tasks.DBCleanTask; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.system.tasks.bungee.BungeeTPSCountTimer; -import com.djrapitops.plan.system.tasks.bungee.PingCountTimerBungee; -import com.djrapitops.plan.system.tasks.proxy.NetworkConfigStoreTask; -import com.djrapitops.plan.system.tasks.proxy.NetworkPageRefreshTask; +import com.djrapitops.plan.gathering.timed.BungeePingCounter; +import com.djrapitops.plan.gathering.timed.BungeeTPSCounter; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DataGatheringSettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.upkeep.NetworkConfigStoreTask; +import com.djrapitops.plan.storage.upkeep.DBCleanTask; +import com.djrapitops.plan.storage.upkeep.LogsFolderCleanTask; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.task.RunnableFactory; @@ -43,12 +43,12 @@ public class BungeeTaskSystem extends TaskSystem { private final PlanBungee plugin; private final PlanConfig config; - private final NetworkPageRefreshTask networkPageRefreshTask; - private final PingCountTimerBungee pingCountTimer; + private final BungeeTPSCounter tpsCounter; + private final BungeePingCounter pingCounter; private final LogsFolderCleanTask logsFolderCleanTask; - private final PlayersPageRefreshTask playersPageRefreshTask; private final NetworkConfigStoreTask networkConfigStoreTask; private final DBCleanTask dbCleanTask; + private final JSONCache.CleanTask jsonCacheCleanTask; private final ExtensionServerMethodCallerTask extensionServerMethodCallerTask; @Inject @@ -56,25 +56,24 @@ public class BungeeTaskSystem extends TaskSystem { PlanBungee plugin, PlanConfig config, RunnableFactory runnableFactory, - BungeeTPSCountTimer bungeeTPSCountTimer, - NetworkPageRefreshTask networkPageRefreshTask, - PingCountTimerBungee pingCountTimer, + BungeeTPSCounter tpsCounter, + BungeePingCounter pingCounter, LogsFolderCleanTask logsFolderCleanTask, - PlayersPageRefreshTask playersPageRefreshTask, NetworkConfigStoreTask networkConfigStoreTask, DBCleanTask dbCleanTask, + JSONCache.CleanTask jsonCacheCleanTask, ExtensionServerMethodCallerTask extensionServerMethodCallerTask ) { - super(runnableFactory, bungeeTPSCountTimer); + super(runnableFactory); this.plugin = plugin; this.config = config; + this.tpsCounter = tpsCounter; - this.networkPageRefreshTask = networkPageRefreshTask; - this.pingCountTimer = pingCountTimer; + this.pingCounter = pingCounter; this.logsFolderCleanTask = logsFolderCleanTask; - this.playersPageRefreshTask = playersPageRefreshTask; this.networkConfigStoreTask = networkConfigStoreTask; this.dbCleanTask = dbCleanTask; + this.jsonCacheCleanTask = jsonCacheCleanTask; this.extensionServerMethodCallerTask = extensionServerMethodCallerTask; } @@ -84,20 +83,16 @@ public class BungeeTaskSystem extends TaskSystem { } private void registerTasks() { - registerTask(tpsCountTimer).runTaskTimerAsynchronously(1000, TimeAmount.toTicks(1L, TimeUnit.SECONDS)); - registerTask(networkPageRefreshTask).runTaskTimerAsynchronously(1500, TimeAmount.toTicks(5L, TimeUnit.MINUTES)); + registerTask(tpsCounter).runTaskTimerAsynchronously(1000, TimeAmount.toTicks(1L, TimeUnit.SECONDS)); registerTask(logsFolderCleanTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS)); Long pingDelay = config.get(TimeSettings.PING_SERVER_ENABLE_DELAY); if (pingDelay < TimeUnit.HOURS.toMillis(1L) && config.get(DataGatheringSettings.PING)) { - plugin.registerListener(pingCountTimer); + plugin.registerListener(pingCounter); long startDelay = TimeAmount.toTicks(pingDelay, TimeUnit.MILLISECONDS); - registerTask(pingCountTimer).runTaskTimer(startDelay, 40L); + registerTask(pingCounter).runTaskTimer(startDelay, 40L); } - registerTask(playersPageRefreshTask) - .runTaskTimerAsynchronously(TimeAmount.toTicks(5L, TimeUnit.MINUTES), TimeAmount.toTicks(5L, TimeUnit.MINUTES)); - // +40 ticks / 2 seconds so that update check task runs first. long storeDelay = TimeAmount.toTicks(config.get(TimeSettings.CONFIG_UPDATE_INTERVAL), TimeUnit.MILLISECONDS) + 40; registerTask(networkConfigStoreTask).runTaskLaterAsynchronously(storeDelay); @@ -106,6 +101,8 @@ public class BungeeTaskSystem extends TaskSystem { TimeAmount.toTicks(20, TimeUnit.SECONDS), TimeAmount.toTicks(config.get(TimeSettings.CLEAN_DATABASE_PERIOD), TimeUnit.MILLISECONDS) ); + long minute = TimeAmount.toTicks(1, TimeUnit.MINUTES); + registerTask(jsonCacheCleanTask).runTaskTimerAsynchronously(minute, minute); long extensionRefreshPeriod = TimeAmount.toTicks(config.get(TimeSettings.EXTENSION_DATA_REFRESH_PERIOD), TimeUnit.MILLISECONDS); registerTask(extensionServerMethodCallerTask).runTaskTimerAsynchronously( diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungee.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungee.java index 8972a7d3e..1fdefd1cc 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungee.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungee.java @@ -16,12 +16,11 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.command.PlanProxyCommand; -import com.djrapitops.plan.system.PlanSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.settings.theme.PlanColorScheme; +import com.djrapitops.plan.commands.PlanProxyCommand; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; +import com.djrapitops.plan.settings.theme.PlanColorScheme; import com.djrapitops.plugin.BungeePlugin; import com.djrapitops.plugin.command.ColorScheme; import com.djrapitops.plugin.logging.L; @@ -48,8 +47,7 @@ public class PlanBungee extends BungeePlugin implements PlanPlugin { new BStatsBungee( this, - system.getDatabaseSystem().getDatabase(), - system.getInfoSystem().getConnectionSystem() + system.getDatabaseSystem().getDatabase() ).registerMetrics(); logger.info(locale.getString(PluginLang.ENABLED)); diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungeeComponent.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungeeComponent.java index 9fba65e39..c0bcb8a75 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungeeComponent.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungeeComponent.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.command.PlanProxyCommand; +import com.djrapitops.plan.commands.PlanProxyCommand; import com.djrapitops.plan.modules.APFModule; import com.djrapitops.plan.modules.FilesModule; import com.djrapitops.plan.modules.ProxySuperClassBindingModule; @@ -25,8 +25,6 @@ import com.djrapitops.plan.modules.bungee.BungeeCommandModule; import com.djrapitops.plan.modules.bungee.BungeePlanModule; import com.djrapitops.plan.modules.bungee.BungeeServerPropertiesModule; import com.djrapitops.plan.modules.bungee.BungeeSuperClassBindingModule; -import com.djrapitops.plan.system.PlanSystem; -import com.djrapitops.pluginbridge.plan.PluginBridgeModule; import dagger.BindsInstance; import dagger.Component; @@ -46,8 +44,7 @@ import javax.inject.Singleton; FilesModule.class, ProxySuperClassBindingModule.class, BungeeSuperClassBindingModule.class, - BungeeServerPropertiesModule.class, - PluginBridgeModule.Bungee.class + BungeeServerPropertiesModule.class }) public interface PlanBungeeComponent { diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/listeners/BungeeListenerSystem.java similarity index 93% rename from Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java rename to Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/listeners/BungeeListenerSystem.java index 722ed46dd..bb3aba4b9 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/BungeeListenerSystem.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/listeners/BungeeListenerSystem.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners; +package com.djrapitops.plan.gathering.listeners; import com.djrapitops.plan.PlanBungee; import com.djrapitops.plan.PlanPlugin; import com.djrapitops.plan.api.events.PlanBungeeEnableEvent; import com.djrapitops.plan.capability.CapabilityServiceImplementation; -import com.djrapitops.plan.system.listeners.bungee.PlayerOnlineListener; +import com.djrapitops.plan.gathering.listeners.bungee.PlayerOnlineListener; import javax.inject.Inject; diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/listeners/bungee/PlayerOnlineListener.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/listeners/bungee/PlayerOnlineListener.java new file mode 100644 index 000000000..537b65c89 --- /dev/null +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/listeners/bungee/PlayerOnlineListener.java @@ -0,0 +1,203 @@ +/* + * 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 . + */ +package com.djrapitops.plan.gathering.listeners.bungee; + +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.export.Exporter; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.extension.CallEvents; +import com.djrapitops.plan.extension.ExtensionServiceImplementation; +import com.djrapitops.plan.gathering.cache.GeolocationCache; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DataGatheringSettings; +import com.djrapitops.plan.settings.config.paths.ExportSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.events.GeoInfoStoreTransaction; +import com.djrapitops.plan.storage.database.transactions.events.PlayerRegisterTransaction; +import com.djrapitops.plugin.logging.L; +import com.djrapitops.plugin.logging.error.ErrorHandler; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.PostLoginEvent; +import net.md_5.bungee.api.event.ServerSwitchEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.event.EventPriority; + +import javax.inject.Inject; +import java.net.InetAddress; +import java.util.UUID; + +/** + * Player Join listener for Bungee. + * + * @author Rsl1122 + */ +public class PlayerOnlineListener implements Listener { + + private final PlanConfig config; + private final Processing processing; + private final DBSystem dbSystem; + private final ExtensionServiceImplementation extensionService; + private final Exporter exporter; + private final GeolocationCache geolocationCache; + private final SessionCache sessionCache; + private final ServerInfo serverInfo; + private final ErrorHandler errorHandler; + + @Inject + public PlayerOnlineListener( + PlanConfig config, + Processing processing, + DBSystem dbSystem, + ExtensionServiceImplementation extensionService, + Exporter exporter, GeolocationCache geolocationCache, + SessionCache sessionCache, + ServerInfo serverInfo, + ErrorHandler errorHandler + ) { + this.config = config; + this.processing = processing; + this.dbSystem = dbSystem; + this.extensionService = extensionService; + this.exporter = exporter; + this.geolocationCache = geolocationCache; + this.sessionCache = sessionCache; + this.serverInfo = serverInfo; + this.errorHandler = errorHandler; + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPostLogin(PostLoginEvent event) { + try { + actOnLogin(event); + } catch (Exception e) { + errorHandler.log(L.WARN, this.getClass(), e); + } + } + + private void actOnLogin(PostLoginEvent event) { + ProxiedPlayer player = event.getPlayer(); + UUID playerUUID = player.getUniqueId(); + String playerName = player.getName(); + InetAddress address = player.getAddress().getAddress(); + long time = System.currentTimeMillis(); + + Session session = new Session(playerUUID, serverInfo.getServerUUID(), time, null, null); + session.putRawData(SessionKeys.NAME, playerName); + session.putRawData(SessionKeys.SERVER_NAME, "Proxy Server"); + sessionCache.cacheSession(playerUUID, session); + Database database = dbSystem.getDatabase(); + + boolean gatheringGeolocations = config.isTrue(DataGatheringSettings.GEOLOCATIONS); + if (gatheringGeolocations) { + database.executeTransaction( + new GeoInfoStoreTransaction(playerUUID, address, time, geolocationCache::getCountry) + ); + } + + database.executeTransaction(new PlayerRegisterTransaction(playerUUID, () -> time, playerName)); + processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_JOIN)); + if (config.get(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) { + processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName)); + } + + UUID serverUUID = serverInfo.getServerUUID(); + JSONCache.invalidateMatching(DataID.SERVER_OVERVIEW); + JSONCache.invalidate(DataID.GRAPH_ONLINE, serverUUID); + JSONCache.invalidate(DataID.SERVERS); + JSONCache.invalidate(DataID.SESSIONS); + } + + @EventHandler(priority = EventPriority.NORMAL) + public void beforeLogout(PlayerDisconnectEvent event) { + ProxiedPlayer player = event.getPlayer(); + UUID playerUUID = player.getUniqueId(); + String playerName = player.getName(); + processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_LEAVE)); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onLogout(PlayerDisconnectEvent event) { + try { + actOnLogout(event); + } catch (Exception e) { + errorHandler.log(L.WARN, this.getClass(), e); + } + } + + private void actOnLogout(PlayerDisconnectEvent event) { + ProxiedPlayer player = event.getPlayer(); + String playerName = player.getName(); + UUID playerUUID = player.getUniqueId(); + + sessionCache.endSession(playerUUID, System.currentTimeMillis()); + if (config.get(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) { + processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName)); + } + processing.submit(() -> { + JSONCache.invalidateMatching( + DataID.SERVER_OVERVIEW, + DataID.SESSIONS, + DataID.GRAPH_WORLD_PIE, + DataID.GRAPH_PUNCHCARD, + DataID.KILLS, + DataID.ONLINE_OVERVIEW, + DataID.SESSIONS_OVERVIEW, + DataID.PVP_PVE, + DataID.GRAPH_UNIQUE_NEW, + DataID.GRAPH_CALENDAR + ); + UUID serverUUID = serverInfo.getServerUUID(); + JSONCache.invalidate(DataID.GRAPH_ONLINE, serverUUID); + JSONCache.invalidate(DataID.SERVERS); + }); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onServerSwitch(ServerSwitchEvent event) { + try { + actOnServerSwitch(event); + } catch (Exception e) { + errorHandler.log(L.WARN, this.getClass(), e); + } + } + + private void actOnServerSwitch(ServerSwitchEvent event) { + ProxiedPlayer player = event.getPlayer(); + String playerName = player.getName(); + UUID playerUUID = player.getUniqueId(); + + long time = System.currentTimeMillis(); + // Replaces the current session in the cache. + Session session = new Session(playerUUID, serverInfo.getServerUUID(), time, null, null); + session.putRawData(SessionKeys.NAME, playerName); + session.putRawData(SessionKeys.SERVER_NAME, "Proxy Server"); + sessionCache.cacheSession(playerUUID, session); + if (config.get(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) { + processing.submitNonCritical(() -> exporter.exportPlayerPage(playerUUID, playerName)); + } + + JSONCache.invalidate(DataID.SERVERS); + } +} diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/timed/BungeePingCounter.java similarity index 89% rename from Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java rename to Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/timed/BungeePingCounter.java index b9245fb62..d0d23c9d1 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/PingCountTimerBungee.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/timed/BungeePingCounter.java @@ -21,14 +21,14 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.djrapitops.plan.system.tasks.bungee; +package com.djrapitops.plan.gathering.timed; -import com.djrapitops.plan.data.store.objects.DateObj; -import com.djrapitops.plan.db.access.transactions.events.PingStoreTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.transactions.events.PingStoreTransaction; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.task.AbsRunnable; import com.djrapitops.plugin.task.RunnableFactory; @@ -50,7 +50,7 @@ import java.util.concurrent.TimeUnit; * @author BrainStone */ @Singleton -public class PingCountTimerBungee extends AbsRunnable implements Listener { +public class BungeePingCounter extends AbsRunnable implements Listener { private final Map>> playerHistory; @@ -60,7 +60,7 @@ public class PingCountTimerBungee extends AbsRunnable implements Listener { private final RunnableFactory runnableFactory; @Inject - public PingCountTimerBungee( + public BungeePingCounter( PlanConfig config, DBSystem dbSystem, ServerInfo serverInfo, diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/BungeeTPSCountTimer.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/timed/BungeeTPSCounter.java similarity index 78% rename from Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/BungeeTPSCountTimer.java rename to Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/timed/BungeeTPSCounter.java index 57f249b3e..53f774e31 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/tasks/bungee/BungeeTPSCountTimer.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/gathering/timed/BungeeTPSCounter.java @@ -14,14 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks.bungee; +package com.djrapitops.plan.gathering.timed; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.builders.TPSBuilder; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.tasks.TPSCountTimer; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.gathering.domain.builders.TPSBuilder; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.identification.properties.ServerProperties; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -29,12 +28,12 @@ import javax.inject.Inject; import javax.inject.Singleton; @Singleton -public class BungeeTPSCountTimer extends TPSCountTimer { +public class BungeeTPSCounter extends TPSCounter { private final ServerProperties serverProperties; @Inject - public BungeeTPSCountTimer( + public BungeeTPSCounter( DBSystem dbSystem, ServerInfo serverInfo, ServerProperties serverProperties, diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/BungeeServerInfo.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/BungeeServerInfo.java similarity index 87% rename from Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/BungeeServerInfo.java rename to Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/BungeeServerInfo.java index dfa5627b4..45bde58a5 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/BungeeServerInfo.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/BungeeServerInfo.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server; +package com.djrapitops.plan.identification; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.transactions.StoreServerInformationTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.webserver.WebServer; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.properties.ServerProperties; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction; import com.djrapitops.plugin.logging.console.PluginLogger; import dagger.Lazy; diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/BungeeServerProperties.java similarity index 88% rename from Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java rename to Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/BungeeServerProperties.java index 424ac8494..25243b5ce 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/BungeeServerProperties.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/BungeeServerProperties.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server.properties; +package com.djrapitops.plan.identification.properties; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ProxySettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.ProxySettings; import net.md_5.bungee.api.ProxyServer; /** diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/RedisCheck.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/RedisCheck.java similarity index 94% rename from Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/RedisCheck.java rename to Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/RedisCheck.java index b2aafd857..00adf930b 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/RedisCheck.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/RedisCheck.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server.properties; +package com.djrapitops.plan.identification.properties; import com.djrapitops.plugin.api.Check; diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/RedisPlayersOnlineSupplier.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/RedisPlayersOnlineSupplier.java similarity index 94% rename from Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/RedisPlayersOnlineSupplier.java rename to Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/RedisPlayersOnlineSupplier.java index bfbc2898d..e7266785b 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/info/server/properties/RedisPlayersOnlineSupplier.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/identification/properties/RedisPlayersOnlineSupplier.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server.properties; +package com.djrapitops.plan.identification.properties; import com.imaginarycode.minecraft.redisbungee.RedisBungee; diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeePlanModule.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeePlanModule.java index 39b041797..05c5f2f84 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeePlanModule.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeePlanModule.java @@ -18,7 +18,7 @@ package com.djrapitops.plan.modules.bungee; import com.djrapitops.plan.PlanBungee; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.command.PlanProxyCommand; +import com.djrapitops.plan.commands.PlanProxyCommand; import com.djrapitops.plugin.command.CommandNode; import dagger.Binds; import dagger.Module; diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeServerPropertiesModule.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeServerPropertiesModule.java index 222e7838b..ed9b480d5 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeServerPropertiesModule.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeServerPropertiesModule.java @@ -17,9 +17,9 @@ package com.djrapitops.plan.modules.bungee; import com.djrapitops.plan.PlanBungee; -import com.djrapitops.plan.system.info.server.properties.BungeeServerProperties; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.identification.properties.BungeeServerProperties; +import com.djrapitops.plan.identification.properties.ServerProperties; +import com.djrapitops.plan.settings.config.PlanConfig; import dagger.Module; import dagger.Provides; diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeSuperClassBindingModule.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeSuperClassBindingModule.java index 6727fd74b..d0dbaac53 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeSuperClassBindingModule.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/modules/bungee/BungeeSuperClassBindingModule.java @@ -16,12 +16,12 @@ */ package com.djrapitops.plan.modules.bungee; -import com.djrapitops.plan.system.info.server.BungeeServerInfo; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.listeners.BungeeListenerSystem; -import com.djrapitops.plan.system.listeners.ListenerSystem; -import com.djrapitops.plan.system.tasks.BungeeTaskSystem; -import com.djrapitops.plan.system.tasks.TaskSystem; +import com.djrapitops.plan.BungeeTaskSystem; +import com.djrapitops.plan.TaskSystem; +import com.djrapitops.plan.gathering.listeners.BungeeListenerSystem; +import com.djrapitops.plan.gathering.listeners.ListenerSystem; +import com.djrapitops.plan.identification.BungeeServerInfo; +import com.djrapitops.plan.identification.ServerInfo; import dagger.Binds; import dagger.Module; diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java deleted file mode 100644 index 6b7863b98..000000000 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/system/listeners/bungee/PlayerOnlineListener.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.listeners.bungee; - -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.events.GeoInfoStoreTransaction; -import com.djrapitops.plan.db.access.transactions.events.PlayerRegisterTransaction; -import com.djrapitops.plan.extension.CallEvents; -import com.djrapitops.plan.extension.ExtensionServiceImplementation; -import com.djrapitops.plan.system.cache.GeolocationCache; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.processing.processors.Processors; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.error.ErrorHandler; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.api.event.ServerSwitchEvent; -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.event.EventHandler; -import net.md_5.bungee.event.EventPriority; - -import javax.inject.Inject; -import java.net.InetAddress; -import java.util.UUID; - -/** - * Player Join listener for Bungee. - * - * @author Rsl1122 - */ -public class PlayerOnlineListener implements Listener { - - private final PlanConfig config; - private final Processors processors; - private final Processing processing; - private final DBSystem dbSystem; - private final ExtensionServiceImplementation extensionService; - private final GeolocationCache geolocationCache; - private final SessionCache sessionCache; - private final ServerInfo serverInfo; - private final ErrorHandler errorHandler; - - @Inject - public PlayerOnlineListener( - PlanConfig config, - Processors processors, - Processing processing, - DBSystem dbSystem, - ExtensionServiceImplementation extensionService, - GeolocationCache geolocationCache, - SessionCache sessionCache, - ServerInfo serverInfo, - ErrorHandler errorHandler - ) { - this.config = config; - this.processors = processors; - this.processing = processing; - this.dbSystem = dbSystem; - this.extensionService = extensionService; - this.geolocationCache = geolocationCache; - this.sessionCache = sessionCache; - this.serverInfo = serverInfo; - this.errorHandler = errorHandler; - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPostLogin(PostLoginEvent event) { - try { - ProxiedPlayer player = event.getPlayer(); - UUID playerUUID = player.getUniqueId(); - String playerName = player.getName(); - InetAddress address = player.getAddress().getAddress(); - long time = System.currentTimeMillis(); - - sessionCache.cacheSession(playerUUID, new Session(playerUUID, serverInfo.getServerUUID(), time, null, null)); - Database database = dbSystem.getDatabase(); - - boolean gatheringGeolocations = config.isTrue(DataGatheringSettings.GEOLOCATIONS); - if (gatheringGeolocations) { - database.executeTransaction( - new GeoInfoStoreTransaction(playerUUID, address, time, geolocationCache::getCountry) - ); - } - - database.executeTransaction(new PlayerRegisterTransaction(playerUUID, () -> time, playerName)); - processing.submit(processors.info().playerPageUpdateProcessor(playerUUID)); - processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_JOIN)); - ResponseCache.clearResponse(PageId.SERVER.of(serverInfo.getServerUUID())); - } catch (Exception e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - - @EventHandler(priority = EventPriority.NORMAL) - public void beforeLogout(PlayerDisconnectEvent event) { - ProxiedPlayer player = event.getPlayer(); - UUID playerUUID = player.getUniqueId(); - String playerName = player.getName(); - processing.submitNonCritical(() -> extensionService.updatePlayerValues(playerUUID, playerName, CallEvents.PLAYER_LEAVE)); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onLogout(PlayerDisconnectEvent event) { - try { - ProxiedPlayer player = event.getPlayer(); - UUID playerUUID = player.getUniqueId(); - - sessionCache.endSession(playerUUID, System.currentTimeMillis()); - processing.submit(processors.info().playerPageUpdateProcessor(playerUUID)); - ResponseCache.clearResponse(PageId.SERVER.of(serverInfo.getServerUUID())); - } catch (Exception e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onServerSwitch(ServerSwitchEvent event) { - try { - ProxiedPlayer player = event.getPlayer(); - UUID playerUUID = player.getUniqueId(); - - long time = System.currentTimeMillis(); - // Replaces the current session in the cache. - sessionCache.cacheSession(playerUUID, new Session(playerUUID, serverInfo.getServerUUID(), time, null, null)); - processing.submit(processors.info().playerPageUpdateProcessor(playerUUID)); - } catch (Exception e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } -} diff --git a/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java b/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java index c7a796ee5..40e861fec 100644 --- a/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java +++ b/Plan/bungeecord/src/test/java/com/djrapitops/plan/BungeeSystemTest.java @@ -16,13 +16,12 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.db.SQLiteDB; -import com.djrapitops.plan.system.PlanSystem; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ProxySettings; -import com.djrapitops.plan.system.settings.paths.WebserverSettings; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.ProxySettings; +import com.djrapitops.plan.settings.config.paths.WebserverSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.SQLiteDB; import com.google.common.util.concurrent.MoreExecutors; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/Plan/bungeecord/src/test/java/utilities/mocks/BungeeMockComponent.java b/Plan/bungeecord/src/test/java/utilities/mocks/BungeeMockComponent.java index 1cd2b74f4..9ceef0ad9 100644 --- a/Plan/bungeecord/src/test/java/utilities/mocks/BungeeMockComponent.java +++ b/Plan/bungeecord/src/test/java/utilities/mocks/BungeeMockComponent.java @@ -19,7 +19,7 @@ package utilities.mocks; import com.djrapitops.plan.DaggerPlanBungeeComponent; import com.djrapitops.plan.PlanBungee; import com.djrapitops.plan.PlanBungeeComponent; -import com.djrapitops.plan.system.PlanSystem; +import com.djrapitops.plan.PlanSystem; import java.nio.file.Path; diff --git a/Plan/common/build.gradle b/Plan/common/build.gradle index 983d891c1..ff4735903 100644 --- a/Plan/common/build.gradle +++ b/Plan/common/build.gradle @@ -2,7 +2,6 @@ dependencies { compile "com.djrapitops:AbstractPluginFramework-api:$abstractPluginFrameworkVersion" compile project(":api") compile project(path: ":extensions", configuration: 'shadow') - compile "com.djrapitops:PlanPluginBridge:$planPluginBridgeVersion" compile "org.apache.httpcomponents:httpclient:$httpClientVersion" compile "org.apache.commons:commons-text:$commonsTextVersion" compile "com.googlecode.htmlcompressor:htmlcompressor:$htmlCompressorVersion" @@ -13,10 +12,10 @@ dependencies { compile "org.slf4j:slf4j-nop:$slf4jVersion" compile "org.slf4j:slf4j-api:$slf4jVersion" compile "com.maxmind.geoip2:geoip2:$geoIpVersion" - compileOnly "com.google.guava:guava:$guavaVersion" + compile "com.google.code.gson:gson:$gsonVersion" testCompile project(":api") - testCompile "com.google.code.gson:gson:2.8.5" + testCompile "com.google.code.gson:gson:$gsonVersion" } shadowJar { @@ -41,6 +40,7 @@ shadowJar { relocate 'com.zaxxer', 'plan.com.zaxxer' relocate 'com.github.benmanes', 'plan.com.github.benmanes' relocate 'com.googlecode', 'plan.com.googlecode' + relocate 'com.google', 'plan.com.google' relocate 'org.h2', 'plan.org.h2' relocate 'org.bstats', 'plan.org.bstats' relocate 'org.slf4j', 'plan.org.slf4j' diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/DebugChannels.java b/Plan/common/src/main/java/com/djrapitops/plan/DebugChannels.java similarity index 79% rename from Plan/common/src/main/java/com/djrapitops/plan/system/DebugChannels.java rename to Plan/common/src/main/java/com/djrapitops/plan/DebugChannels.java index 0bfb6ca49..04147a6e5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/DebugChannels.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/DebugChannels.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system; +package com.djrapitops.plan; /** * Identifiers for different Debug channels. @@ -28,10 +28,6 @@ public class DebugChannels { /* Static variable class */ } - public static final String ANALYSIS = "Analysis"; - public static final String INFO_REQUESTS = "InfoRequests"; - public static final String CONNECTIONS = "Connections"; - public static final String WEB_REQUESTS = "Web Requests"; public static final String IMPORTING = "Importing"; public static final String SQL = "SQL"; public static final String DATA_EXTENSIONS = "DataExtensions"; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/PlanPlugin.java b/Plan/common/src/main/java/com/djrapitops/plan/PlanPlugin.java index 6f42757e2..aca1fe4a3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/PlanPlugin.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/PlanPlugin.java @@ -16,7 +16,6 @@ */ package com.djrapitops.plan; -import com.djrapitops.plan.system.PlanSystem; import com.djrapitops.plugin.IPlugin; import com.djrapitops.plugin.command.ColorScheme; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java similarity index 68% rename from Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java index 4af6b6369..92062023d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/PlanSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java @@ -14,30 +14,34 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system; +package com.djrapitops.plan; import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.api.exceptions.EnableException; import com.djrapitops.plan.capability.CapabilityServiceImplementation; -import com.djrapitops.plan.data.plugin.HookHandler; +import com.djrapitops.plan.delivery.DeliveryUtilities; +import com.djrapitops.plan.delivery.export.ExportSystem; +import com.djrapitops.plan.delivery.webserver.NonProxyWebserverDisableChecker; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.delivery.webserver.WebServerSystem; +import com.djrapitops.plan.exceptions.EnableException; import com.djrapitops.plan.extension.ExtensionService; import com.djrapitops.plan.extension.ExtensionServiceImplementation; +import com.djrapitops.plan.gathering.cache.CacheSystem; +import com.djrapitops.plan.gathering.importing.ImportSystem; +import com.djrapitops.plan.gathering.listeners.ListenerSystem; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.processing.Processing; import com.djrapitops.plan.query.QueryServiceImplementation; -import com.djrapitops.plan.system.cache.CacheSystem; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.export.ExportSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.importing.ImportSystem; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.listeners.ListenerSystem; -import com.djrapitops.plan.system.locale.LocaleSystem; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.ConfigSystem; -import com.djrapitops.plan.system.tasks.TaskSystem; -import com.djrapitops.plan.system.update.VersionCheckSystem; -import com.djrapitops.plan.system.webserver.WebServerSystem; +import com.djrapitops.plan.settings.ConfigSystem; +import com.djrapitops.plan.settings.SettingsServiceImplementation; +import com.djrapitops.plan.settings.locale.LocaleSystem; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; import com.djrapitops.plugin.logging.L; +import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; import javax.inject.Inject; @@ -63,7 +67,6 @@ public class PlanSystem implements SubSystem { private final CacheSystem cacheSystem; private final ListenerSystem listenerSystem; private final TaskSystem taskSystem; - private final InfoSystem infoSystem; private final ServerInfo serverInfo; private final WebServerSystem webServerSystem; @@ -71,11 +74,11 @@ public class PlanSystem implements SubSystem { private final ImportSystem importSystem; private final ExportSystem exportSystem; - private final HtmlUtilities htmlUtilities; - private final HookHandler hookHandler; + private final DeliveryUtilities deliveryUtilities; private final ExtensionServiceImplementation extensionService; private final QueryServiceImplementation queryService; - private final PlanAPI planAPI; + private final SettingsServiceImplementation settingsService; + private final PluginLogger logger; private final ErrorHandler errorHandler; @Inject @@ -88,18 +91,18 @@ public class PlanSystem implements SubSystem { CacheSystem cacheSystem, ListenerSystem listenerSystem, TaskSystem taskSystem, - InfoSystem infoSystem, ServerInfo serverInfo, WebServerSystem webServerSystem, Processing processing, ImportSystem importSystem, ExportSystem exportSystem, - HtmlUtilities htmlUtilities, - HookHandler hookHandler, + DeliveryUtilities deliveryUtilities, ExtensionServiceImplementation extensionService, QueryServiceImplementation queryService, - PlanAPI planAPI, - ErrorHandler errorHandler + SettingsServiceImplementation settingsService, + PluginLogger logger, + ErrorHandler errorHandler, + PlanAPI.PlanAPIHolder apiHolder ) { this.files = files; this.configSystem = configSystem; @@ -109,18 +112,36 @@ public class PlanSystem implements SubSystem { this.cacheSystem = cacheSystem; this.listenerSystem = listenerSystem; this.taskSystem = taskSystem; - this.infoSystem = infoSystem; this.serverInfo = serverInfo; this.webServerSystem = webServerSystem; this.processing = processing; this.importSystem = importSystem; this.exportSystem = exportSystem; - this.htmlUtilities = htmlUtilities; - this.hookHandler = hookHandler; + this.deliveryUtilities = deliveryUtilities; this.extensionService = extensionService; this.queryService = queryService; - this.planAPI = planAPI; + this.settingsService = settingsService; + this.logger = logger; this.errorHandler = errorHandler; + + logger.log(L.INFO_COLOR, + "", + "§2 ██▌", + "§2 ██▌ ██▌", + "§2 ██▌██▌██▌██▌ §2Player Analytics", + "§2 ██▌██▌██▌██▌ §fv" + versionCheckSystem.getCurrentVersion(), + "" + ); + } + + public static String getMainAddress(WebServer webServer, DBSystem dbSystem) { + return dbSystem.getDatabase().query(ServerQueries.fetchProxyServerInformation()) + .map(Server::getWebAddress) + .orElse(webServer.getAccessAddress()); + } + + public String getMainAddress() { + return PlanSystem.getMainAddress(webServerSystem.getWebServer(), databaseSystem); } @Override @@ -138,12 +159,19 @@ public class PlanSystem implements SubSystem { serverInfo, importSystem, exportSystem, - infoSystem, cacheSystem, listenerSystem, - taskSystem, - hookHandler + taskSystem ); + + // Disables Webserver if Proxy is detected in the database + if (serverInfo.getServer().isNotProxy()) { + processing.submitNonCritical(new NonProxyWebserverDisableChecker( + configSystem.getConfig(), databaseSystem, webServerSystem, logger, errorHandler + )); + } + + settingsService.register(); queryService.register(); extensionService.register(); enabled = true; @@ -160,7 +188,6 @@ public class PlanSystem implements SubSystem { enabled = false; disableSystems( taskSystem, - hookHandler, cacheSystem, listenerSystem, importSystem, @@ -168,7 +195,6 @@ public class PlanSystem implements SubSystem { processing, databaseSystem, webServerSystem, - infoSystem, serverInfo, localeSystem, configSystem, @@ -235,18 +261,6 @@ public class PlanSystem implements SubSystem { return cacheSystem; } - public InfoSystem getInfoSystem() { - return infoSystem; - } - - public HookHandler getHookHandler() { - return hookHandler; - } - - public PlanAPI getPlanAPI() { - return planAPI; - } - public Processing getProcessing() { return processing; } @@ -255,8 +269,8 @@ public class PlanSystem implements SubSystem { return localeSystem; } - public HtmlUtilities getHtmlUtilities() { - return htmlUtilities; + public DeliveryUtilities getDeliveryUtilities() { + return deliveryUtilities; } public boolean isEnabled() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/SubSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/SubSystem.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/system/SubSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/SubSystem.java index 01a19b807..eaeff2e93 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/SubSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/SubSystem.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system; +package com.djrapitops.plan; -import com.djrapitops.plan.api.exceptions.EnableException; +import com.djrapitops.plan.exceptions.EnableException; /** * Represents a system that can be enabled and disabled. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TaskSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/TaskSystem.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TaskSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/TaskSystem.java index 4f25386ed..39e23e836 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TaskSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/TaskSystem.java @@ -14,9 +14,8 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks; +package com.djrapitops.plan; -import com.djrapitops.plan.system.SubSystem; import com.djrapitops.plugin.task.AbsRunnable; import com.djrapitops.plugin.task.PluginRunnable; import com.djrapitops.plugin.task.RunnableFactory; @@ -30,11 +29,9 @@ import com.djrapitops.plugin.task.RunnableFactory; */ public abstract class TaskSystem implements SubSystem { - protected final TPSCountTimer tpsCountTimer; protected final RunnableFactory runnableFactory; - public TaskSystem(RunnableFactory runnableFactory, TPSCountTimer tpsCountTimer) { - this.tpsCountTimer = tpsCountTimer; + public TaskSystem(RunnableFactory runnableFactory) { this.runnableFactory = runnableFactory; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/CommonAPI.java b/Plan/common/src/main/java/com/djrapitops/plan/api/CommonAPI.java index 97bc6b8b9..6489b3f1c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/CommonAPI.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/api/CommonAPI.java @@ -18,17 +18,14 @@ package com.djrapitops.plan.api; import com.djrapitops.plan.api.data.PlayerContainer; import com.djrapitops.plan.api.data.ServerContainer; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.data.plugin.HookHandler; import com.djrapitops.plan.data.plugin.PluginData; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.containers.ContainerFetchQueries; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.database.databases.operation.FetchOperations; -import com.djrapitops.plan.system.database.databases.sql.operation.SQLFetchOps; -import com.djrapitops.plan.utilities.uuid.UUIDUtility; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.UUIDUtility; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -44,13 +41,14 @@ import java.util.UUID; * PlanAPI extension for all implementations. * * @author Rsl1122 + * @deprecated Plan API v4 has been deprecated, use the APIv5 instead (https://github.com/plan-player-analytics/Plan/wiki/APIv5). */ @Singleton +@Deprecated public class CommonAPI implements PlanAPI { private final DBSystem dbSystem; private final UUIDUtility uuidUtility; - private final HookHandler hookHandler; private final PluginLogger logger; private final ErrorHandler errorHandler; @@ -58,13 +56,11 @@ public class CommonAPI implements PlanAPI { public CommonAPI( DBSystem dbSystem, UUIDUtility uuidUtility, - HookHandler hookHandler, PluginLogger logger, ErrorHandler errorHandler ) { this.dbSystem = dbSystem; this.uuidUtility = uuidUtility; - this.hookHandler = hookHandler; this.logger = logger; this.errorHandler = errorHandler; PlanAPIHolder.set(this); @@ -72,7 +68,9 @@ public class CommonAPI implements PlanAPI { @Override public void addPluginDataSource(PluginData pluginData) { - hookHandler.addPluginDataSource(pluginData); + logger.warn(pluginData.getClass().getName() + " was attempted to be registered." + + " PluginData API has been decommissioned, so this is a no-op." + + " Please move to using DataExtension API. https://github.com/plan-player-analytics/Plan/wiki/APIv5"); } @Override @@ -120,15 +118,6 @@ public class CommonAPI implements PlanAPI { return queryDB(UserIdentifierQueries.fetchPlayerNameOf(playerUUID)).orElse(null); } - @Override - public FetchOperations fetchFromPlanDB() { - logger.warn("PlanAPI#fetchFromPlanDB has been deprecated and will be removed in the future. Stack trace to follow"); - for (StackTraceElement element : Thread.currentThread().getStackTrace()) { - logger.warn(element.toString()); - } - return new SQLFetchOps(dbSystem.getDatabase()); - } - private T queryDB(Query query) { return dbSystem.getDatabase().query(query); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/PlanAPI.java b/Plan/common/src/main/java/com/djrapitops/plan/api/PlanAPI.java index 91516d0c9..3bce22993 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/PlanAPI.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/api/PlanAPI.java @@ -19,8 +19,13 @@ package com.djrapitops.plan.api; import com.djrapitops.plan.api.data.PlayerContainer; import com.djrapitops.plan.api.data.ServerContainer; import com.djrapitops.plan.data.plugin.PluginData; -import com.djrapitops.plan.system.database.databases.operation.FetchOperations; +import com.djrapitops.plan.identification.UUIDUtility; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plugin.logging.console.PluginLogger; +import com.djrapitops.plugin.logging.error.ErrorHandler; +import javax.inject.Inject; +import javax.inject.Singleton; import java.util.Collection; import java.util.Map; import java.util.Optional; @@ -30,7 +35,9 @@ import java.util.UUID; * Interface for PlanAPI methods. * * @author Rsl1122 + * @deprecated Plan API v4 has been deprecated, use the APIv5 instead (https://github.com/plan-player-analytics/Plan/wiki/APIv5). */ +@Deprecated public interface PlanAPI { static PlanAPI getInstance() { @@ -38,6 +45,7 @@ public interface PlanAPI { .orElseThrow(() -> new IllegalStateException("PlanAPI has not been initialised yet.")); } + @Singleton class PlanAPIHolder { static PlanAPI API; @@ -45,8 +53,14 @@ public interface PlanAPI { PlanAPIHolder.API = api; } - private PlanAPIHolder() { - /* Static variable holder */ + @Inject + public PlanAPIHolder( + DBSystem dbSystem, + UUIDUtility uuidUtility, + PluginLogger logger, + ErrorHandler errorHandler + ) { + set(new CommonAPI(dbSystem, uuidUtility, logger, errorHandler)); } } @@ -66,15 +80,6 @@ public interface PlanAPI { Map getKnownPlayerNames(); - /** - * Fetch things from the database. - * - * @return FetchOperations object. - * @deprecated FetchOperations interface is going to removed since it is too rigid. - */ - @Deprecated - FetchOperations fetchFromPlanDB(); - /** * Fetch PlayerContainer from the database. *

diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/data/PlayerContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/api/data/PlayerContainer.java index 76d0c728e..102f505b4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/data/PlayerContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/api/data/PlayerContainer.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.api.data; -import com.djrapitops.plan.data.store.Key; +import com.djrapitops.plan.delivery.domain.keys.Key; import java.util.Optional; @@ -24,22 +24,32 @@ import java.util.Optional; * Wrapper for a PlayerContainer. *

* The actual object is wrapped to avoid exposing too much API that might change. - * See {@link com.djrapitops.plan.data.store.keys.PlayerKeys} for Key objects. + * See {@link com.djrapitops.plan.delivery.domain.keys.PlayerKeys} for Key objects. *

* The Keys might change in the future, but the Optional API should help dealing with those cases. * + * @deprecated Plan API v4 has been deprecated, use the APIv5 instead (https://github.com/plan-player-analytics/Plan/wiki/APIv5). * @author Rsl1122 */ +@Deprecated public class PlayerContainer { - private final com.djrapitops.plan.data.store.containers.PlayerContainer container; + private final com.djrapitops.plan.delivery.domain.container.PlayerContainer container; - public PlayerContainer(com.djrapitops.plan.data.store.containers.PlayerContainer container) { + public PlayerContainer(com.djrapitops.plan.delivery.domain.container.PlayerContainer container) { this.container = container; } + /** + * @deprecated loginThreshold no longer used for activity index. + */ + @Deprecated public double getActivityIndex(long date, long playtimeMsThreshold, int loginThreshold) { - return container.getActivityIndex(date, playtimeMsThreshold, loginThreshold).getValue(); + return getActivityIndex(date, playtimeMsThreshold); + } + + public double getActivityIndex(long date, long playtimeMsThreshold) { + return container.getActivityIndex(date, playtimeMsThreshold).getValue(); } public boolean playedBetween(long after, long before) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/data/ServerContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/api/data/ServerContainer.java index e002f70c0..a7e0c03cc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/data/ServerContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/api/data/ServerContainer.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.api.data; -import com.djrapitops.plan.data.store.Key; +import com.djrapitops.plan.delivery.domain.keys.Key; import java.util.Optional; @@ -24,17 +24,19 @@ import java.util.Optional; * Wrapper for a ServerContainer. *

* The actual object is wrapped to avoid exposing too much API that might change. - * See {@link com.djrapitops.plan.data.store.keys.ServerKeys} for Key objects. + * See {@link com.djrapitops.plan.delivery.domain.keys.ServerKeys} for Key objects. *

* The Keys might change in the future, but the Optional API should help dealing with those cases. * * @author Rsl1122 + * @deprecated Plan API v4 has been deprecated, use the APIv5 instead (https://github.com/plan-player-analytics/Plan/wiki/APIv5). */ +@Deprecated public class ServerContainer { - private final com.djrapitops.plan.data.store.containers.ServerContainer container; + private final com.djrapitops.plan.delivery.domain.container.ServerContainer container; - public ServerContainer(com.djrapitops.plan.data.store.containers.ServerContainer container) { + public ServerContainer(com.djrapitops.plan.delivery.domain.container.ServerContainer container) { this.container = container; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/ConnectionFailException.java b/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/ConnectionFailException.java deleted file mode 100644 index db6c7dbed..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/ConnectionFailException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.api.exceptions.connection; - -import com.djrapitops.plan.system.webserver.response.ResponseCode; - -/** - * Thrown when Connection fails to connect to an address. - * - * @author Rsl1122 - */ -public class ConnectionFailException extends WebException { - - public ConnectionFailException(String message, Throwable cause) { - super(message, cause, ResponseCode.CONNECTION_REFUSED); - } - - public ConnectionFailException(Throwable cause) { - super(cause, ResponseCode.CONNECTION_REFUSED); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/TransferDatabaseException.java b/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/TransferDatabaseException.java deleted file mode 100644 index 55e9f45f6..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/TransferDatabaseException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.api.exceptions.connection; - -import com.djrapitops.plan.system.info.request.InfoRequest; - -/** - * Thrown when {@link com.djrapitops.plan.api.exceptions.database.DBOpException} occurs during {@link InfoRequest#runLocally()}. - * - * @author Rsl1122 - */ -public class TransferDatabaseException extends WebException { - - public TransferDatabaseException(Exception cause) { - super(cause); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/UnauthorizedServerException.java b/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/UnauthorizedServerException.java deleted file mode 100644 index 735057577..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/UnauthorizedServerException.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.api.exceptions.connection; - -import com.djrapitops.plan.system.webserver.response.ResponseCode; - -/** - * Thrown when Connection gets a 412 response due to ServerUUID not being in the database. - * - * @author Rsl1122 - */ -public class UnauthorizedServerException extends WebFailException { - - public UnauthorizedServerException(String message) { - super(message, ResponseCode.PRECONDITION_FAILED); - } - - public UnauthorizedServerException(String message, Throwable cause) { - super(message, cause, ResponseCode.PRECONDITION_FAILED); - } - - public UnauthorizedServerException(Throwable cause) { - super(cause, ResponseCode.PRECONDITION_FAILED); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/WebFailException.java b/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/WebFailException.java deleted file mode 100644 index f46eddcfd..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/WebFailException.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.api.exceptions.connection; - -import com.djrapitops.plan.system.webserver.response.ResponseCode; - -/** - * Group of WebExceptions that can be considered a failed connection state on some occasions. - * - * @author Rsl1122 - */ -public class WebFailException extends WebException { - - public WebFailException(String message, Throwable cause) { - super(message, cause); - } - - public WebFailException(String message, ResponseCode responseCode) { - super(message, responseCode); - } - - public WebFailException(String message, Throwable cause, ResponseCode responseCode) { - super(message, cause, responseCode); - } - - public WebFailException(Throwable cause, ResponseCode responseCode) { - super(cause, responseCode); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/BungeeSetupToggleCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/BungeeSetupToggleCommand.java deleted file mode 100644 index 662e0fbcc..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/BungeeSetupToggleCommand.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.command.commands; - -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plugin.command.CommandNode; -import com.djrapitops.plugin.command.CommandType; -import com.djrapitops.plugin.command.Sender; - -import javax.inject.Inject; - -/** - * Command for Toggling whether or not BungeeCord accepts set up requests. - *

- * This was added as a security measure against unwanted MySQL snooping. - * - * @author Rsl1122 - */ -public class BungeeSetupToggleCommand extends CommandNode { - - private final Locale locale; - private final ConnectionSystem connectionSystem; - - @Inject - public BungeeSetupToggleCommand(Locale locale, ConnectionSystem connectionSystem) { - super("setup", Permissions.MANAGE.getPermission(), CommandType.ALL); - - this.locale = locale; - this.connectionSystem = connectionSystem; - - setShortHelp(locale.getString(CmdHelpLang.SETUP)); - setInDepthHelp(locale.getArray(DeepHelpLang.SETUP)); - } - - @Override - public void onCommand(Sender sender, String s, String[] strings) { - if (connectionSystem.isSetupAllowed()) { - connectionSystem.setSetupAllowed(false); - } else { - connectionSystem.setSetupAllowed(true); - } - - String msg = locale.getString(connectionSystem.isSetupAllowed() ? CommandLang.SETUP_ALLOWED : CommandLang.SETUP_FORBIDDEN); - sender.sendMessage(msg); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java deleted file mode 100644 index bed68b24f..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageConDebugCommand.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.command.commands.manage; - -import com.djrapitops.plan.api.exceptions.connection.*; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.request.InfoRequestFactory; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.command.ColorScheme; -import com.djrapitops.plugin.command.CommandNode; -import com.djrapitops.plugin.command.CommandType; -import com.djrapitops.plugin.command.Sender; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Map; -import java.util.UUID; - -/** - * This manage SubCommand is used to request settings from Bungee so that connection can be established. - * - * @author Rsl1122 - */ -@Singleton -public class ManageConDebugCommand extends CommandNode { - - private final ColorScheme colorScheme; - private final Locale locale; - private final Processing processing; - private final ServerInfo serverInfo; - private final ConnectionSystem connectionSystem; - private final InfoRequestFactory infoRequestFactory; - private final WebServer webServer; - private final DBSystem dbSystem; - - @Inject - public ManageConDebugCommand( - ColorScheme colorScheme, - Locale locale, - Processing processing, - ServerInfo serverInfo, - ConnectionSystem connectionSystem, - InfoRequestFactory infoRequestFactory, - WebServer webServer, - DBSystem dbSystem - ) { - super("con", Permissions.MANAGE.getPermission(), CommandType.ALL); - - this.colorScheme = colorScheme; - this.locale = locale; - this.processing = processing; - this.serverInfo = serverInfo; - this.connectionSystem = connectionSystem; - this.infoRequestFactory = infoRequestFactory; - this.webServer = webServer; - this.dbSystem = dbSystem; - - boolean isProxy = serverInfo.getServer() != null && serverInfo.getServer().isProxy(); - setShortHelp(locale.getString(isProxy ? CmdHelpLang.CON : CmdHelpLang.MANAGE_CON)); - setInDepthHelp(locale.getArray(DeepHelpLang.MANAGE_CON)); - } - - private void testServer(Sender sender, Server server, Locale locale) { - String address = server.getWebAddress().toLowerCase(); - boolean usingHttps = address.startsWith("https"); - boolean local = address.contains("localhost") - || address.startsWith("https://:") // IP empty = Localhost - || address.startsWith("http://:") // IP empty = Localhost - || address.contains("127.0.0.1"); - - try { - connectionSystem.sendInfoRequest(infoRequestFactory.checkConnectionRequest(address), server); - sender.sendMessage(getMsgFor(address, usingHttps, local, true, true)); - } catch (UnauthorizedServerException e) { - sender.sendMessage(getMsgFor(address, usingHttps, local, true, false)); - sender.sendMessage(locale.getString(ManageLang.CON_UNAUTHORIZED)); - } catch (ConnectionFailException e) { - sender.sendMessage(getMsgFor(address, usingHttps, local, false, false)); - sender.sendMessage(locale.getString(ManageLang.CON_GENERIC_FAIL) + e.getCause().getClass().getSimpleName() + " " + e.getCause().getMessage()); - if (!local) { - sender.sendMessage(locale.getString(ManageLang.CON_EXTERNAL_URL)); - } - } catch (GatewayException e) { - sender.sendMessage(getMsgFor(address, usingHttps, local, true, false)); - } catch (NotFoundException e) { - sender.sendMessage(getMsgFor(address, usingHttps, local, false, false)); - sender.sendMessage(locale.getString(ManageLang.CON_OLD_VERSION)); - } catch (WebException e) { - sender.sendMessage(getMsgFor(address, usingHttps, local, false, false)); - sender.sendMessage(locale.getString(ManageLang.CON_EXCEPTION, e.getClass().getSimpleName())); - } - } - - @Override - public void onCommand(Sender sender, String commandLabel, String[] args) { - if (!webServer.isEnabled()) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_WEBSERVER_NOT_ENABLED)); - return; - } - - Database.State dbState = dbSystem.getDatabase().getState(); - if (dbState != Database.State.OPEN) { - sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name())); - return; - } - - processing.submitNonCritical(() -> testServers(sender)); - } - - private void testServers(Sender sender) { - Map servers = dbSystem.getDatabase().query(ServerQueries.fetchPlanServerInformation()); - - if (servers.isEmpty()) { - sender.sendMessage(locale.getString(ManageLang.CON_NO_SERVERS)); - } - - UUID thisServer = serverInfo.getServerUUID(); - for (Server server : servers.values()) { - if (thisServer.equals(server.getUuid())) { - continue; - } - testServer(sender, server, locale); - } - } - - private String getMsgFor(String address, boolean usingHttps, boolean local, boolean successTo, boolean successFrom) { - String tCol = colorScheme.getTertiaryColor(); - String sCol = colorScheme.getSecondaryColor(); - return tCol + address + sCol + ": " - + (usingHttps ? "HTTPS" : "HTTP") + " : " - + (local ? "Local" : "External") + " : " - + "To:" + (successTo ? "§aOK" : "§cFail") + sCol + " : " - + "From:" + (successFrom ? "§aOK" : "§cFail"); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageExportCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageExportCommand.java deleted file mode 100644 index d2a6fd570..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageExportCommand.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.command.commands.manage; - -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.export.HtmlExport; -import com.djrapitops.plan.system.export.JSONExport; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ExportSettings; -import com.djrapitops.plugin.command.ColorScheme; -import com.djrapitops.plugin.command.CommandNode; -import com.djrapitops.plugin.command.CommandType; -import com.djrapitops.plugin.command.Sender; -import com.djrapitops.plugin.utilities.Verify; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Arrays; -import java.util.Map; -import java.util.UUID; -import java.util.function.Consumer; - -/** - * This manage SubCommand is used to import data from 3rd party plugins. - * - * @author Rsl1122 - */ -@Singleton -public class ManageExportCommand extends CommandNode { - - private final Locale locale; - private final ColorScheme colorScheme; - private final PlanConfig config; - private final DBSystem dbSystem; - private final ServerInfo serverInfo; - private final HtmlExport htmlExport; - private final JSONExport jsonExport; - private final Processing processing; - - @Inject - public ManageExportCommand( - Locale locale, - ColorScheme colorScheme, - PlanConfig config, - DBSystem dbSystem, - ServerInfo serverInfo, - Processing processing, - HtmlExport htmlExport, - JSONExport jsonExport - ) { - super("export", Permissions.MANAGE.getPermission(), CommandType.CONSOLE); - - this.locale = locale; - this.colorScheme = colorScheme; - this.config = config; - this.dbSystem = dbSystem; - this.serverInfo = serverInfo; - this.htmlExport = htmlExport; - this.jsonExport = jsonExport; - this.processing = processing; - - setArguments("/list"); - setShortHelp(locale.getString(CmdHelpLang.MANAGE_EXPORT)); - setInDepthHelp(locale.getArray(DeepHelpLang.MANAGE_EXPORT)); - } - - @Override - public void onCommand(Sender sender, String commandLabel, String[] args) { - Verify.isTrue(args.length >= 1, - () -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ARGS, "1+", Arrays.toString(this.getArguments())))); - - String exportArg = args[0]; - - if ("list".equals(exportArg)) { - sender.sendMessage("> " + colorScheme.getMainColor() + "players, server_json"); - return; - } - - Database.State dbState = dbSystem.getDatabase().getState(); - if (dbState != Database.State.OPEN) { - sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name())); - return; - } - - getExportFunction(exportArg).accept(sender); - } - - private Consumer getExportFunction(String exportArg) { - switch (exportArg) { - case "players": - return this::exportPlayers; - case "server_json": - return this::exportServerJSON; - default: - return sender -> sender.sendMessage(locale.getString(ManageLang.FAIL_EXPORTER_NOT_FOUND, exportArg)); - } - } - - private void exportServerJSON(Sender sender) { - sender.sendMessage(locale.getString(ManageLang.PROGRESS_START)); - processing.submitNonCritical(() -> { - jsonExport.exportServerJSON(serverInfo.getServerUUID()); - sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS)); - }); - } - - private void exportPlayers(Sender sender) { - sender.sendMessage(locale.getString(ManageLang.PROGRESS_START)); - if (config.get(ExportSettings.PLAYERS_PAGE)) { - processing.submitNonCritical(htmlExport::exportPlayersPage); - } - Boolean exportPlayerJSON = config.get(ExportSettings.PLAYER_JSON); - Boolean exportPlayerHTML = config.get(ExportSettings.PLAYER_PAGES); - processing.submitNonCritical(() -> { - Map players = dbSystem.getDatabase().query(UserIdentifierQueries.fetchAllPlayerNames()); - int size = players.size(); - int i = 1; - for (Map.Entry entry : players.entrySet()) { - if (exportPlayerJSON) { - jsonExport.exportPlayerJSON(entry.getKey()); - } - if (exportPlayerHTML) { - htmlExport.exportPlayerPage(entry.getKey(), entry.getValue()); - } - i++; - if (i % 1000 == 0) { - sender.sendMessage(i + " / " + size + " processed.."); - } - } - sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS)); - }); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageSetupCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageSetupCommand.java deleted file mode 100644 index c16d66990..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageSetupCommand.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.command.commands.manage; - -import com.djrapitops.plan.api.exceptions.connection.*; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.command.CommandNode; -import com.djrapitops.plugin.command.CommandType; -import com.djrapitops.plugin.command.Sender; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.djrapitops.plugin.utilities.Verify; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Arrays; - -/** - * This manage SubCommand is used to request settings from Bungee so that connection can be established. - * - * @author Rsl1122 - */ -@Singleton -public class ManageSetupCommand extends CommandNode { - - private final Locale locale; - private final PlanConfig config; - private final Processing processing; - private final InfoSystem infoSystem; - private final WebServer webServer; - private final ErrorHandler errorHandler; - - @Inject - public ManageSetupCommand( - Locale locale, - PlanConfig config, - Processing processing, - InfoSystem infoSystem, - WebServer webServer, - ErrorHandler errorHandler - ) { - super("setup", Permissions.MANAGE.getPermission(), CommandType.PLAYER_OR_ARGS); - - this.locale = locale; - this.config = config; - this.processing = processing; - this.infoSystem = infoSystem; - this.webServer = webServer; - this.errorHandler = errorHandler; - - setArguments(""); - setShortHelp(locale.getString(CmdHelpLang.MANAGE_SETUP)); - setInDepthHelp(locale.getArray(DeepHelpLang.MANAGE_SETUP)); - } - - @Override - public void onCommand(Sender sender, String commandLabel, String[] args) { - Verify.isTrue(args.length >= 1, - () -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ONE_ARG, Arrays.toString(this.getArguments())))); - - if (!webServer.isEnabled()) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_WEBSERVER_NOT_ENABLED)); - return; - } - String address = args[0].toLowerCase(); - if (!address.startsWith("http") || address.endsWith("://")) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_URL_MISTAKE)); - return; - } - if (address.endsWith("/")) { - address = address.substring(0, address.length() - 1); - } - - requestSetup(sender, address); - } - - private void requestSetup(Sender sender, String address) { - processing.submitNonCritical(() -> { - try { - config.set(PluginSettings.BUNGEE_COPY_CONFIG, true); - - infoSystem.requestSetUp(address); - - sender.sendMessage(locale.getString(CommandLang.CONNECT_SUCCESS)); - } catch (ForbiddenException e) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_FORBIDDEN)); - } catch (BadRequestException e) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_BAD_REQUEST)); - } catch (UnauthorizedServerException e) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_UNAUTHORIZED)); - } catch (ConnectionFailException e) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_FAIL, e.getMessage())); - } catch (InternalErrorException e) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_INTERNAL_ERROR, e.getMessage())); - } catch (GatewayException e) { - sender.sendMessage(locale.getString(CommandLang.CONNECT_GATEWAY)); - } catch (WebException e) { - errorHandler.log(L.WARN, this.getClass(), e); - sender.sendMessage(locale.getString(CommandLang.CONNECT_FAIL, e.toString())); - } - }); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/PlanCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanCommand.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/command/PlanCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/PlanCommand.java index fe0bc7bc1..4a981b8d3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/PlanCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanCommand.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command; +package com.djrapitops.plan.commands; -import com.djrapitops.plan.command.commands.*; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; +import com.djrapitops.plan.commands.subcommands.*; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; import com.djrapitops.plugin.command.ColorScheme; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/PlanProxyCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanProxyCommand.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/command/PlanProxyCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/PlanProxyCommand.java index 628bdb6b0..1e533bb76 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/PlanProxyCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanProxyCommand.java @@ -14,15 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command; +package com.djrapitops.plan.commands; -import com.djrapitops.plan.command.commands.*; -import com.djrapitops.plan.command.commands.manage.ManageConDebugCommand; -import com.djrapitops.plan.command.commands.manage.ManageRawDataCommand; -import com.djrapitops.plan.command.commands.manage.ManageUninstalledCommand; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.commands.subcommands.*; +import com.djrapitops.plan.commands.subcommands.manage.ManageRawDataCommand; +import com.djrapitops.plan.commands.subcommands.manage.ManageUninstalledCommand; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; import com.djrapitops.plugin.command.ColorScheme; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; @@ -48,9 +47,7 @@ public class PlanProxyCommand extends TreeCmdNode { private final ListPlayersCommand listPlayersCommand; private final RegisterCommand registerCommand; private final Lazy webUserCommand; - private final ManageConDebugCommand conDebugCommand; private final ManageRawDataCommand rawDataCommand; - private final BungeeSetupToggleCommand setupToggleCommand; private final ReloadCommand reloadCommand; private final DisableCommand disableCommand; private final ManageUninstalledCommand uninstalledCommand; @@ -70,9 +67,7 @@ public class PlanProxyCommand extends TreeCmdNode { RegisterCommand registerCommand, Lazy webUserCommand, // Group 3 - ManageConDebugCommand conDebugCommand, ManageRawDataCommand rawDataCommand, - BungeeSetupToggleCommand setupToggleCommand, ManageUninstalledCommand uninstalledCommand, ReloadCommand reloadCommand, DisableCommand disableCommand @@ -87,9 +82,7 @@ public class PlanProxyCommand extends TreeCmdNode { this.listPlayersCommand = listPlayersCommand; this.registerCommand = registerCommand; this.webUserCommand = webUserCommand; - this.conDebugCommand = conDebugCommand; this.rawDataCommand = rawDataCommand; - this.setupToggleCommand = setupToggleCommand; this.reloadCommand = reloadCommand; this.disableCommand = disableCommand; @@ -113,9 +106,7 @@ public class PlanProxyCommand extends TreeCmdNode { webUserCommand.get() }; CommandNode[] manageGroup = { - conDebugCommand, rawDataCommand, - setupToggleCommand, uninstalledCommand, reloadCommand, disableCommand diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/AnalyzeCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/AnalyzeCommand.java similarity index 64% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/AnalyzeCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/AnalyzeCommand.java index 37b67efaf..766c27be9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/AnalyzeCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/AnalyzeCommand.java @@ -14,26 +14,25 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.queries.objects.WebUserQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.webserver.WebServer; +import com.djrapitops.plan.PlanSystem; +import com.djrapitops.plan.delivery.export.Exporter; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.exceptions.ExportException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.CommandUtils; @@ -44,7 +43,6 @@ import com.djrapitops.plugin.logging.error.ErrorHandler; import javax.inject.Inject; import javax.inject.Singleton; import java.util.Optional; -import java.util.UUID; /** * This SubCommand is used to run the analysis and access the /server link. @@ -56,18 +54,17 @@ public class AnalyzeCommand extends CommandNode { private final Locale locale; private final Processing processing; - private final InfoSystem infoSystem; + private final Exporter exporter; private final ServerInfo serverInfo; private final WebServer webServer; private final DBSystem dbSystem; - private final ConnectionSystem connectionSystem; private final ErrorHandler errorHandler; @Inject public AnalyzeCommand( Locale locale, Processing processing, - InfoSystem infoSystem, + Exporter exporter, ServerInfo serverInfo, WebServer webServer, DBSystem dbSystem, @@ -77,8 +74,7 @@ public class AnalyzeCommand extends CommandNode { this.locale = locale; this.processing = processing; - this.infoSystem = infoSystem; - connectionSystem = infoSystem.getConnectionSystem(); + this.exporter = exporter; this.serverInfo = serverInfo; this.webServer = webServer; this.dbSystem = dbSystem; @@ -91,23 +87,20 @@ public class AnalyzeCommand extends CommandNode { @Override public void onCommand(Sender sender, String commandLabel, String[] args) { - Database.State dbState = dbSystem.getDatabase().getState(); + Database database = dbSystem.getDatabase(); + Database.State dbState = database.getState(); if (dbState != Database.State.OPEN) { sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name())); return; } - sender.sendMessage(locale.getString(ManageLang.PROGRESS_START)); - processing.submitNonCritical(() -> { try { - Server server = getServer(args).orElseGet(serverInfo::getServer); - UUID serverUUID = server.getUuid(); - - infoSystem.generateAnalysisPage(serverUUID); + Server server = getServer(args); sendWebUserNotificationIfNecessary(sender); + exporter.exportServerPage(server); sendLink(server, sender); - } catch (DBOpException | WebException e) { + } catch (DBOpException | ExportException e) { sender.sendMessage("§cError occurred: " + e.toString()); errorHandler.log(L.ERROR, this.getClass(), e); } @@ -116,7 +109,8 @@ public class AnalyzeCommand extends CommandNode { private void sendLink(Server server, Sender sender) { String target = "/server/" + server.getName(); - String url = connectionSystem.getMainAddress() + target; + String address = PlanSystem.getMainAddress(webServer, dbSystem); + String url = address + target; String linkPrefix = locale.getString(CommandLang.LINK_PREFIX); sender.sendMessage(locale.getString(CommandLang.HEADER_ANALYSIS)); // Link @@ -138,22 +132,24 @@ public class AnalyzeCommand extends CommandNode { } } - private Optional getServer(String[] args) { - if (args.length >= 1 && connectionSystem.isServerAvailable()) { - String serverIdentifier = getGivenIdentifier(args); - return dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverIdentifier)) - .filter(server -> !server.isProxy()); - } - return Optional.empty(); + private Server getServer(String[] args) { + return getGivenIdentifier(args) + .flatMap(serverIdentifier -> dbSystem.getDatabase() + .query(ServerQueries.fetchServerMatchingIdentifier(serverIdentifier)) + ).filter(server -> !server.isProxy()) + .orElseGet(serverInfo::getServer); } - private String getGivenIdentifier(String[] args) { + private Optional getGivenIdentifier(String[] args) { + if (args.length < 1) { + return Optional.empty(); + } StringBuilder idBuilder = new StringBuilder(args[0]); if (args.length > 1) { for (int i = 1; i < args.length; i++) { idBuilder.append(" ").append(args[i]); } } - return idBuilder.toString(); + return Optional.of(idBuilder.toString()); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/DevCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DevCommand.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/DevCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DevCommand.java index ba05e527d..5740279c2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/DevCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DevCommand.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/DisableCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DisableCommand.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/DisableCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DisableCommand.java index 4fbf5ea1a..fbdc546a0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/DisableCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DisableCommand.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/InfoCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/InfoCommand.java similarity index 76% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/InfoCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/InfoCommand.java index d48de1777..1adac1e24 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/InfoCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/InfoCommand.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.GenericLang; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.update.VersionCheckSystem; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.GenericLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.version.VersionCheckSystem; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; @@ -42,7 +42,6 @@ public class InfoCommand extends CommandNode { private final PlanPlugin plugin; private final Locale locale; private final DBSystem dbSystem; - private final ConnectionSystem connectionSystem; private final VersionCheckSystem versionCheckSystem; @Inject @@ -50,7 +49,6 @@ public class InfoCommand extends CommandNode { PlanPlugin plugin, Locale locale, DBSystem dbSystem, - ConnectionSystem connectionSystem, VersionCheckSystem versionCheckSystem ) { super("info", Permissions.INFO.getPermission(), CommandType.CONSOLE); @@ -58,7 +56,6 @@ public class InfoCommand extends CommandNode { this.plugin = plugin; this.locale = locale; this.dbSystem = dbSystem; - this.connectionSystem = connectionSystem; this.versionCheckSystem = versionCheckSystem; setShortHelp(locale.get(CmdHelpLang.INFO).toString()); @@ -69,18 +66,19 @@ public class InfoCommand extends CommandNode { String yes = locale.getString(GenericLang.YES); String no = locale.getString(GenericLang.NO); - String updateAvailable = versionCheckSystem.isNewVersionAvailable() ? yes : no; - String connectedToBungee = connectionSystem.isServerAvailable() ? yes : no; - Database database = dbSystem.getDatabase(); + String updateAvailable = versionCheckSystem.isNewVersionAvailable() ? yes : no; + String proxyAvailable = database.query(ServerQueries.fetchProxyServerInformation()).isPresent() ? yes : no; + + String[] messages = { locale.getString(CommandLang.HEADER_INFO), "", locale.getString(CommandLang.INFO_VERSION, plugin.getVersion()), locale.getString(CommandLang.INFO_UPDATE, updateAvailable), locale.getString(CommandLang.INFO_DATABASE, database.getType().getName() + " (" + database.getState().name() + ")"), - locale.getString(CommandLang.INFO_PROXY_CONNECTION, connectedToBungee), + locale.getString(CommandLang.INFO_PROXY_CONNECTION, proxyAvailable), "", ">" }; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/InspectCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/InspectCommand.java similarity index 76% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/InspectCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/InspectCommand.java index c6e356c4c..84c14f1df 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/InspectCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/InspectCommand.java @@ -14,24 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.PlayerFetchQueries; -import com.djrapitops.plan.db.access.queries.objects.WebUserQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.processing.processors.info.InfoProcessors; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.webserver.WebServer; +import com.djrapitops.plan.PlanSystem; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.UUIDUtility; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries; +import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; import com.djrapitops.plan.utilities.MiscUtils; -import com.djrapitops.plan.utilities.uuid.UUIDUtility; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.CommandUtils; @@ -52,27 +51,21 @@ public class InspectCommand extends CommandNode { private final Locale locale; private final DBSystem dbSystem; private final WebServer webServer; - private final InfoProcessors processorFactory; private final Processing processing; - private final ConnectionSystem connectionSystem; private final UUIDUtility uuidUtility; private final ErrorHandler errorHandler; @Inject public InspectCommand( Locale locale, - InfoProcessors processorFactory, Processing processing, DBSystem dbSystem, WebServer webServer, - ConnectionSystem connectionSystem, UUIDUtility uuidUtility, ErrorHandler errorHandler ) { super("inspect", Permissions.INSPECT.getPermission(), CommandType.PLAYER_OR_ARGS); - this.processorFactory = processorFactory; this.processing = processing; - this.connectionSystem = connectionSystem; setArguments(""); this.locale = locale; @@ -117,7 +110,7 @@ public class InspectCommand extends CommandNode { } checkWebUserAndNotify(sender); - processing.submit(processorFactory.inspectCacheRequestProcessor(playerUUID, sender, playerName, this::sendInspectMsg)); + this.sendInspectMsg(sender, playerName); } catch (DBOpException e) { sender.sendMessage("§eDatabase exception occurred: " + e.getMessage()); errorHandler.log(L.ERROR, this.getClass(), e); @@ -138,7 +131,8 @@ public class InspectCommand extends CommandNode { private void sendInspectMsg(Sender sender, String playerName) { sender.sendMessage(locale.getString(CommandLang.HEADER_INSPECT, playerName)); - String url = connectionSystem.getMainAddress() + "/player/" + playerName; + String address = PlanSystem.getMainAddress(webServer, dbSystem); + String url = address + "/player/" + playerName; String linkPrefix = locale.getString(CommandLang.LINK_PREFIX); boolean console = !CommandUtils.isPlayer(sender); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/ListPlayersCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ListPlayersCommand.java similarity index 70% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/ListPlayersCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ListPlayersCommand.java index e301fcba0..bf2134f46 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/ListPlayersCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ListPlayersCommand.java @@ -14,14 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.PlanSystem; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.CommandUtils; @@ -37,14 +39,20 @@ import javax.inject.Inject; public class ListPlayersCommand extends CommandNode { private final Locale locale; - private final ConnectionSystem connectionSystem; + private final DBSystem dbSystem; + private final WebServer webServer; @Inject - public ListPlayersCommand(Locale locale, ConnectionSystem connectionSystem) { + public ListPlayersCommand( + Locale locale, + DBSystem dbSystem, + WebServer webServer + ) { super("players|pl|playerlist|list", Permissions.INSPECT_OTHER.getPermission(), CommandType.CONSOLE); this.locale = locale; - this.connectionSystem = connectionSystem; + this.dbSystem = dbSystem; + this.webServer = webServer; setShortHelp(locale.getString(CmdHelpLang.PLAYERS)); setInDepthHelp(locale.getArray(DeepHelpLang.PLAYERS)); @@ -59,7 +67,8 @@ public class ListPlayersCommand extends CommandNode { sender.sendMessage(locale.getString(CommandLang.HEADER_PLAYERS)); // Link - String url = connectionSystem.getMainAddress() + "/players/"; + String address = PlanSystem.getMainAddress(webServer, dbSystem); + String url = address + "/players/"; String linkPrefix = locale.getString(CommandLang.LINK_PREFIX); boolean console = !CommandUtils.isPlayer(sender); if (console) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/ListServersCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ListServersCommand.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/ListServersCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ListServersCommand.java index ab4867e7c..13b1476ff 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/ListServersCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ListServersCommand.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; import com.djrapitops.plugin.command.ColorScheme; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/ManageCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ManageCommand.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/ManageCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ManageCommand.java index ad95725d6..e3edbf425 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/ManageCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ManageCommand.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.command.commands.manage.*; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.commands.subcommands.manage.*; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; import com.djrapitops.plugin.command.ColorScheme; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; @@ -48,8 +48,6 @@ public class ManageCommand extends TreeCmdNode { ManageHotSwapCommand hotSwapCommand, ManageClearCommand clearCommand, // Group 2 - ManageSetupCommand setupCommand, - ManageConDebugCommand conDebugCommand, ManageImportCommand importCommand, ManageExportCommand exportCommand, ManageDisableCommand disableCommand, @@ -70,8 +68,6 @@ public class ManageCommand extends TreeCmdNode { clearCommand, }; CommandNode[] pluginGroup = { - setupCommand, - conDebugCommand, importCommand, exportCommand, disableCommand, diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/NetworkCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/NetworkCommand.java similarity index 70% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/NetworkCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/NetworkCommand.java index 8d4a49bc0..2677f82a1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/NetworkCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/NetworkCommand.java @@ -14,14 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.PlanSystem; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.CommandUtils; @@ -37,14 +39,20 @@ import javax.inject.Inject; public class NetworkCommand extends CommandNode { private final Locale locale; - private final ConnectionSystem connectionSystem; + private final DBSystem dbSystem; + private final WebServer webServer; @Inject - public NetworkCommand(Locale locale, ConnectionSystem connectionSystem) { + public NetworkCommand( + Locale locale, + DBSystem dbSystem, + WebServer webServer + ) { super("network|n|netw", Permissions.ANALYZE.getPermission(), CommandType.CONSOLE); this.locale = locale; - this.connectionSystem = connectionSystem; + this.dbSystem = dbSystem; + this.webServer = webServer; setShortHelp(locale.getString(CmdHelpLang.NETWORK)); setInDepthHelp(locale.getArray(DeepHelpLang.NETWORK)); @@ -59,7 +67,8 @@ public class NetworkCommand extends CommandNode { sender.sendMessage(locale.getString(CommandLang.HEADER_NETWORK)); // Link - String url = connectionSystem.getMainAddress() + "/network/"; + String address = PlanSystem.getMainAddress(webServer, dbSystem); + String url = address + "/network/"; String linkPrefix = locale.getString(CommandLang.LINK_PREFIX); boolean console = !CommandUtils.isPlayer(sender); if (console) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/QInspectCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/QInspectCommand.java similarity index 79% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/QInspectCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/QInspectCommand.java index 1e8786ae2..d03080000 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/QInspectCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/QInspectCommand.java @@ -14,32 +14,32 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.mutators.ActivityIndex; -import com.djrapitops.plan.data.store.mutators.GeoInfoMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.containers.ContainerFetchQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.GenericLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.delivery.domain.mutators.GeoInfoMutator; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.gathering.domain.GeoInfo; +import com.djrapitops.plan.identification.UUIDUtility; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.GenericLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries; import com.djrapitops.plan.utilities.MiscUtils; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.uuid.UUIDUtility; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; @@ -143,11 +143,7 @@ public class QInspectCommand extends CommandNode { String playerName = player.getValue(PlayerKeys.NAME).orElse(locale.getString(GenericLang.UNKNOWN)); - ActivityIndex activityIndex = player.getActivityIndex( - now, - config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), - config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD) - ); + ActivityIndex activityIndex = player.getActivityIndex(now, config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD)); Long registered = player.getValue(PlayerKeys.REGISTERED).orElse(0L); Long lastSeen = player.getValue(PlayerKeys.LAST_SEEN).orElse(0L); List geoInfo = player.getValue(PlayerKeys.GEO_INFO).orElse(new ArrayList<>()); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/RegisterCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/RegisterCommand.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/RegisterCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/RegisterCommand.java index 15ad88944..153e70488 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/RegisterCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/RegisterCommand.java @@ -14,21 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.WebUserQueries; -import com.djrapitops.plan.db.access.transactions.commands.RegisterWebUserTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.PlanSystem; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; +import com.djrapitops.plan.storage.database.transactions.commands.RegisterWebUserTransaction; import com.djrapitops.plan.utilities.PassEncryptUtil; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; @@ -61,7 +62,7 @@ public class RegisterCommand extends CommandNode { private final Locale locale; private final Processing processing; private final DBSystem dbSystem; - private final ConnectionSystem connectionSystem; + private final WebServer webServer; private final PluginLogger logger; private final ErrorHandler errorHandler; @@ -70,7 +71,7 @@ public class RegisterCommand extends CommandNode { Locale locale, Processing processing, DBSystem dbSystem, - ConnectionSystem connectionSystem, + WebServer webServer, PluginLogger logger, ErrorHandler errorHandler ) { @@ -79,7 +80,7 @@ public class RegisterCommand extends CommandNode { this.locale = locale; this.processing = processing; - this.connectionSystem = connectionSystem; + this.webServer = webServer; this.logger = logger; this.dbSystem = dbSystem; this.errorHandler = errorHandler; @@ -181,7 +182,7 @@ public class RegisterCommand extends CommandNode { } private void sendLink(Sender sender) { - String url = connectionSystem.getMainAddress(); + String url = PlanSystem.getMainAddress(webServer, dbSystem); String linkPrefix = locale.getString(CommandLang.LINK_PREFIX); // Link boolean console = !CommandUtils.isPlayer(sender); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/ReloadCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ReloadCommand.java similarity index 78% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/ReloadCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ReloadCommand.java index b538fff5e..3efece550 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/ReloadCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/ReloadCommand.java @@ -14,20 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.djrapitops.plugin.task.RunnableFactory; import javax.inject.Inject; @@ -41,15 +40,13 @@ public class ReloadCommand extends CommandNode { private final PlanPlugin plugin; private final Locale locale; private final ErrorHandler errorHandler; - private final RunnableFactory runnableFactory; @Inject - public ReloadCommand(PlanPlugin plugin, Locale locale, RunnableFactory runnableFactory, ErrorHandler errorHandler) { + public ReloadCommand(PlanPlugin plugin, Locale locale, ErrorHandler errorHandler) { super("reload", Permissions.RELOAD.getPermission(), CommandType.CONSOLE); this.plugin = plugin; this.locale = locale; - this.runnableFactory = runnableFactory; this.errorHandler = errorHandler; setShortHelp(locale.getString(CmdHelpLang.RELOAD)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/SearchCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/SearchCommand.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/SearchCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/SearchCommand.java index 63e977156..eaf94c275 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/SearchCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/SearchCommand.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/WebUserCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/WebUserCommand.java similarity index 78% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/WebUserCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/WebUserCommand.java index b8d311473..41a44706f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/WebUserCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/WebUserCommand.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands; +package com.djrapitops.plan.commands.subcommands; -import com.djrapitops.plan.command.commands.webuser.WebCheckCommand; -import com.djrapitops.plan.command.commands.webuser.WebDeleteCommand; -import com.djrapitops.plan.command.commands.webuser.WebLevelCommand; -import com.djrapitops.plan.command.commands.webuser.WebListUsersCommand; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.commands.subcommands.webuser.WebCheckCommand; +import com.djrapitops.plan.commands.subcommands.webuser.WebDeleteCommand; +import com.djrapitops.plan.commands.subcommands.webuser.WebLevelCommand; +import com.djrapitops.plan.commands.subcommands.webuser.WebListUsersCommand; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; import com.djrapitops.plugin.command.ColorScheme; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageBackupCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageBackupCommand.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageBackupCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageBackupCommand.java index 68b86db3f..5e769e24f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageBackupCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageBackupCommand.java @@ -14,25 +14,25 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.SQLiteDB; -import com.djrapitops.plan.db.access.queries.ServerAggregateQueries; -import com.djrapitops.plan.db.access.transactions.BackupCopyTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.SQLiteDB; +import com.djrapitops.plan.storage.database.queries.ServerAggregateQueries; +import com.djrapitops.plan.storage.database.transactions.BackupCopyTransaction; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageClearCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageClearCommand.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageClearCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageClearCommand.java index f2ce884e2..12b7f8f43 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageClearCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageClearCommand.java @@ -14,23 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.commands.RemoveEverythingTransaction; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.processing.Processing; import com.djrapitops.plan.query.QueryServiceImplementation; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageDisableCommand.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageDisableCommand.java index 913c32886..380d2a5dc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageDisableCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageDisableCommand.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.status.Status; +import com.djrapitops.plan.gathering.listeners.Status; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageExportCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageExportCommand.java new file mode 100644 index 000000000..2059b44e7 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageExportCommand.java @@ -0,0 +1,178 @@ +/* + * 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 . + */ +package com.djrapitops.plan.commands.subcommands.manage; + +import com.djrapitops.plan.delivery.export.Exporter; +import com.djrapitops.plan.exceptions.ExportException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.ExportSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries; +import com.djrapitops.plugin.command.ColorScheme; +import com.djrapitops.plugin.command.CommandNode; +import com.djrapitops.plugin.command.CommandType; +import com.djrapitops.plugin.command.Sender; +import com.djrapitops.plugin.utilities.Verify; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Arrays; +import java.util.Map; +import java.util.UUID; +import java.util.function.Consumer; + +/** + * This manage SubCommand is used to import data from 3rd party plugins. + * + * @author Rsl1122 + */ +@Singleton +public class ManageExportCommand extends CommandNode { + + private final Locale locale; + private final ColorScheme colorScheme; + private final PlanConfig config; + private final DBSystem dbSystem; + private final ServerInfo serverInfo; + private final Exporter exporter; + private final Processing processing; + + @Inject + public ManageExportCommand( + Locale locale, + ColorScheme colorScheme, + PlanConfig config, + DBSystem dbSystem, + ServerInfo serverInfo, + Exporter exporter, + Processing processing + ) { + super("export", Permissions.MANAGE.getPermission(), CommandType.CONSOLE); + + this.locale = locale; + this.colorScheme = colorScheme; + this.config = config; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + this.exporter = exporter; + this.processing = processing; + + setArguments("/list"); + setShortHelp(locale.getString(CmdHelpLang.MANAGE_EXPORT)); + setInDepthHelp(locale.getArray(DeepHelpLang.MANAGE_EXPORT)); + } + + @Override + public void onCommand(Sender sender, String commandLabel, String[] args) { + Verify.isTrue(args.length >= 1, + () -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_REQ_ARGS, "1+", Arrays.toString(this.getArguments())))); + + String exportArg = args[0]; + + if ("list".equals(exportArg)) { + sender.sendMessage("> " + colorScheme.getMainColor() + "players, server_json"); + return; + } + + Database.State dbState = dbSystem.getDatabase().getState(); + if (dbState != Database.State.OPEN) { + sender.sendMessage(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name())); + return; + } + + getExportFunction(exportArg).accept(sender); + } + + private Consumer getExportFunction(String exportArg) { + if ("players".equals(exportArg)) { + return this::exportPlayers; + } else if ("server_json".endsWith(exportArg)) { + return this::exportServerJSON; + } + return sender -> sender.sendMessage(locale.getString(ManageLang.FAIL_EXPORTER_NOT_FOUND, exportArg)); + } + + private void exportServerJSON(Sender sender) { + if (!config.get(ExportSettings.SERVER_JSON)) { + sender.sendMessage("§c'" + ExportSettings.SERVER_JSON.getPath() + "': false"); + return; + } + processing.submitNonCritical(() -> { + try { + sender.sendMessage(locale.getString(ManageLang.PROGRESS_START)); + exporter.exportServerJSON(serverInfo.getServer()); + sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS)); + } catch (ExportException e) { + sender.sendMessage(locale.getString(ManageLang.PROGRESS_FAIL)); + sender.sendMessage("§c" + e.toString()); + } + }); + } + + private void exportPlayers(Sender sender) { + boolean exportPlayerJSON = config.get(ExportSettings.PLAYER_JSON); + boolean exportPlayerHTML = config.get(ExportSettings.PLAYER_PAGES); + boolean exportPlayersHtml = config.get(ExportSettings.PLAYERS_PAGE); + if (!exportPlayerJSON && !exportPlayerHTML) { + sender.sendMessage(locale.getString(ManageLang.PROGRESS_FAIL)); + sender.sendMessage("§c'" + ExportSettings.PLAYER_JSON.getPath() + "' & '" + ExportSettings.PLAYER_PAGES.getPath() + "': false"); + return; + } + + if (exportPlayersHtml) { + processing.submitNonCritical(exporter::exportPlayersPage); + } + processing.submitNonCritical(() -> performExport(sender, exportPlayerJSON, exportPlayerHTML)); + } + + private void performExport(Sender sender, boolean exportPlayerJSON, boolean exportPlayerHTML) { + sender.sendMessage(locale.getString(ManageLang.PROGRESS_START)); + + Map players = dbSystem.getDatabase().query(UserIdentifierQueries.fetchAllPlayerNames()); + int size = players.size(); + int failed = 0; + + int i = 1; + for (Map.Entry entry : players.entrySet()) { + try { + if (exportPlayerJSON) exporter.exportPlayerJSON(entry.getKey(), entry.getValue()); + if (exportPlayerHTML) exporter.exportPlayerPage(entry.getKey(), entry.getValue()); + } catch (ExportException e) { + failed++; + } + i++; + if (i % 1000 == 0) { + sender.sendMessage(i + " / " + size + " processed.."); + } + } + sender.sendMessage(locale.getString(ManageLang.PROGRESS_SUCCESS)); + if (failed != 0) { + sender.sendMessage(locale.getString(ManageLang.PROGRESS_FAIL)); + sender.sendMessage(" §2✔: §f" + (i - failed)); + sender.sendMessage(" §c✕: §f" + failed); + } + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHotSwapCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageHotSwapCommand.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHotSwapCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageHotSwapCommand.java index e4179e6ed..9975e71ed 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageHotSwapCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageHotSwapCommand.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.settings.Permissions; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DatabaseSettings; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DatabaseSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageImportCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageImportCommand.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageImportCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageImportCommand.java index 608baae6a..fa8976b5a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageImportCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageImportCommand.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.importing.ImportSystem; -import com.djrapitops.plan.system.importing.importers.Importer; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.gathering.importing.ImportSystem; +import com.djrapitops.plan.gathering.importing.importers.Importer; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageMoveCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageMoveCommand.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageMoveCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageMoveCommand.java index 2dec242fd..486e656b8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageMoveCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageMoveCommand.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.BackupCopyTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.BackupCopyTransaction; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRawDataCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRawDataCommand.java similarity index 73% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRawDataCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRawDataCommand.java index 7a8822883..7012e7b37 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRawDataCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRawDataCommand.java @@ -14,14 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.PlanSystem; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.utilities.MiscUtils; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; @@ -41,14 +43,16 @@ import java.util.Arrays; public class ManageRawDataCommand extends CommandNode { private final Locale locale; - private final ConnectionSystem connectionSystem; + private final DBSystem dbSystem; + private final WebServer webServer; @Inject - public ManageRawDataCommand(Locale locale, ConnectionSystem connectionSystem) { + public ManageRawDataCommand(Locale locale, DBSystem dbSystem, WebServer webServer) { super("raw", Permissions.MANAGE.getPermission(), CommandType.PLAYER_OR_ARGS); this.locale = locale; - this.connectionSystem = connectionSystem; + this.dbSystem = dbSystem; + this.webServer = webServer; setArguments(""); setShortHelp(locale.getString(CmdHelpLang.MANAGE_RAW_DATA)); @@ -64,7 +68,8 @@ public class ManageRawDataCommand extends CommandNode { sender.sendMessage(locale.getString(CommandLang.HEADER_INSPECT, playerName)); // Link - String url = connectionSystem.getMainAddress() + "/player/" + playerName + "/raw"; + String address = PlanSystem.getMainAddress(webServer, dbSystem); + String url = address + "/player/" + playerName + "/raw"; String linkPrefix = locale.getString(CommandLang.LINK_PREFIX); boolean console = !CommandUtils.isPlayer(sender); if (console) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRemoveCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRemoveCommand.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRemoveCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRemoveCommand.java index fcc9efb76..eba9aab16 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRemoveCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRemoveCommand.java @@ -14,23 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.PlayerFetchQueries; -import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransaction; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.UUIDUtility; +import com.djrapitops.plan.processing.Processing; import com.djrapitops.plan.query.QueryServiceImplementation; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries; +import com.djrapitops.plan.storage.database.transactions.commands.RemovePlayerTransaction; import com.djrapitops.plan.utilities.MiscUtils; -import com.djrapitops.plan.utilities.uuid.UUIDUtility; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRestoreCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRestoreCommand.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRestoreCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRestoreCommand.java index c23b7f581..9bb58e5cc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageRestoreCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageRestoreCommand.java @@ -14,21 +14,21 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.SQLiteDB; -import com.djrapitops.plan.db.access.transactions.BackupCopyTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.SQLiteDB; +import com.djrapitops.plan.storage.database.transactions.BackupCopyTransaction; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageUninstalledCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageUninstalledCommand.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageUninstalledCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageUninstalledCommand.java index ce23ecf31..723c2284c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/manage/ManageUninstalledCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/manage/ManageUninstalledCommand.java @@ -14,22 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.manage; +package com.djrapitops.plan.commands.subcommands.manage; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.transactions.commands.SetServerAsUninstalledTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.DeepHelpLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.DeepHelpLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.transactions.commands.SetServerAsUninstalledTransaction; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebCheckCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebCheckCommand.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebCheckCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebCheckCommand.java index 87a0f5e76..05144209e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebCheckCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebCheckCommand.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.webuser; +package com.djrapitops.plan.commands.subcommands.webuser; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.WebUserQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebDeleteCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebDeleteCommand.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebDeleteCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebDeleteCommand.java index 119ad4c8b..4c05fe30b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebDeleteCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebDeleteCommand.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.webuser; +package com.djrapitops.plan.commands.subcommands.webuser; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.WebUserQueries; -import com.djrapitops.plan.db.access.transactions.commands.RemoveWebUserTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; +import com.djrapitops.plan.storage.database.transactions.commands.RemoveWebUserTransaction; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebLevelCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebLevelCommand.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebLevelCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebLevelCommand.java index 2d95007ac..5e09a9001 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebLevelCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebLevelCommand.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.webuser; +package com.djrapitops.plan.commands.subcommands.webuser; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebListUsersCommand.java b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebListUsersCommand.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebListUsersCommand.java rename to Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebListUsersCommand.java index 861a5fb5c..e4dbc368a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/command/commands/webuser/WebListUsersCommand.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/webuser/WebListUsersCommand.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.command.commands.webuser; +package com.djrapitops.plan.commands.subcommands.webuser; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.WebUserQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.CmdHelpLang; -import com.djrapitops.plan.system.locale.lang.CommandLang; -import com.djrapitops.plan.system.locale.lang.ManageLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.Permissions; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.CmdHelpLang; +import com.djrapitops.plan.settings.locale.lang.CommandLang; +import com.djrapitops.plan.settings.locale.lang.ManageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java index 159b17e98..fe4484973 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/element/TableContainer.java @@ -16,9 +16,8 @@ */ package com.djrapitops.plan.data.element; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.icon.Icon; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; import com.djrapitops.plugin.utilities.ArrayUtil; import java.io.Serializable; @@ -67,14 +66,14 @@ public class TableContainer { return getTableHeader() + parseHeader() + parseBody() + - "" + (jqueryDatatable != null ? "" : ""); + ""; } public final String parseBody() { if (values.isEmpty()) { addRow("No Data"); } - return Html.TABLE_BODY.parse(buildBody()); + return "" + buildBody() + ""; } @@ -136,8 +135,6 @@ public class TableContainer { * Make use of jQuery Data-tables plugin. *

* Use this with custom tables. - *

- * If this is called, result of {@code parseHtml()} should be wrapped with {@code Html.PANEL.parse(Html.PANEL_BODY.parse(result))} */ public void useJqueryDataTables() { this.jqueryDatatable = "player-plugin-table"; @@ -154,9 +151,12 @@ public class TableContainer { private String getTableHeader() { if (jqueryDatatable != null) { - return "

" + Html.TABLE_JQUERY.parse(jqueryDatatable); + return "
" + + ""; } else { - return Html.TABLE_SCROLL.parse(); + return "
"; } } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/HookHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/HookHandler.java deleted file mode 100644 index 84427f893..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/HookHandler.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.plugin; - -import com.djrapitops.plan.data.element.InspectContainer; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.console.PluginLogger; -import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.djrapitops.pluginbridge.plan.Bridge; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.*; - -/** - * Class responsible for hooking to other plugins and managing the %plugins% - * placeholder on Analysis and Inspect pages. - * - * @author Rsl1122 - */ -@Singleton -public class HookHandler implements SubSystem { - - private final List additionalDataSources; - - private final Bridge bridge; - private PluginsConfigSection configHandler; - private final PluginLogger logger; - private final ErrorHandler errorHandler; - - @Inject - public HookHandler( - Bridge bridge, - PluginsConfigSection configHandler, - PluginLogger logger, - ErrorHandler errorHandler - ) { - this.bridge = bridge; - this.configHandler = configHandler; - this.logger = logger; - this.errorHandler = errorHandler; - - additionalDataSources = new ArrayList<>(); - } - - @Override - public void enable() { - try { - bridge.hook(this); - } catch (Exception e) { - errorHandler.log(L.ERROR, this.getClass(), e); - logger.error("Plan Plugin Bridge not included in the plugin jar."); - } - } - - @Override - public void disable() { - // Nothing to disable - } - - /** - * Adds a new PluginData source to the list. - *

- * The plugin data will appear on Analysis and/or Inspect pages depending on - * how the extending object is set up. - *

- * Refer to documentation on GitHub for more information. - * - * @param dataSource an object extending the PluginData class. - */ - public void addPluginDataSource(PluginData dataSource) { - if (dataSource == null) { - return; - } - try { - if (!configHandler.hasSection(dataSource)) { - configHandler.createSection(dataSource); - } - if (configHandler.isEnabled(dataSource)) { - additionalDataSources.stream() - .filter(pluginData -> pluginData.getSourcePlugin().equals(dataSource.getSourcePlugin())) - .findAny() - .ifPresent(additionalDataSources::remove); - logger.debug("Registered a new datasource: " + dataSource.getSourcePlugin()); - additionalDataSources.add(dataSource); - } - } catch (Exception e) { - errorHandler.log(L.WARN, this.getClass(), e); - logger.error("Attempting to register PluginDataSource caused an exception."); - } - } - - /** - * Used to get all PluginData objects currently registered. - * - * @return List of PluginData objects. - */ - public List getAdditionalDataSources() { - return additionalDataSources; - } - - public Map getInspectContainersFor(UUID uuid) { - List plugins = getAdditionalDataSources(); - Map containers = new HashMap<>(); - for (PluginData pluginData : plugins) { - InspectContainer inspectContainer = new InspectContainer(); - try { - InspectContainer container = pluginData.getPlayerData(uuid, inspectContainer); - if (container != null && !container.isEmpty()) { - containers.put(pluginData, container); - } - } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) { - String pluginName = pluginData.getSourcePlugin(); - logger.error("PluginData caused exception: " + pluginName + - ", you can disable the integration under 'Plugins." + pluginName + ".Enabled'"); - errorHandler.log(L.WARN, pluginData.getClass(), e); - } - } - return containers; - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/PluginData.java b/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/PluginData.java index b2cd15c91..3c881b160 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/PluginData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/PluginData.java @@ -18,12 +18,11 @@ package com.djrapitops.plan.data.plugin; import com.djrapitops.plan.data.element.AnalysisContainer; import com.djrapitops.plan.data.element.InspectContainer; -import com.djrapitops.plan.utilities.html.Html; import com.djrapitops.plan.utilities.html.icon.Color; import com.djrapitops.plan.utilities.html.icon.Icon; -import com.google.common.base.Objects; import java.util.Collection; +import java.util.Objects; import java.util.UUID; /** @@ -46,7 +45,7 @@ public abstract class PluginData { private String helpText; - protected com.djrapitops.plan.data.store.containers.AnalysisContainer analysisData; + protected com.djrapitops.plan.data.store.containers.AnalysisContainer analysisData = new com.djrapitops.plan.data.store.containers.AnalysisContainer(); public PluginData(ContainerSize size, String sourcePlugin) { this.size = size; @@ -94,22 +93,22 @@ public abstract class PluginData { } protected final void setHelpText(String html) { - helpText = Html.HELP_BUBBLE.parse(sourcePlugin, html); + // no-op } @Override - public final boolean equals(Object o) { + public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PluginData that = (PluginData) o; return size == that.size && - Objects.equal(sourcePlugin, that.sourcePlugin) && - Objects.equal(pluginIcon, that.pluginIcon); + Objects.equals(sourcePlugin, that.sourcePlugin) && + Objects.equals(pluginIcon, that.pluginIcon); } @Override - public final int hashCode() { - return Objects.hashCode(size, sourcePlugin, pluginIcon); + public int hashCode() { + return Objects.hash(size, sourcePlugin, pluginIcon); } /** diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java index 3a26990f9..462edbbcc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/AnalysisContainer.java @@ -16,546 +16,17 @@ */ package com.djrapitops.plan.data.store.containers; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.Type; -import com.djrapitops.plan.data.store.keys.AnalysisKeys; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.keys.ServerKeys; -import com.djrapitops.plan.data.store.mutators.*; -import com.djrapitops.plan.data.store.mutators.health.HealthInformation; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DisplaySettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.settings.theme.ThemeVal; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.graphs.Graphs; -import com.djrapitops.plan.utilities.html.graphs.bar.BarGraph; -import com.djrapitops.plan.utilities.html.graphs.line.PingGraph; -import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie; -import com.djrapitops.plan.utilities.html.graphs.stack.StackGraph; -import com.djrapitops.plan.utilities.html.pages.AnalysisPluginTabs; -import com.djrapitops.plan.utilities.html.structure.Accordions; -import com.djrapitops.plan.utilities.html.structure.AnalysisPluginsTabContentCreator; -import com.djrapitops.plan.utilities.html.structure.RecentLoginList; -import com.djrapitops.plan.utilities.html.structure.SessionAccordion; -import com.djrapitops.plan.utilities.html.tables.HtmlTables; -import com.djrapitops.plugin.api.TimeAmount; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; +import com.djrapitops.plan.delivery.domain.container.DynamicDataContainer; +import com.djrapitops.plan.delivery.domain.keys.PlaceholderKey; /** * Container used for analysis. * * @author Rsl1122 - * @see com.djrapitops.plan.data.store.keys.AnalysisKeys for Key objects - * @see com.djrapitops.plan.data.store.PlaceholderKey for placeholder information + * @see com.djrapitops.plan.delivery.domain.keys.AnalysisKeys for Key objects + * @see PlaceholderKey for placeholder information + * @deprecated AnalysisContainer is no longer used. */ +@Deprecated public class AnalysisContainer extends DynamicDataContainer { - - private final ServerContainer serverContainer; - - private final String version; - private final Locale locale; - private final PlanConfig config; - private final Theme theme; - private final ServerProperties serverProperties; - private final Formatters formatters; - private final Graphs graphs; - private final HtmlTables tables; - private final Accordions accordions; - private final AnalysisPluginsTabContentCreator pluginsTabContentCreator; - private TimeZone timeZone; - - public AnalysisContainer( - ServerContainer serverContainer, - String version, - Locale locale, - PlanConfig config, - Theme theme, - ServerProperties serverProperties, - Formatters formatters, - Graphs graphs, - HtmlTables tables, - Accordions accordions, - AnalysisPluginsTabContentCreator pluginsTabContentCreator - ) { - this.serverContainer = serverContainer; - this.version = version; - this.locale = locale; - this.config = config; - this.theme = theme; - this.serverProperties = serverProperties; - this.formatters = formatters; - this.graphs = graphs; - this.tables = tables; - this.accordions = accordions; - this.pluginsTabContentCreator = pluginsTabContentCreator; - - timeZone = config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT"); - - addAnalysisSuppliers(); - } - - public ServerContainer getServerContainer() { - return serverContainer; - } - - private void addAnalysisSuppliers() { - putCachingSupplier(AnalysisKeys.SESSIONS_MUTATOR, () -> SessionsMutator.forContainer(serverContainer)); - putCachingSupplier(AnalysisKeys.TPS_MUTATOR, () -> TPSMutator.forContainer(serverContainer)); - putCachingSupplier(AnalysisKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(serverContainer)); - - addConstants(); - addPlayerSuppliers(); - addSessionSuppliers(); - addGraphSuppliers(); - addTPSAverageSuppliers(); - addCommandSuppliers(); - addServerHealth(); - addPluginSuppliers(); - } - - private void addConstants() { - long now = System.currentTimeMillis(); - putRawData(AnalysisKeys.ANALYSIS_TIME, now); - putRawData(AnalysisKeys.ANALYSIS_TIME_DAY_AGO, now - TimeUnit.DAYS.toMillis(1L)); - putRawData(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO, now - TimeAmount.WEEK.toMillis(1L)); - putRawData(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO, now - TimeAmount.MONTH.toMillis(1L)); - putSupplier(AnalysisKeys.REFRESH_TIME_F, () -> formatters.clockLong().apply(getUnsafe(AnalysisKeys.ANALYSIS_TIME))); - putSupplier(AnalysisKeys.REFRESH_TIME_FULL_F, () -> formatters.secondLong().apply(getUnsafe(AnalysisKeys.ANALYSIS_TIME))); - - putRawData(AnalysisKeys.VERSION, version); - putSupplier(AnalysisKeys.TIME_ZONE, config::getTimeZoneOffsetHours); - putRawData(AnalysisKeys.FIRST_DAY, 1); - putRawData(AnalysisKeys.TPS_MEDIUM, config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED)); - putRawData(AnalysisKeys.TPS_HIGH, config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_HIGH)); - putRawData(AnalysisKeys.DISK_MEDIUM, config.get(DisplaySettings.GRAPH_DISK_THRESHOLD_MED)); - putRawData(AnalysisKeys.DISK_HIGH, config.get(DisplaySettings.GRAPH_DISK_THRESHOLD_HIGH)); - - addServerProperties(); - addThemeColors(); - } - - private void addServerProperties() { - putCachingSupplier(AnalysisKeys.SERVER_NAME, () -> serverContainer.getValue(ServerKeys.NAME).orElse("Plan")); - - putRawData(AnalysisKeys.PLAYERS_MAX, serverProperties.getMaxPlayers()); - putRawData(AnalysisKeys.PLAYERS_ONLINE, serverProperties.getOnlinePlayers()); - } - - private void addThemeColors() { - putRawData(AnalysisKeys.ACTIVITY_PIE_COLORS, theme.getValue(ThemeVal.GRAPH_ACTIVITY_PIE)); - putRawData(AnalysisKeys.GM_PIE_COLORS, theme.getValue(ThemeVal.GRAPH_GM_PIE)); - putRawData(AnalysisKeys.PLAYERS_GRAPH_COLOR, theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE)); - putRawData(AnalysisKeys.TPS_LOW_COLOR, theme.getValue(ThemeVal.GRAPH_TPS_LOW)); - putRawData(AnalysisKeys.TPS_MEDIUM_COLOR, theme.getValue(ThemeVal.GRAPH_TPS_MED)); - putRawData(AnalysisKeys.TPS_HIGH_COLOR, theme.getValue(ThemeVal.GRAPH_TPS_HIGH)); - putRawData(AnalysisKeys.WORLD_MAP_LOW_COLOR, theme.getValue(ThemeVal.WORLD_MAP_LOW)); - putRawData(AnalysisKeys.WORLD_MAP_HIGH_COLOR, theme.getValue(ThemeVal.WORLD_MAP_HIGH)); - putRawData(AnalysisKeys.WORLD_PIE_COLORS, theme.getValue(ThemeVal.GRAPH_WORLD_PIE)); - putRawData(AnalysisKeys.AVG_PING_COLOR, theme.getValue(ThemeVal.GRAPH_AVG_PING)); - putRawData(AnalysisKeys.MAX_PING_COLOR, theme.getValue(ThemeVal.GRAPH_MAX_PING)); - putRawData(AnalysisKeys.MIN_PING_COLOR, theme.getValue(ThemeVal.GRAPH_MIN_PING)); - } - - private void addPlayerSuppliers() { - putCachingSupplier(AnalysisKeys.PLAYER_NAMES, () -> serverContainer.getValue(ServerKeys.PLAYERS).orElse(new ArrayList<>()) - .stream().collect(Collectors.toMap( - p -> p.getUnsafe(PlayerKeys.UUID), p -> p.getValue(PlayerKeys.NAME).orElse("?")) - ) - ); - putSupplier(AnalysisKeys.PLAYERS_TOTAL, () -> serverContainer.getValue(ServerKeys.PLAYER_COUNT).orElse(0)); - putSupplier(AnalysisKeys.PLAYERS_LAST_PEAK, () -> - serverContainer.getValue(ServerKeys.RECENT_PEAK_PLAYERS) - .map(dateObj -> Integer.toString(dateObj.getValue())).orElse("-") - ); - putSupplier(AnalysisKeys.PLAYERS_ALL_TIME_PEAK, () -> - serverContainer.getValue(ServerKeys.ALL_TIME_PEAK_PLAYERS) - .map(dateObj -> Integer.toString(dateObj.getValue())).orElse("-") - ); - putSupplier(AnalysisKeys.LAST_PEAK_TIME_F, () -> - serverContainer.getValue(ServerKeys.RECENT_PEAK_PLAYERS) - .map(dateObj -> formatters.year().apply(dateObj)).orElse("-") - ); - putSupplier(AnalysisKeys.ALL_TIME_PEAK_TIME_F, () -> - serverContainer.getValue(ServerKeys.ALL_TIME_PEAK_PLAYERS) - .map(dateObj -> formatters.year().apply(dateObj)).orElse("-") - ); - putSupplier(AnalysisKeys.OPERATORS, () -> serverContainer.getValue(ServerKeys.OPERATORS).map(List::size).orElse(0)); - putSupplier(AnalysisKeys.PLAYERS_TABLE, () -> - tables.playerTableForServerPage(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).all()).parseHtml() - ); - putSupplier(AnalysisKeys.PING_TABLE, () -> - tables.pingTable( - getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) - .getPingPerCountry(serverContainer.getUnsafe(ServerKeys.SERVER_UUID)) - ).parseHtml() - ); - - newAndUniquePlayerCounts(); - } - - private void newAndUniquePlayerCounts() { - Key newDay = new Key<>(PlayersMutator.class, "NEW_DAY"); - Key newWeek = new Key<>(PlayersMutator.class, "NEW_WEEK"); - Key newMonth = new Key<>(PlayersMutator.class, "NEW_MONTH"); - Key uniqueDay = new Key<>(PlayersMutator.class, "UNIQUE_DAY"); - Key uniqueWeek = new Key<>(PlayersMutator.class, "UNIQUE_WEEK"); - Key uniqueMonth = new Key<>(PlayersMutator.class, "UNIQUE_MONTH"); - putCachingSupplier(newDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) - .filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(newWeek, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) - .filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(newMonth, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) - .filterRegisteredBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(uniqueDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) - .filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(uniqueWeek, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) - .filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(uniqueMonth, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR) - .filterPlayedBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - - putSupplier(AnalysisKeys.PLAYERS_NEW_DAY, () -> getUnsafe(newDay).count()); - putSupplier(AnalysisKeys.PLAYERS_NEW_WEEK, () -> getUnsafe(newWeek).count()); - putSupplier(AnalysisKeys.PLAYERS_NEW_MONTH, () -> getUnsafe(newMonth).count()); - putSupplier(AnalysisKeys.PLAYERS_DAY, () -> getUnsafe(uniqueDay).count()); - putSupplier(AnalysisKeys.PLAYERS_WEEK, () -> getUnsafe(uniqueWeek).count()); - putSupplier(AnalysisKeys.PLAYERS_MONTH, () -> getUnsafe(uniqueMonth).count()); - putSupplier(AnalysisKeys.AVG_PLAYERS_NEW, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).averageNewPerDay(timeZone)); - putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_DAY, () -> getUnsafe(newDay).averageNewPerDay(timeZone)); - putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_WEEK, () -> getUnsafe(newWeek).averageNewPerDay(timeZone)); - putSupplier(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, () -> getUnsafe(newMonth).averageNewPerDay(timeZone)); - - putSupplier(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).uniqueJoinsPerDay(timeZone)); - putSupplier(AnalysisKeys.NEW_PLAYERS_PER_DAY, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).newPerDay(timeZone)); - putSupplier(AnalysisKeys.UNIQUE_PLAYERS_SERIES, () -> graphs.line().lineGraph( - MutatorFunctions.toPointsWithRemovedOffset(getUnsafe(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY), timeZone)).toHighChartsSeries() - ); - putSupplier(AnalysisKeys.NEW_PLAYERS_SERIES, () -> graphs.line().lineGraph( - MutatorFunctions.toPointsWithRemovedOffset(getUnsafe(AnalysisKeys.NEW_PLAYERS_PER_DAY), timeZone)).toHighChartsSeries() - ); - - Key retentionDay = new Key<>(Integer.class, "RETENTION_DAY"); - // compareAndFindThoseLikelyToBeRetained can throw exception. - putCachingSupplier(retentionDay, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).compareAndFindThoseLikelyToBeRetained( - getUnsafe(newDay).all(), getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), - getUnsafe(AnalysisKeys.PLAYERS_ONLINE_RESOLVER), - config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), - config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD) - ).count() - ); - putSupplier(AnalysisKeys.PLAYERS_RETAINED_DAY, () -> { - try { - return getUnsafe(retentionDay); - } catch (IllegalStateException noPlayersAfterDateFiltering) { - return 0; - } - }); - putCachingSupplier(AnalysisKeys.PLAYERS_RETAINED_WEEK, () -> - getUnsafe(newWeek).filterRetained( - getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), - getUnsafe(AnalysisKeys.ANALYSIS_TIME) - ).count() - ); - putCachingSupplier(AnalysisKeys.PLAYERS_RETAINED_MONTH, () -> - getUnsafe(newMonth).filterRetained( - getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), - getUnsafe(AnalysisKeys.ANALYSIS_TIME) - ).count() - ); - putSupplier(AnalysisKeys.PLAYERS_RETAINED_DAY_PERC, () -> { - try { - Integer playersNewDay = getUnsafe(AnalysisKeys.PLAYERS_NEW_DAY); - return playersNewDay != 0 - ? formatters.percentage().apply(1.0 * getUnsafe(retentionDay) / playersNewDay) - : "-"; - } catch (IllegalStateException noPlayersAfterDateFiltering) { - return "Not enough data"; - } - }); - putSupplier(AnalysisKeys.PLAYERS_RETAINED_WEEK_PERC, () -> { - Integer playersNewWeek = getUnsafe(AnalysisKeys.PLAYERS_NEW_WEEK); - return playersNewWeek != 0 ? formatters.percentage().apply(1.0 * getUnsafe(AnalysisKeys.PLAYERS_RETAINED_WEEK) / playersNewWeek) : "-"; - } - ); - putSupplier(AnalysisKeys.PLAYERS_RETAINED_MONTH_PERC, () -> { - Integer playersNewMonth = getUnsafe(AnalysisKeys.PLAYERS_NEW_MONTH); - return playersNewMonth != 0 - ? formatters.percentage().apply(1.0 * getUnsafe(AnalysisKeys.PLAYERS_RETAINED_MONTH) / playersNewMonth) - : "-"; - } - ); - } - - private void addSessionSuppliers() { - Key sessionAccordion = new Key<>(SessionAccordion.class, "SESSION_ACCORDION"); - putCachingSupplier(sessionAccordion, () -> accordions.serverSessionAccordion( - getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).all(), - () -> Collections.singletonMap( - serverContainer.getUnsafe(ServerKeys.SERVER_UUID), - serverContainer.getValue(ServerKeys.NAME).orElse("This server") - ), - () -> getUnsafe(AnalysisKeys.PLAYER_NAMES) - )); - putSupplier(AnalysisKeys.SESSION_ACCORDION_HTML, () -> getUnsafe(sessionAccordion).toHtml()); - putSupplier(AnalysisKeys.SESSION_ACCORDION_FUNCTIONS, () -> getUnsafe(sessionAccordion).toViewScript()); - - putSupplier(AnalysisKeys.RECENT_LOGINS, () -> new RecentLoginList( - serverContainer.getValue(ServerKeys.PLAYERS).orElse(new ArrayList<>()), - formatters.secondLong()).toHtml() - ); - putSupplier(AnalysisKeys.SESSION_TABLE, () -> tables.serverSessionTable( - getUnsafe(AnalysisKeys.PLAYER_NAMES), getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).all()).parseHtml() - ); - - putSupplier(AnalysisKeys.AVERAGE_SESSION_LENGTH_F, - () -> formatters.timeAmount().apply(getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toAverageSessionLength()) - ); - putSupplier(AnalysisKeys.SESSION_COUNT, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).count()); - putSupplier(AnalysisKeys.PLAYTIME_TOTAL, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toPlaytime()); - putSupplier(AnalysisKeys.DEATHS, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toDeathCount()); - putSupplier(AnalysisKeys.MOB_KILL_COUNT, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toMobKillCount()); - putSupplier(AnalysisKeys.PLAYER_KILL_COUNT, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toPlayerKillCount()); - putSupplier(AnalysisKeys.PLAYTIME_F, - () -> formatters.timeAmount().apply(getUnsafe(AnalysisKeys.PLAYTIME_TOTAL)) - ); - putSupplier(AnalysisKeys.AVERAGE_PLAYTIME_F, () -> { - long players = getUnsafe(AnalysisKeys.PLAYERS_TOTAL); - return players != 0 - ? formatters.timeAmount().apply(getUnsafe(AnalysisKeys.PLAYTIME_TOTAL) / players) - : "-"; - } - ); - putSupplier(AnalysisKeys.AVERAGE_SESSION_LENGTH_F, - () -> formatters.timeAmount().apply(getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toAverageSessionLength()) - ); - - Key sessionsDay = new Key<>(SessionsMutator.class, "SESSIONS_DAY"); - Key sessionsWeek = new Key<>(SessionsMutator.class, "SESSIONS_WEEK"); - Key sessionsMonth = new Key<>(SessionsMutator.class, "SESSIONS_MONTH"); - putCachingSupplier(sessionsDay, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) - .filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(sessionsWeek, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) - .filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(sessionsMonth, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR) - .filterSessionsBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - - putSupplier(AnalysisKeys.PUNCHCARD_SERIES, () -> graphs.special().punchCard(getUnsafe(sessionsMonth).all()).toHighChartsSeries()); - putSupplier(AnalysisKeys.AVG_PLAYERS, () -> getUnsafe(AnalysisKeys.SESSIONS_MUTATOR).toAverageUniqueJoinsPerDay(timeZone)); - putSupplier(AnalysisKeys.AVG_PLAYERS_DAY, () -> getUnsafe(sessionsDay).toAverageUniqueJoinsPerDay(timeZone)); - putSupplier(AnalysisKeys.AVG_PLAYERS_WEEK, () -> getUnsafe(sessionsWeek).toAverageUniqueJoinsPerDay(timeZone)); - putSupplier(AnalysisKeys.AVG_PLAYERS_MONTH, () -> getUnsafe(sessionsMonth).toAverageUniqueJoinsPerDay(timeZone)); - } - - private void addGraphSuppliers() { - Key worldPie = new Key<>(WorldPie.class, "WORLD_PIE"); - putCachingSupplier(worldPie, () -> graphs.pie().worldPie( - serverContainer.getValue(ServerKeys.WORLD_TIMES).orElse(new WorldTimes()) - )); - putSupplier(AnalysisKeys.WORLD_PIE_SERIES, () -> getUnsafe(worldPie).toHighChartsSeries()); - putSupplier(AnalysisKeys.GM_PIE_SERIES, () -> getUnsafe(worldPie).toHighChartsDrilldown()); - putSupplier(AnalysisKeys.PLAYERS_ONLINE_SERIES, () -> - graphs.line().playersOnlineGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries() - ); - putSupplier(AnalysisKeys.TPS_SERIES, () -> graphs.line().tpsGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries()); - putSupplier(AnalysisKeys.CPU_SERIES, () -> graphs.line().cpuGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries()); - putSupplier(AnalysisKeys.RAM_SERIES, () -> graphs.line().ramGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries()); - putSupplier(AnalysisKeys.DISK_SERIES, () -> graphs.line().diskGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries()); - putSupplier(AnalysisKeys.ENTITY_SERIES, () -> graphs.line().entityGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries()); - putSupplier(AnalysisKeys.CHUNK_SERIES, () -> graphs.line().chunkGraph(getUnsafe(AnalysisKeys.TPS_MUTATOR)).toHighChartsSeries()); - putSupplier(AnalysisKeys.WORLD_MAP_SERIES, () -> - graphs.special().worldMap(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)).toHighChartsSeries() - ); - Key geolocationBarChart = new Key<>(BarGraph.class, "GEOLOCATION_BAR_GRAPH"); - putCachingSupplier(geolocationBarChart, () -> graphs.bar().geolocationBarGraph(getUnsafe(AnalysisKeys.PLAYERS_MUTATOR))); - putSupplier(AnalysisKeys.COUNTRY_CATEGORIES, () -> getUnsafe(geolocationBarChart).toHighChartsCategories()); - putSupplier(AnalysisKeys.COUNTRY_SERIES, () -> getUnsafe(geolocationBarChart).toHighChartsSeries()); - - Key pingGraph = new Key<>(PingGraph.class, "PING_GRAPH"); - putCachingSupplier(pingGraph, () -> - graphs.line().pingGraph(PingMutator.forContainer(serverContainer).mutateToByMinutePings().all()) - ); - putSupplier(AnalysisKeys.AVG_PING_SERIES, () -> getUnsafe(pingGraph).toAvgSeries()); - putSupplier(AnalysisKeys.MAX_PING_SERIES, () -> getUnsafe(pingGraph).toMaxSeries()); - putSupplier(AnalysisKeys.MIN_PING_SERIES, () -> getUnsafe(pingGraph).toMinSeries()); - - putSupplier(AnalysisKeys.CALENDAR_SERIES, () -> graphs.calendar().serverCalendar( - getUnsafe(AnalysisKeys.PLAYERS_MUTATOR), - getUnsafe(AnalysisKeys.UNIQUE_PLAYERS_PER_DAY), - getUnsafe(AnalysisKeys.NEW_PLAYERS_PER_DAY) - ).toCalendarSeries()); - - putCachingSupplier(AnalysisKeys.ACTIVITY_DATA, () -> getUnsafe(AnalysisKeys.PLAYERS_MUTATOR).toActivityDataMap(getUnsafe(AnalysisKeys.ANALYSIS_TIME), config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD))); - Key activityStackGraph = new Key<>(StackGraph.class, "ACTIVITY_STACK_GRAPH"); - putCachingSupplier(activityStackGraph, () -> graphs.stack().activityStackGraph(getUnsafe(AnalysisKeys.ACTIVITY_DATA))); - putSupplier(AnalysisKeys.ACTIVITY_STACK_CATEGORIES, () -> getUnsafe(activityStackGraph).toHighChartsLabels()); - putSupplier(AnalysisKeys.ACTIVITY_STACK_SERIES, () -> getUnsafe(activityStackGraph).toHighChartsSeries()); - putSupplier(AnalysisKeys.ACTIVITY_PIE_SERIES, () -> graphs.pie().activityPie( - getUnsafe(AnalysisKeys.ACTIVITY_DATA).get(getUnsafe(AnalysisKeys.ANALYSIS_TIME))).toHighChartsSeries() - ); - putSupplier(AnalysisKeys.PLAYERS_REGULAR, () -> { - Map> activityNow = getUnsafe(AnalysisKeys.ACTIVITY_DATA) - .floorEntry(getUnsafe(AnalysisKeys.ANALYSIS_TIME)).getValue(); - Set veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>()); - Set activeNow = activityNow.getOrDefault("Active", new HashSet<>()); - Set regularNow = activityNow.getOrDefault("Regular", new HashSet<>()); - return veryActiveNow.size() + activeNow.size() + regularNow.size(); - }); - } - - private void addTPSAverageSuppliers() { - Key tpsMonth = new Key<>(TPSMutator.class, "TPS_MONTH"); - Key tpsWeek = new Key<>(TPSMutator.class, "TPS_WEEK"); - Key tpsDay = new Key<>(TPSMutator.class, "TPS_DAY"); - - putCachingSupplier(tpsMonth, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) - .filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(tpsWeek, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) - .filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_WEEK_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - putCachingSupplier(tpsDay, () -> getUnsafe(AnalysisKeys.TPS_MUTATOR) - .filterDataBetween(getUnsafe(AnalysisKeys.ANALYSIS_TIME_DAY_AGO), getUnsafe(AnalysisKeys.ANALYSIS_TIME)) - ); - - putCachingSupplier(AnalysisKeys.PLAYERS_ONLINE_RESOLVER, () -> new PlayersOnlineResolver(getUnsafe(AnalysisKeys.TPS_MUTATOR))); - - int threshold = config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED); - - putSupplier(AnalysisKeys.TPS_SPIKE_MONTH, () -> getUnsafe(tpsMonth).lowTpsSpikeCount(threshold)); - putSupplier(AnalysisKeys.AVG_TPS_MONTH, () -> getUnsafe(tpsMonth).averageTPS()); - putSupplier(AnalysisKeys.AVG_CPU_MONTH, () -> getUnsafe(tpsMonth).averageCPU()); - putSupplier(AnalysisKeys.AVG_RAM_MONTH, () -> getUnsafe(tpsMonth).averageRAM()); - putSupplier(AnalysisKeys.AVG_ENTITY_MONTH, () -> getUnsafe(tpsMonth).averageEntities()); - putSupplier(AnalysisKeys.AVG_CHUNK_MONTH, () -> getUnsafe(tpsMonth).averageChunks()); - putSupplier(AnalysisKeys.AVG_FREE_DISK_MONTH, () -> getUnsafe(tpsMonth).averageFreeDisk()); - putSupplier(AnalysisKeys.MAX_FREE_DISK_MONTH, () -> getUnsafe(tpsMonth).maxFreeDisk()); - putSupplier(AnalysisKeys.MIN_FREE_DISK_MONTH, () -> getUnsafe(tpsMonth).minFreeDisk()); - - putSupplier(AnalysisKeys.TPS_SPIKE_WEEK, () -> getUnsafe(tpsWeek).lowTpsSpikeCount(threshold)); - putSupplier(AnalysisKeys.AVG_TPS_WEEK, () -> getUnsafe(tpsWeek).averageTPS()); - putSupplier(AnalysisKeys.AVG_CPU_WEEK, () -> getUnsafe(tpsWeek).averageCPU()); - putSupplier(AnalysisKeys.AVG_RAM_WEEK, () -> getUnsafe(tpsWeek).averageRAM()); - putSupplier(AnalysisKeys.AVG_ENTITY_WEEK, () -> getUnsafe(tpsWeek).averageEntities()); - putSupplier(AnalysisKeys.AVG_CHUNK_WEEK, () -> getUnsafe(tpsWeek).averageChunks()); - putSupplier(AnalysisKeys.AVG_FREE_DISK_WEEK, () -> getUnsafe(tpsWeek).averageFreeDisk()); - putSupplier(AnalysisKeys.MAX_FREE_DISK_WEEK, () -> getUnsafe(tpsWeek).maxFreeDisk()); - putSupplier(AnalysisKeys.MIN_FREE_DISK_WEEK, () -> getUnsafe(tpsWeek).minFreeDisk()); - - putSupplier(AnalysisKeys.TPS_SPIKE_DAY, () -> getUnsafe(tpsDay).lowTpsSpikeCount(threshold)); - putSupplier(AnalysisKeys.AVG_TPS_DAY, () -> getUnsafe(tpsDay).averageTPS()); - putSupplier(AnalysisKeys.AVG_CPU_DAY, () -> getUnsafe(tpsDay).averageCPU()); - putSupplier(AnalysisKeys.AVG_RAM_DAY, () -> getUnsafe(tpsDay).averageRAM()); - putSupplier(AnalysisKeys.AVG_ENTITY_DAY, () -> getUnsafe(tpsDay).averageEntities()); - putSupplier(AnalysisKeys.AVG_CHUNK_DAY, () -> getUnsafe(tpsDay).averageChunks()); - putSupplier(AnalysisKeys.AVG_FREE_DISK_DAY, () -> getUnsafe(tpsDay).averageFreeDisk()); - putSupplier(AnalysisKeys.MAX_FREE_DISK_DAY, () -> getUnsafe(tpsDay).maxFreeDisk()); - putSupplier(AnalysisKeys.MIN_FREE_DISK_DAY, () -> getUnsafe(tpsDay).minFreeDisk()); - } - - private void addCommandSuppliers() { - putSupplier(AnalysisKeys.COMMAND_USAGE_TABLE, () -> tables.commandUseTable(serverContainer).parseHtml()); - putSupplier(AnalysisKeys.COMMAND_COUNT_UNIQUE, () -> serverContainer.getValue(ServerKeys.COMMAND_USAGE).map(Map::size).orElse(0)); - putSupplier(AnalysisKeys.COMMAND_COUNT, () -> CommandUseMutator.forContainer(serverContainer).commandUsageCount()); - } - - private void addServerHealth() { - Key healthInformation = new Key<>(HealthInformation.class, "HEALTH_INFORMATION"); - putCachingSupplier(healthInformation, () -> new HealthInformation( - this, - locale, - config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED), - config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), - config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD), - formatters.timeAmount(), formatters.decimals(), formatters.percentage() - )); - putSupplier(AnalysisKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth()); - putSupplier(AnalysisKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml()); - } - - private void addPluginSuppliers() { - // TODO Refactor into a system that supports running the analysis on Bungee - Key navAndTabs = new Key<>(new Type() {}, "NAV_AND_TABS"); - Key pluginTabs = new Key<>(AnalysisPluginTabs.class, "PLUGIN_TABS"); - putCachingSupplier(navAndTabs, () -> pluginsTabContentCreator.createContent( - this, getValue(AnalysisKeys.PLAYERS_MUTATOR).orElse(new PlayersMutator(new ArrayList<>())) - )); - putCachingSupplier(pluginTabs, () -> new AnalysisPluginTabs(serverContainer.getValue(ServerKeys.EXTENSION_DATA).orElse(new ArrayList<>()), formatters)); - putSupplier(AnalysisKeys.PLUGINS_TAB_NAV, () -> getUnsafe(pluginTabs).getNav() + getUnsafe(navAndTabs)[0]); - putSupplier(AnalysisKeys.PLUGINS_TAB, () -> getUnsafe(pluginTabs).getTabs() + getUnsafe(navAndTabs)[1]); - } - - @Singleton - public static class Factory { - - private final String version; - private final PlanConfig config; - private final Locale locale; - private final Theme theme; - private final ServerProperties serverProperties; - private final Formatters formatters; - private final Graphs graphs; - private final HtmlTables tables; - private final Accordions accordions; - private final AnalysisPluginsTabContentCreator pluginsTabContentCreator; - - @Inject - public Factory( - @Named("currentVersion") String version, - PlanConfig config, - Locale locale, - Theme theme, - ServerProperties serverProperties, - Formatters formatters, - Graphs graphs, - HtmlTables tables, - Accordions accordions, - AnalysisPluginsTabContentCreator pluginsTabContentCreator - ) { - this.version = version; - this.config = config; - this.locale = locale; - this.theme = theme; - this.serverProperties = serverProperties; - this.formatters = formatters; - this.graphs = graphs; - this.tables = tables; - this.accordions = accordions; - this.pluginsTabContentCreator = pluginsTabContentCreator; - } - - public AnalysisContainer forServerContainer(ServerContainer serverContainer) { - return new AnalysisContainer( - serverContainer, - version, - locale, - config, - theme, - serverProperties, - formatters, - graphs, - tables, - accordions, - pluginsTabContentCreator - ); - } - } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java deleted file mode 100644 index 8ce401177..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/NetworkContainer.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.store.containers; - -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.keys.NetworkKeys; -import com.djrapitops.plan.data.store.keys.ServerKeys; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.data.store.mutators.TPSMutator; -import com.djrapitops.plan.data.store.mutators.health.NetworkHealthInformation; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.ServerAggregateQueries; -import com.djrapitops.plan.db.access.queries.objects.TPSQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ProxySettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.settings.theme.ThemeVal; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.graphs.Graphs; -import com.djrapitops.plan.utilities.html.graphs.bar.BarGraph; -import com.djrapitops.plan.utilities.html.graphs.stack.StackGraph; -import com.djrapitops.plan.utilities.html.structure.NetworkServerBox; -import com.djrapitops.plugin.api.Check; -import com.djrapitops.plugin.api.TimeAmount; -import dagger.Lazy; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; -import java.util.*; -import java.util.concurrent.TimeUnit; - -/** - * DataContainer for the whole network. - * - * @author Rsl1122 - * @see com.djrapitops.plan.data.store.keys.NetworkKeys for Key objects - * @see com.djrapitops.plan.data.store.PlaceholderKey for placeholder information - */ -public class NetworkContainer extends DynamicDataContainer { - - private final ServerContainer bungeeContainer; - - private final String version; - private final PlanConfig config; - private final Locale locale; - private final Theme theme; - private final ServerProperties serverProperties; - private final Formatters formatters; - private final Graphs graphs; - private final Database database; - - public NetworkContainer( - ServerContainer bungeeContainer, - String version, - PlanConfig config, - Locale locale, - Theme theme, - DBSystem dbSystem, - ServerProperties serverProperties, - Formatters formatters, - Graphs graphs - ) { - this.bungeeContainer = bungeeContainer; - this.version = version; - this.config = config; - this.locale = locale; - this.theme = theme; - this.database = dbSystem.getDatabase(); - this.serverProperties = serverProperties; - this.formatters = formatters; - this.graphs = graphs; - - putCachingSupplier(NetworkKeys.PLAYERS_MUTATOR, () -> PlayersMutator.forContainer(bungeeContainer)); - - addConstants(); - addServerBoxes(); - addPlayerInformation(); - addNetworkHealth(); - } - - private void addServerBoxes() { - putSupplier(NetworkKeys.NETWORK_PLAYER_ONLINE_DATA, () -> database.query(TPSQueries.fetchPlayerOnlineDataOfServers( - getValue(NetworkKeys.BUKKIT_SERVERS).orElse(new ArrayList<>())) - )); - putSupplier(NetworkKeys.SERVERS_TAB, () -> { - StringBuilder serverBoxes = new StringBuilder(); - Map> playersOnlineData = getValue(NetworkKeys.NETWORK_PLAYER_ONLINE_DATA).orElse(new HashMap<>()); - Map userCounts = database.query(ServerAggregateQueries.serverUserCounts()); - Collection servers = getValue(NetworkKeys.BUKKIT_SERVERS).orElse(new ArrayList<>()); - servers.stream() - .sorted((one, two) -> String.CASE_INSENSITIVE_ORDER.compare(one.getName(), two.getName())) - .forEach(server -> { - TPSMutator tpsMutator = new TPSMutator(playersOnlineData.getOrDefault(server.getId(), new ArrayList<>())); - int registered = userCounts.getOrDefault(server.getUuid(), 0); - NetworkServerBox serverBox = new NetworkServerBox(server, registered, tpsMutator, graphs); - serverBoxes.append(serverBox.toHtml()); - }); - if (servers.isEmpty()) { - serverBoxes.append("

" + - "
" + - "
" + - "
" + - "
" + - "
" + - "

No Servers

" + - "
" + - "

No Bukkit/Sponge servers connected to Plan.

" + - "
"); - } - return serverBoxes.toString(); - }); - } - - private void addNetworkHealth() { - Key healthInformation = new Key<>(NetworkHealthInformation.class, "HEALTH_INFORMATION"); - putCachingSupplier(healthInformation, () -> new NetworkHealthInformation( - this, - locale, - config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), - config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD), - formatters.timeAmount(), formatters.decimals(), formatters.percentage(), - config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT") - )); - putCachingSupplier(NetworkKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth()); - putCachingSupplier(NetworkKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml()); - } - - private void addConstants() { - long now = System.currentTimeMillis(); - putRawData(NetworkKeys.REFRESH_TIME, now); - putRawData(NetworkKeys.REFRESH_TIME_DAY_AGO, getUnsafe(NetworkKeys.REFRESH_TIME) - TimeUnit.DAYS.toMillis(1L)); - putRawData(NetworkKeys.REFRESH_TIME_WEEK_AGO, getUnsafe(NetworkKeys.REFRESH_TIME) - TimeAmount.WEEK.toMillis(1L)); - putRawData(NetworkKeys.REFRESH_TIME_MONTH_AGO, getUnsafe(NetworkKeys.REFRESH_TIME) - TimeAmount.MONTH.toMillis(1L)); - putSupplier(NetworkKeys.REFRESH_TIME_F, () -> formatters.secondLong().apply(getUnsafe(NetworkKeys.REFRESH_TIME))); - - putRawData(NetworkKeys.VERSION, version); - putSupplier(NetworkKeys.TIME_ZONE, config::getTimeZoneOffsetHours); - - putCachingSupplier(NetworkKeys.NETWORK_NAME, () -> - Check.isBungeeAvailable() || Check.isVelocityAvailable() ? - config.get(ProxySettings.NETWORK_NAME) : - bungeeContainer.getValue(ServerKeys.NAME).orElse("Plan") - ); - putSupplier(NetworkKeys.PLAYERS_ONLINE, serverProperties::getOnlinePlayers); - putRawData(NetworkKeys.WORLD_MAP_LOW_COLOR, theme.getValue(ThemeVal.WORLD_MAP_LOW)); - putRawData(NetworkKeys.WORLD_MAP_HIGH_COLOR, theme.getValue(ThemeVal.WORLD_MAP_HIGH)); - putRawData(NetworkKeys.PLAYERS_GRAPH_COLOR, theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE)); - } - - private void addPlayerInformation() { - putSupplier(NetworkKeys.PLAYERS_TOTAL, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR).count()); - putSupplier(NetworkKeys.WORLD_MAP_SERIES, () -> - graphs.special().worldMap(database.query(ServerAggregateQueries.networkGeolocationCounts())).toHighChartsSeries() - ); - Key geolocationBarChart = new Key<>(BarGraph.class, "GEOLOCATION_BAR_GRAPH"); - putSupplier(geolocationBarChart, () -> graphs.bar().geolocationBarGraph(getUnsafe(NetworkKeys.PLAYERS_MUTATOR))); - putSupplier(NetworkKeys.COUNTRY_CATEGORIES, () -> getUnsafe(geolocationBarChart).toHighChartsCategories()); - putSupplier(NetworkKeys.COUNTRY_SERIES, () -> getUnsafe(geolocationBarChart).toHighChartsSeries()); - - putSupplier(NetworkKeys.PLAYERS_ONLINE_SERIES, () -> - graphs.line().playersOnlineGraph(TPSMutator.forContainer(bungeeContainer)).toHighChartsSeries() - ); - Key activityStackGraph = new Key<>(StackGraph.class, "ACTIVITY_STACK_GRAPH"); - putSupplier(NetworkKeys.ACTIVITY_DATA, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR).toActivityDataMap(getUnsafe(NetworkKeys.REFRESH_TIME), config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD))); - putSupplier(activityStackGraph, () -> graphs.stack().activityStackGraph(getUnsafe(NetworkKeys.ACTIVITY_DATA))); - putSupplier(NetworkKeys.ACTIVITY_STACK_CATEGORIES, () -> getUnsafe(activityStackGraph).toHighChartsLabels()); - putSupplier(NetworkKeys.ACTIVITY_STACK_SERIES, () -> getUnsafe(activityStackGraph).toHighChartsSeries()); - putSupplier(NetworkKeys.ACTIVITY_PIE_SERIES, () -> graphs.pie().activityPie( - getUnsafe(NetworkKeys.ACTIVITY_DATA).get(getUnsafe(NetworkKeys.REFRESH_TIME))).toHighChartsSeries() - ); - - putSupplier(NetworkKeys.ALL_TIME_PEAK_TIME_F, () -> - bungeeContainer.getValue(ServerKeys.ALL_TIME_PEAK_PLAYERS).map(formatters.year()).orElse("No data") - ); - putSupplier(NetworkKeys.RECENT_PEAK_TIME_F, () -> - bungeeContainer.getValue(ServerKeys.RECENT_PEAK_PLAYERS).map(formatters.year()).orElse("No data") - ); - putSupplier(NetworkKeys.PLAYERS_ALL_TIME_PEAK, () -> - bungeeContainer.getValue(ServerKeys.ALL_TIME_PEAK_PLAYERS).map(dateObj -> "" + dateObj.getValue()).orElse("-") - ); - putSupplier(NetworkKeys.PLAYERS_RECENT_PEAK, () -> - bungeeContainer.getValue(ServerKeys.RECENT_PEAK_PLAYERS).map(dateObj -> "" + dateObj.getValue()).orElse("-") - ); - - addPlayerCounts(); - } - - private void addPlayerCounts() { - Key newDay = new Key<>(PlayersMutator.class, "NEW_DAY"); - Key newWeek = new Key<>(PlayersMutator.class, "NEW_WEEK"); - Key newMonth = new Key<>(PlayersMutator.class, "NEW_MONTH"); - Key uniqueDay = new Key<>(PlayersMutator.class, "UNIQUE_DAY"); - Key uniqueWeek = new Key<>(PlayersMutator.class, "UNIQUE_WEEK"); - Key uniqueMonth = new Key<>(PlayersMutator.class, "UNIQUE_MONTH"); - putCachingSupplier(newDay, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) - .filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_DAY_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) - ); - putCachingSupplier(newWeek, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) - .filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_WEEK_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) - ); - putCachingSupplier(newMonth, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) - .filterRegisteredBetween(getUnsafe(NetworkKeys.REFRESH_TIME_MONTH_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) - ); - putCachingSupplier(uniqueDay, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) - .filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_DAY_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) - ); - putCachingSupplier(uniqueWeek, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) - .filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_WEEK_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) - ); - putCachingSupplier(uniqueMonth, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR) - .filterPlayedBetween(getUnsafe(NetworkKeys.REFRESH_TIME_MONTH_AGO), getUnsafe(NetworkKeys.REFRESH_TIME)) - ); - - putSupplier(NetworkKeys.PLAYERS_NEW_DAY, () -> getUnsafe(newDay).count()); - putSupplier(NetworkKeys.PLAYERS_NEW_WEEK, () -> getUnsafe(newWeek).count()); - putSupplier(NetworkKeys.PLAYERS_NEW_MONTH, () -> getUnsafe(newMonth).count()); - putSupplier(NetworkKeys.PLAYERS_DAY, () -> getUnsafe(uniqueDay).count()); - putSupplier(NetworkKeys.PLAYERS_WEEK, () -> getUnsafe(uniqueWeek).count()); - putSupplier(NetworkKeys.PLAYERS_MONTH, () -> getUnsafe(uniqueMonth).count()); - } - - public ServerContainer getBungeeContainer() { - return bungeeContainer; - } - - @Singleton - public static class Factory { - - private final Lazy version; - private final Lazy config; - private final Lazy locale; - private final Lazy theme; - private final Lazy dbSystem; - private final Lazy serverProperties; - private final Lazy formatters; - private final Lazy graphs; - - @Inject - public Factory( - @Named("currentVersion") Lazy version, - Lazy config, - Lazy locale, - Lazy theme, - Lazy dbSystem, - Lazy serverProperties, - Lazy formatters, - Lazy graphs - ) { - this.version = version; - this.config = config; - this.locale = locale; - this.theme = theme; - this.dbSystem = dbSystem; - this.serverProperties = serverProperties; - this.formatters = formatters; - this.graphs = graphs; - } - - public NetworkContainer forBungeeContainer(ServerContainer bungeeContainer) { - return new NetworkContainer( - bungeeContainer, - version.get(), - config.get(), - locale.get(), - theme.get(), - dbSystem.get(), - serverProperties.get(), - formatters.get(), - graphs.get() - ); - } - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/NetworkKeys.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/NetworkKeys.java deleted file mode 100644 index 50421cddd..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/NetworkKeys.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.store.keys; - -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.PlaceholderKey; -import com.djrapitops.plan.data.store.Type; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.system.info.server.Server; - -import java.util.*; - -/** - * Key objects for {@link com.djrapitops.plan.data.store.containers.NetworkContainer}. - * - * @author Rsl1122 - * @see com.djrapitops.plan.data.store.containers.NetworkContainer for DataContainer. - */ -public class NetworkKeys { - - public static final PlaceholderKey VERSION = CommonPlaceholderKeys.VERSION; - public static final PlaceholderKey NETWORK_NAME = new PlaceholderKey<>(String.class, "networkName"); - public static final PlaceholderKey TIME_ZONE = CommonPlaceholderKeys.TIME_ZONE; - public static final PlaceholderKey PLAYERS_ONLINE = CommonPlaceholderKeys.PLAYERS_ONLINE; - public static final PlaceholderKey PLAYERS_TOTAL = CommonPlaceholderKeys.PLAYERS_TOTAL; - public static final PlaceholderKey PLAYERS_GRAPH_COLOR = CommonPlaceholderKeys.PLAYERS_GRAPH_COLOR; - public static final PlaceholderKey WORLD_MAP_HIGH_COLOR = CommonPlaceholderKeys.WORLD_MAP_HIGH_COLOR; - public static final PlaceholderKey WORLD_MAP_LOW_COLOR = CommonPlaceholderKeys.WORLD_MAP_LOW_COLOR; - - public static final PlaceholderKey REFRESH_TIME_F = CommonPlaceholderKeys.REFRESH_TIME_F; - public static final PlaceholderKey RECENT_PEAK_TIME_F = CommonPlaceholderKeys.LAST_PEAK_TIME_F; - public static final PlaceholderKey ALL_TIME_PEAK_TIME_F = CommonPlaceholderKeys.ALL_TIME_PEAK_TIME_F; - public static final PlaceholderKey PLAYERS_RECENT_PEAK = CommonPlaceholderKeys.PLAYERS_LAST_PEAK; - public static final PlaceholderKey PLAYERS_ALL_TIME_PEAK = CommonPlaceholderKeys.PLAYERS_ALL_TIME_PEAK; - public static final PlaceholderKey PLAYERS_DAY = CommonPlaceholderKeys.PLAYERS_DAY; - public static final PlaceholderKey PLAYERS_WEEK = CommonPlaceholderKeys.PLAYERS_WEEK; - public static final PlaceholderKey PLAYERS_MONTH = CommonPlaceholderKeys.PLAYERS_MONTH; - public static final PlaceholderKey PLAYERS_NEW_DAY = CommonPlaceholderKeys.PLAYERS_NEW_DAY; - public static final PlaceholderKey PLAYERS_NEW_WEEK = CommonPlaceholderKeys.PLAYERS_NEW_WEEK; - public static final PlaceholderKey PLAYERS_NEW_MONTH = CommonPlaceholderKeys.PLAYERS_NEW_MONTH; - - public static final PlaceholderKey SERVERS_TAB = new PlaceholderKey<>(String.class, "tabContentServers"); - public static final PlaceholderKey WORLD_MAP_SERIES = CommonPlaceholderKeys.WORLD_MAP_SERIES; - public static final PlaceholderKey PLAYERS_ONLINE_SERIES = CommonPlaceholderKeys.PLAYERS_ONLINE_SERIES; - public static final PlaceholderKey ACTIVITY_STACK_SERIES = CommonPlaceholderKeys.ACTIVITY_STACK_SERIES; - public static final PlaceholderKey ACTIVITY_STACK_CATEGORIES = CommonPlaceholderKeys.ACTIVITY_STACK_CATEGORIES; - public static final PlaceholderKey ACTIVITY_PIE_SERIES = CommonPlaceholderKeys.ACTIVITY_PIE_SERIES; - public static final PlaceholderKey COUNTRY_CATEGORIES = CommonPlaceholderKeys.COUNTRY_CATEGORIES; - public static final PlaceholderKey COUNTRY_SERIES = CommonPlaceholderKeys.COUNTRY_SERIES; - public static final PlaceholderKey HEALTH_INDEX = CommonPlaceholderKeys.HEALTH_INDEX; - public static final PlaceholderKey HEALTH_NOTES = CommonPlaceholderKeys.HEALTH_NOTES; - - public static final Key REFRESH_TIME = new Key<>(Long.class, "REFRESH_TIME"); - public static final Key REFRESH_TIME_DAY_AGO = new Key<>(Long.class, "REFRESH_TIME_DAY_AGO"); - public static final Key REFRESH_TIME_WEEK_AGO = new Key<>(Long.class, "REFRESH_TIME_WEEK_AGO"); - public static final Key REFRESH_TIME_MONTH_AGO = new Key<>(Long.class, "REFRESH_TIME_MONTH_AGO"); - public static final Key PLAYERS_MUTATOR = CommonKeys.PLAYERS_MUTATOR; - - public static final Key> BUKKIT_SERVERS = new Key<>(new Type>() {}, "BUKKIT_SERVERS"); - public static final Key>>> ACTIVITY_DATA = CommonKeys.ACTIVITY_DATA; - public static final Key>> NETWORK_PLAYER_ONLINE_DATA = new Key<>(new Type>>() {}, "NETWORK_PLAYER_ONLINE_DATA"); - @Deprecated - public static final Key> SERVER_REGISTER_DATA = new Key<>(new Type>() {}, "SERVER_REGISTER_DATA"); - - private NetworkKeys() { - /* static variable class */ - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/ActivityIndex.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/ActivityIndex.java deleted file mode 100644 index dd3ca8485..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/ActivityIndex.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.store.mutators; - -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plugin.api.TimeAmount; - -import java.util.List; -import java.util.Optional; - -public class ActivityIndex { - - private final double value; - - private final long playtimeMsThreshold; - private final int loginThreshold; - - public ActivityIndex( - DataContainer container, long date, - long playtimeMsThreshold, int loginThreshold - ) { - this.playtimeMsThreshold = playtimeMsThreshold; - this.loginThreshold = loginThreshold; - - value = calculate(container, date); - } - - public static String[] getGroups() { - return new String[]{"Very Active", "Active", "Regular", "Irregular", "Inactive"}; - } - - private double calculate(DataContainer container, long date) { - long week = TimeAmount.WEEK.toMillis(1L); - long weekAgo = date - week; - long twoWeeksAgo = date - 2L * week; - long threeWeeksAgo = date - 3L * week; - - long activePlayThreshold = playtimeMsThreshold; - int activeLoginThreshold = loginThreshold; - - Optional> sessionsValue = container.getValue(PlayerKeys.SESSIONS); - if (!sessionsValue.isPresent()) { - return 0.0; - } - SessionsMutator sessionsMutator = new SessionsMutator(sessionsValue.get()); - if (sessionsMutator.all().isEmpty()) { - return 0.0; - } - - SessionsMutator weekOne = sessionsMutator.filterSessionsBetween(weekAgo, date); - SessionsMutator weekTwo = sessionsMutator.filterSessionsBetween(twoWeeksAgo, weekAgo); - SessionsMutator weekThree = sessionsMutator.filterSessionsBetween(threeWeeksAgo, twoWeeksAgo); - - // Playtime per week multipliers, max out to avoid too high values. - double max = 4.0; - - long playtimeWeek = weekOne.toActivePlaytime(); - double weekPlay = playtimeWeek * 1.0 / activePlayThreshold; - if (weekPlay > max) { - weekPlay = max; - } - long playtimeWeek2 = weekTwo.toActivePlaytime(); - double week2Play = playtimeWeek2 * 1.0 / activePlayThreshold; - if (week2Play > max) { - week2Play = max; - } - long playtimeWeek3 = weekThree.toActivePlaytime(); - double week3Play = playtimeWeek3 * 1.0 / activePlayThreshold; - if (week3Play > max) { - week3Play = max; - } - - double playtimeMultiplier = 1.0; - if (playtimeWeek + playtimeWeek2 + playtimeWeek3 > activePlayThreshold * 3.0) { - playtimeMultiplier = 1.25; - } - - // Reduce the harshness for new players and players who have had a vacation - if (weekPlay > 1 && week3Play > 1 && week2Play == 0.0) { - week2Play = 0.5; - } - if (weekPlay > 1 && week2Play == 0.0) { - week2Play = 0.6; - } - if (weekPlay > 1 && week3Play == 0.0) { - week3Play = 0.75; - } - - double playAvg = (weekPlay + week2Play + week3Play) / 3.0; - - double weekLogin = weekOne.count() >= activeLoginThreshold ? 1.0 : 0.5; - double week2Login = weekTwo.count() >= activeLoginThreshold ? 1.0 : 0.5; - double week3Login = weekThree.count() >= activeLoginThreshold ? 1.0 : 0.5; - - double loginMultiplier = 1.0; - double loginTotal = weekLogin + week2Login + week3Login; - double loginAvg = loginTotal / 3.0; - - if (loginTotal <= 2.0) { - // Reduce index for players that have not logged in the threshold amount for 2 weeks - loginMultiplier = 0.75; - } - - return playAvg * loginAvg * loginMultiplier * playtimeMultiplier; - } - - public double getValue() { - return value; - } - - public String getFormattedValue(Formatter formatter) { - return formatter.apply(value); - } - - public String getGroup() { - if (value >= 3.5) { - return "Very Active"; - } else if (value >= 1.75) { - return "Active"; - } else if (value >= 1.0) { - return "Regular"; - } else if (value >= 0.5) { - return "Irregular"; - } else { - return "Inactive"; - } - } - - public String getColor() { - if (value >= 3.5) { - return "green"; - } else if (value >= 1.75) { - return "green"; - } else if (value >= 1.0) { - return "lime"; - } else if (value >= 0.5) { - return "amber"; - } else { - return "blue-gray"; - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/CommandUseMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/CommandUseMutator.java deleted file mode 100644 index 11dca3d32..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/CommandUseMutator.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.store.mutators; - -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.keys.ServerKeys; - -import java.util.HashMap; -import java.util.Map; - -/** - * Mutator for Command Usage Map objects. - *

- * Can be used to easily get different values about the map. - * - * @author Rsl1122 - */ -public class CommandUseMutator { - - private Map commandUsage; - - public CommandUseMutator(Map commandUsage) { - this.commandUsage = commandUsage; - } - - public static CommandUseMutator forContainer(DataContainer container) { - return new CommandUseMutator(container.getValue(ServerKeys.COMMAND_USAGE).orElse(new HashMap<>())); - } - - public int commandUsageCount() { - int total = 0; - for (Integer value : commandUsage.values()) { - total += value; - } - return total; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/MutatorFunctions.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/MutatorFunctions.java deleted file mode 100644 index 21b802369..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/MutatorFunctions.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.store.mutators; - -import com.djrapitops.plan.utilities.html.graphs.line.Point; - -import java.util.List; -import java.util.Map; -import java.util.NavigableMap; -import java.util.TimeZone; -import java.util.stream.Collectors; - -public class MutatorFunctions { - - public static List toPoints(NavigableMap map) { - return map.entrySet().stream() - .map(entry -> new Point(entry.getKey(), entry.getValue())) - .collect(Collectors.toList()); - } - - public static List toPointsWithRemovedOffset(NavigableMap map, TimeZone timeZone) { - return map.entrySet().stream() - .map(entry -> new Point(entry.getKey() - timeZone.getOffset(entry.getKey()), entry.getValue())) - .collect(Collectors.toList()); - } - - public static int average(Map map) { - return (int) map.values().stream() - .mapToInt(i -> i) - .average().orElse(0); - } - - private MutatorFunctions() { - // Static method class. - } - -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java deleted file mode 100644 index ce42de3ed..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/AbstractHealthInfo.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.store.mutators.health; - -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.HealthInfoLang; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.icon.Icons; - -import java.util.*; -import java.util.concurrent.TimeUnit; - -public abstract class AbstractHealthInfo { - - protected static final String SUB_NOTE = "
     "; - - protected final List notes; - protected final long now; - protected final long monthAgo; - - protected double serverHealth; - - protected final Locale locale; - protected final long activeMsThreshold; - protected final int activeLoginThreshold; - protected final Formatter timeAmountFormatter; - protected final Formatter decimalFormatter; - protected final Formatter percentageFormatter; - - public AbstractHealthInfo( - long now, long monthAgo, - Locale locale, - long activeMsThreshold, - int activeLoginThreshold, - Formatter timeAmountFormatter, - Formatter decimalFormatter, - Formatter percentageFormatter - ) { - this.now = now; - this.monthAgo = monthAgo; - this.locale = locale; - this.activeMsThreshold = activeMsThreshold; - this.activeLoginThreshold = activeLoginThreshold; - this.timeAmountFormatter = timeAmountFormatter; - this.decimalFormatter = decimalFormatter; - this.percentageFormatter = percentageFormatter; - serverHealth = 100.0; - - this.notes = new ArrayList<>(); - } - - protected abstract void calculate(); - - public double getServerHealth() { - return serverHealth; - } - - public String toHtml() { - StringBuilder healthNoteBuilder = new StringBuilder(); - for (String healthNote : notes) { - healthNoteBuilder.append(healthNote); - } - return healthNoteBuilder.toString(); - } - - protected void activityChangeNote(TreeMap>> activityData) { - Map> activityNow = activityData.getOrDefault(now, new HashMap<>()); - Set veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>()); - Set activeNow = activityNow.getOrDefault("Active", new HashSet<>()); - Set regularNow = activityNow.getOrDefault("Regular", new HashSet<>()); - - Map> activityFourWAgo = activityData.getOrDefault(monthAgo, new HashMap<>()); - Set veryActiveFWAG = activityFourWAgo.getOrDefault("Very Active", new HashSet<>()); - Set activeFWAG = activityFourWAgo.getOrDefault("Active", new HashSet<>()); - Set regularFWAG = activityFourWAgo.getOrDefault("Regular", new HashSet<>()); - - Set regularRemainCompareSet = new HashSet<>(regularFWAG); - regularRemainCompareSet.addAll(activeFWAG); - regularRemainCompareSet.addAll(veryActiveFWAG); - - int activeFWAGNum = regularRemainCompareSet.size(); - regularRemainCompareSet.removeAll(regularNow); - regularRemainCompareSet.removeAll(activeNow); - regularRemainCompareSet.removeAll(veryActiveNow); - int notRegularAnymore = regularRemainCompareSet.size(); - int remain = activeFWAGNum - notRegularAnymore; - double percRemain = activeFWAGNum != 0 ? remain * 1.0 / activeFWAGNum : 1.0; - - int newActive = getNewActive(veryActiveNow, activeNow, regularNow, veryActiveFWAG, activeFWAG, regularFWAG); - - int change = newActive - notRegularAnymore; - - StringBuilder remainNote = new StringBuilder(); - if (activeFWAGNum != 0) { - remainNote.append(SUB_NOTE); - if (percRemain > 0.5) { - remainNote.append(Icons.GREEN_THUMB); - } else if (percRemain > 0.2) { - remainNote.append(Icons.YELLOW_FLAG); - } else { - remainNote.append(Icons.RED_WARN); - serverHealth -= 2.5; - } - - remainNote.append(locale.getString(HealthInfoLang.REGULAR_ACTIVITY_REMAIN, - percentageFormatter.apply(percRemain), - remain, activeFWAGNum - )); - } - - String sentenceStart = locale.getString(HealthInfoLang.REGULAR_CHANGE); - if (change > 0) { - addNote(Icons.GREEN_THUMB + sentenceStart + locale.getString(HealthInfoLang.REGULAR_CHANGE_INCREASE, change) + remainNote); - } else if (change == 0) { - addNote(Icons.GREEN_THUMB + sentenceStart + locale.getString(HealthInfoLang.REGULAR_CHANGE_ZERO, change) + remainNote); - } else if (change > -20) { - addNote(Icons.YELLOW_FLAG + sentenceStart + locale.getString(HealthInfoLang.REGULAR_CHANGE_DECREASE, change) + remainNote); - serverHealth -= 5; - } else { - addNote(Icons.RED_WARN + sentenceStart + locale.getString(HealthInfoLang.REGULAR_CHANGE_DECREASE, change) + remainNote); - serverHealth -= 10; - } - } - - protected void activePlayerPlaytimeChange(PlayersMutator playersMutator) { - PlayersMutator currentlyActive = playersMutator.filterActive(now, activeMsThreshold, activeLoginThreshold, 1.75); - long twoWeeksAgo = now - ((now - monthAgo) / 2L); - - long totalFourToTwoWeeks = 0; - long totalLastTwoWeeks = 0; - for (PlayerContainer activePlayer : currentlyActive.all()) { - totalFourToTwoWeeks += SessionsMutator.forContainer(activePlayer) - .filterSessionsBetween(monthAgo, twoWeeksAgo).toActivePlaytime(); - totalLastTwoWeeks += SessionsMutator.forContainer(activePlayer) - .filterSessionsBetween(twoWeeksAgo, now).toActivePlaytime(); - } - int activeCount = currentlyActive.count(); - if (activeCount != 0) { - long avgFourToTwoWeeks = totalFourToTwoWeeks / (long) activeCount; - long avgLastTwoWeeks = totalLastTwoWeeks / (long) activeCount; - String avgLastTwoWeeksString = timeAmountFormatter.apply(avgLastTwoWeeks); - String avgFourToTwoWeeksString = timeAmountFormatter.apply(avgFourToTwoWeeks); - - // Played more or equal amount than 2 weeks ago - if (avgLastTwoWeeks >= avgFourToTwoWeeks) { - addNote(Icons.GREEN_THUMB + locale.getString(HealthInfoLang.ACTIVE_PLAY_COMPARISON_INCREASE, - avgLastTwoWeeksString, avgFourToTwoWeeksString)); - // Played more than 2 hours less, than 2 weeks ago - } else if (avgFourToTwoWeeks - avgLastTwoWeeks > TimeUnit.HOURS.toMillis(2L)) { - addNote(Icons.RED_WARN + locale.getString(HealthInfoLang.ACTIVE_PLAY_COMPARISON_DECREASE, - avgLastTwoWeeksString, avgFourToTwoWeeksString)); - serverHealth -= 5; - // Played less than two weeks ago - } else { - addNote(Icons.YELLOW_FLAG + locale.getString(HealthInfoLang.ACTIVE_PLAY_COMPARISON_DECREASE, - avgLastTwoWeeksString, avgFourToTwoWeeksString)); - } - } - } - - private int getNewActive(Set veryActiveNow, Set activeNow, Set regularNow, Set veryActiveFWAG, Set activeFWAG, Set regularFWAG) { - Set regularNewCompareSet = new HashSet<>(regularNow); - regularNewCompareSet.addAll(activeNow); - regularNewCompareSet.addAll(veryActiveNow); - regularNewCompareSet.removeAll(regularFWAG); - regularNewCompareSet.removeAll(activeFWAG); - regularNewCompareSet.removeAll(veryActiveFWAG); - return regularNewCompareSet.size(); - } - - protected void addNote(String note) { - notes.add("

" + note + "

"); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java deleted file mode 100644 index ea79c05a4..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/HealthInformation.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.store.mutators.health; - -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.containers.AnalysisContainer; -import com.djrapitops.plan.data.store.keys.AnalysisKeys; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.data.store.mutators.PlayersOnlineResolver; -import com.djrapitops.plan.data.store.mutators.TPSMutator; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.HealthInfoLang; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.icon.Icons; -import com.djrapitops.plugin.api.TimeAmount; - -import java.util.ArrayList; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -/** - * Server Health analysis mutator. - * - * @author Rsl1122 - */ -public class HealthInformation extends AbstractHealthInfo { - - private final AnalysisContainer analysisContainer; - - private final int lowTPSThreshold; - - public HealthInformation( - AnalysisContainer analysisContainer, - Locale locale, - int lowTPSThreshold, - long activeMsThreshold, - int activeLoginThreshold, - Formatter timeAmountFormatter, - Formatter decimalFormatter, - Formatter percentageFormatter - ) { - super( - analysisContainer.getUnsafe(AnalysisKeys.ANALYSIS_TIME), - analysisContainer.getUnsafe(AnalysisKeys.ANALYSIS_TIME_MONTH_AGO), - locale, - activeMsThreshold, activeLoginThreshold, - timeAmountFormatter, decimalFormatter, percentageFormatter - ); - this.analysisContainer = analysisContainer; - this.lowTPSThreshold = lowTPSThreshold; - calculate(); - } - - @Override - public String toHtml() { - StringBuilder healthNoteBuilder = new StringBuilder(); - for (String healthNote : notes) { - healthNoteBuilder.append(healthNote); - } - return healthNoteBuilder.toString(); - } - - @Override - protected void calculate() { - activityChangeNote(analysisContainer.getUnsafe(AnalysisKeys.ACTIVITY_DATA)); - newPlayerNote(); - activePlayerPlaytimeChange(analysisContainer.getUnsafe(AnalysisKeys.PLAYERS_MUTATOR)); - lowPerformance(); - } - - private void newPlayerNote() { - Key newMonth = new Key<>(PlayersMutator.class, "NEW_MONTH"); - PlayersMutator newPlayersMonth = analysisContainer.getValue(newMonth).orElse(new PlayersMutator(new ArrayList<>())); - PlayersOnlineResolver onlineResolver = analysisContainer.getUnsafe(AnalysisKeys.PLAYERS_ONLINE_RESOLVER); - - double avgOnlineOnRegister = newPlayersMonth.registerDates().stream() - .map(onlineResolver::getOnlineOn) - .filter(Optional::isPresent) - .mapToInt(Optional::get) - .average().orElse(0); - if (avgOnlineOnRegister >= 1) { - addNote(Icons.GREEN_THUMB + locale.getString(HealthInfoLang.NEW_PLAYER_JOIN_PLAYERS_GOOD, - decimalFormatter.apply(avgOnlineOnRegister))); - } else { - addNote(Icons.YELLOW_FLAG + locale.getString(HealthInfoLang.NEW_PLAYER_JOIN_PLAYERS_BAD, - decimalFormatter.apply(avgOnlineOnRegister))); - serverHealth -= 5; - } - - long playersNewMonth = analysisContainer.getValue(AnalysisKeys.PLAYERS_NEW_MONTH).orElse(0); - long playersRetainedMonth = analysisContainer.getValue(AnalysisKeys.PLAYERS_RETAINED_MONTH).orElse(0); - - if (playersNewMonth != 0) { - double retainPercentage = playersRetainedMonth * 1.0 / playersNewMonth; - String stickinessSentence = locale.getString(HealthInfoLang.NEW_PLAYER_STICKINESS, - percentageFormatter.apply(retainPercentage), playersRetainedMonth, playersNewMonth); - if (retainPercentage >= 0.25) { - addNote(Icons.GREEN_THUMB + stickinessSentence); - } else { - addNote(Icons.YELLOW_FLAG + stickinessSentence); - } - } - } - - private void lowPerformance() { - Key tpsMonth = new Key<>(TPSMutator.class, "TPS_MONTH"); - TPSMutator tpsMutator = analysisContainer.getUnsafe(tpsMonth); - long serverDownTime = tpsMutator.serverDownTime(); - - double aboveThreshold = tpsMutator.percentageTPSAboveThreshold(lowTPSThreshold); - long tpsSpikeMonth = analysisContainer.getValue(AnalysisKeys.TPS_SPIKE_MONTH).orElse(0); - - StringBuilder avgLowThresholdString = new StringBuilder(SUB_NOTE); - if (aboveThreshold >= 0.96) { - avgLowThresholdString.append(Icons.GREEN_THUMB); - } else if (aboveThreshold >= 0.9) { - avgLowThresholdString.append(Icons.YELLOW_FLAG); - serverHealth *= 0.9; - } else { - avgLowThresholdString.append(Icons.RED_WARN); - serverHealth *= 0.6; - } - avgLowThresholdString.append(locale.getString(HealthInfoLang.TPS_ABOVE_LOW_THERSHOLD, percentageFormatter.apply(aboveThreshold))); - - String tpsDipSentence = locale.getString(HealthInfoLang.TPS_LOW_DIPS, lowTPSThreshold, tpsSpikeMonth); - if (tpsSpikeMonth <= 5) { - addNote(Icons.GREEN_THUMB + tpsDipSentence + avgLowThresholdString); - } else if (tpsSpikeMonth <= 25) { - addNote(Icons.YELLOW_FLAG + tpsDipSentence + avgLowThresholdString); - serverHealth *= 0.95; - } else { - addNote(Icons.RED_WARN + tpsDipSentence + avgLowThresholdString); - serverHealth *= 0.8; - } - - String downtimeSentence = locale.getString(HealthInfoLang.DOWNTIME, timeAmountFormatter.apply(serverDownTime)); - if (serverDownTime <= TimeUnit.DAYS.toMillis(1L)) { - addNote(Icons.GREEN_THUMB + downtimeSentence); - } else { - long weekMs = TimeAmount.WEEK.toMillis(1L); - if (serverDownTime <= weekMs) { - addNote(Icons.YELLOW_FLAG + downtimeSentence); - serverHealth *= (weekMs - serverDownTime) * 1.0 / weekMs; - } else { - addNote(Icons.RED_WARN + downtimeSentence); - long monthMs = TimeAmount.MONTH.toMillis(1L); - serverHealth *= (monthMs - serverDownTime) * 1.0 / monthMs; - } - } - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java deleted file mode 100644 index db50e1b8c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/health/NetworkHealthInformation.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.data.store.mutators.health; - -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.data.store.containers.SupplierDataContainer; -import com.djrapitops.plan.data.store.keys.AnalysisKeys; -import com.djrapitops.plan.data.store.keys.NetworkKeys; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.HealthInfoLang; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.icon.Icon; -import com.djrapitops.plan.utilities.html.icon.Icons; - -import java.util.*; - -public class NetworkHealthInformation extends AbstractHealthInfo { - - private final NetworkContainer container; - private final TimeZone timeZone; - - public NetworkHealthInformation( - NetworkContainer container, - Locale locale, - long activeMsThreshold, - int activeLoginThreshold, - Formatter timeAmountFormatter, - Formatter decimalFormatter, - Formatter percentageFormatter, - TimeZone timeZone - ) { - super( - container.getUnsafe(NetworkKeys.REFRESH_TIME), - container.getUnsafe(NetworkKeys.REFRESH_TIME_MONTH_AGO), - locale, - activeMsThreshold, activeLoginThreshold, - timeAmountFormatter, decimalFormatter, percentageFormatter - ); - this.container = container; - this.timeZone = timeZone; - calculate(); - } - - @Override - protected void calculate() { - perServerComparisonNotes(container.getUnsafe(NetworkKeys.PLAYERS_MUTATOR)); - - activityChangeNote(container.getUnsafe(NetworkKeys.ACTIVITY_DATA)); - activePlayerPlaytimeChange(container.getUnsafe(NetworkKeys.PLAYERS_MUTATOR)); - } - - private void perServerComparisonNotes(PlayersMutator playersMutator) { - Collection servers = container.getValue(NetworkKeys.BUKKIT_SERVERS) - .orElse(Collections.emptyList()); - - if (servers.isEmpty()) { - addNote(Icons.HELP_RING + locale.getString(HealthInfoLang.NO_SERVERS_INACCURACY)); - return; - } - int serverCount = servers.size(); - if (serverCount == 1) { - addNote(Icons.HELP_RING + locale.getString(HealthInfoLang.SINGLE_SERVER_INACCURACY)); - return; - } - - Key serverKey = new Key<>(Server.class, "SERVER"); - - List perServerContainers = getPerServerContainers(playersMutator, servers, serverKey); - - uniquePlayersNote(serverCount, serverKey, perServerContainers); - newPlayersNote(serverCount, serverKey, perServerContainers); - playersNote(serverKey, perServerContainers); - } - - private void uniquePlayersNote(int serverCount, Key serverKey, List perServerContainers) { - Icon icon; - String uniquePlayersNote = locale.getString(HealthInfoLang.PLAYER_VISIT_PER_SERVER); - double average = perServerContainers.stream() - .mapToInt(c -> c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH)) - .average().orElse(0.0); - if (average < 1) { - icon = Icons.RED_WARN; - serverHealth -= 10.0; - } else if (average < serverCount) { - icon = Icons.YELLOW_FLAG; - serverHealth -= 5.0; - } else { - icon = Icons.GREEN_THUMB; - } - StringBuilder subNotes = new StringBuilder(); - perServerContainers.stream() - .sorted(Comparator.comparingInt(c -> 0 - c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH))) - .map(c -> { - int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_MONTH); - Server server = c.getUnsafe(serverKey); - return SUB_NOTE + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + - server.getName() + ": " + playersPerMonth; - }).forEach(subNotes::append); - addNote(icon + " " + decimalFormatter.apply(average) + uniquePlayersNote + subNotes.toString()); - } - - private void newPlayersNote(int serverCount, Key serverKey, List perServerContainers) { - Icon icon; - String newPlayersNote = locale.getString(HealthInfoLang.PLAYER_REGISTER_PER_SERVER); - double average = perServerContainers.stream() - .mapToInt(c -> c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH)) - .average().orElse(0.0); - if (average < 1) { - icon = Icons.RED_WARN; - serverHealth -= 10.0; - } else if (average < serverCount) { - icon = Icons.YELLOW_FLAG; - serverHealth -= 5.0; - } else { - icon = Icons.GREEN_THUMB; - } - StringBuilder subNotes = new StringBuilder(); - perServerContainers.stream() - .sorted(Comparator.comparingInt(c -> 0 - c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH))) - .map(c -> { - int playersPerMonth = c.getUnsafe(AnalysisKeys.AVG_PLAYERS_NEW_MONTH); - Server server = c.getUnsafe(serverKey); - return SUB_NOTE + (playersPerMonth >= average && playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + - server.getName() + ": " + playersPerMonth; - }).forEach(subNotes::append); - addNote(icon + " " + decimalFormatter.apply(average) + newPlayersNote + subNotes.toString()); - } - - private List getPerServerContainers(PlayersMutator playersMutator, Collection servers, Key serverKey) { - List perServerContainers = new ArrayList<>(); - - for (Server server : servers) { - UUID serverUUID = server.getUuid(); - DataContainer serverContainer = new SupplierDataContainer(); - serverContainer.putRawData(serverKey, server); - - PlayersMutator serverPlayers = playersMutator.filterPlayedOnServer(serverUUID); - PlayersMutator serverRegistered = serverPlayers.filterRegisteredBetween(monthAgo, now); - int averageNewPerDay = serverRegistered.averageNewPerDay(timeZone); - serverContainer.putRawData(AnalysisKeys.AVG_PLAYERS_NEW_MONTH, averageNewPerDay); - SessionsMutator serverSessions = new SessionsMutator(serverPlayers.getSessions()) - .filterSessionsBetween(monthAgo, now) - .filterPlayedOnServer(serverUUID); - int averageUniquePerDay = serverSessions.toAverageUniqueJoinsPerDay(timeZone); - int uniquePlayers = serverSessions.toUniquePlayers(); - serverContainer.putRawData(AnalysisKeys.AVG_PLAYERS_MONTH, averageUniquePerDay); - serverContainer.putRawData(AnalysisKeys.PLAYERS_MONTH, uniquePlayers); - - perServerContainers.add(serverContainer); - } - return perServerContainers; - } - - private void playersNote(Key serverKey, List perServerContainers) { - Icon icon = Icons.HELP_RING; - String uniquePlayersNote = "${playersMonth}" + locale.getString(HealthInfoLang.PLAYER_PLAY_ON_NETWORK); - StringBuilder subNotes = new StringBuilder(); - perServerContainers.stream() - .sorted(Comparator.comparingInt(c -> 0 - c.getUnsafe(AnalysisKeys.PLAYERS_MONTH))) - .map(c -> { - int playersPerMonth = c.getUnsafe(AnalysisKeys.PLAYERS_MONTH); - Server server = c.getUnsafe(serverKey); - return SUB_NOTE + (playersPerMonth > 0 ? Icons.GREEN_PLUS : Icons.RED_MINUS) + " " + - server.getName() + ": " + playersPerMonth; - }).forEach(subNotes::append); - addNote(icon.toHtml() + " " + uniquePlayersNote + subNotes.toString()); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/AllPlayerContainersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/AllPlayerContainersQuery.java deleted file mode 100644 index a03cc51dc..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/AllPlayerContainersQuery.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.access.queries.containers; - -import com.djrapitops.plan.data.container.*; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.containers.SupplierDataContainer; -import com.djrapitops.plan.data.store.keys.PerServerKeys; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.mutators.PerServerMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.objects.*; - -import java.util.*; - -/** - * Used to get PlayerContainers of all players on the network, some limitations apply to DataContainer keys. - *

- * Limitations: - * - PlayerContainers do not support: PlayerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_KILL_COUNT - * - PlayerContainers PlayerKeys.PER_SERVER does not support: PerServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_KILL_COUNT - *

- * Blocking methods are not called until DataContainer getter methods are called. - * - * @author Rsl1122 - */ -public class AllPlayerContainersQuery implements Query> { - - /** - * Create PerServerContainers for each player. - * - * @param sessions Map: Server UUID - Map: Player UUID - List of Sessions - * @param allUserInfo Map: Server UUID - List of Users - * @param allPings Map: Player UUID - List of Ping data - * @return Map: Player UUID - PerServerContainer - */ - private Map getPerServerData( - Map>> sessions, - Map> allUserInfo, - Map> allPings - ) { - Map perServerContainers = new HashMap<>(); - - for (Map.Entry> entry : allUserInfo.entrySet()) { - UUID serverUUID = entry.getKey(); - List serverUserInfo = entry.getValue(); - - for (UserInfo userInfo : serverUserInfo) { - UUID uuid = userInfo.getPlayerUuid(); - if (uuid == null) { - continue; - } - PerServerContainer perServerContainer = perServerContainers.getOrDefault(uuid, new PerServerContainer()); - DataContainer container = perServerContainer.getOrDefault(serverUUID, new SupplierDataContainer()); - container.putRawData(PlayerKeys.REGISTERED, userInfo.getRegistered()); - container.putRawData(PlayerKeys.BANNED, userInfo.isBanned()); - container.putRawData(PlayerKeys.OPERATOR, userInfo.isOperator()); - perServerContainer.put(serverUUID, container); - perServerContainers.put(uuid, perServerContainer); - } - } - - for (Map.Entry>> entry : sessions.entrySet()) { - UUID serverUUID = entry.getKey(); - Map> serverUserSessions = entry.getValue(); - - for (Map.Entry> sessionEntry : serverUserSessions.entrySet()) { - UUID playerUUID = sessionEntry.getKey(); - PerServerContainer perServerContainer = perServerContainers.getOrDefault(playerUUID, new PerServerContainer()); - DataContainer container = perServerContainer.getOrDefault(serverUUID, new SupplierDataContainer()); - - List serverSessions = sessionEntry.getValue(); - container.putRawData(PerServerKeys.SESSIONS, serverSessions); - - container.putSupplier(PerServerKeys.LAST_SEEN, () -> SessionsMutator.forContainer(container).toLastSeen()); - - container.putSupplier(PerServerKeys.WORLD_TIMES, () -> SessionsMutator.forContainer(container).toTotalWorldTimes()); - container.putSupplier(PerServerKeys.PLAYER_DEATHS, () -> SessionsMutator.forContainer(container).toPlayerDeathList()); - container.putSupplier(PerServerKeys.PLAYER_KILLS, () -> SessionsMutator.forContainer(container).toPlayerKillList()); - container.putSupplier(PerServerKeys.PLAYER_KILL_COUNT, () -> container.getValue(PerServerKeys.PLAYER_KILLS).map(Collection::size).orElse(0)); - container.putSupplier(PerServerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount()); - container.putSupplier(PerServerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount()); - container.putSupplier(PerServerKeys.PLAYER_DEATH_COUNT, () -> SessionsMutator.forContainer(container).toPlayerDeathCount()); - container.putSupplier(PerServerKeys.MOB_DEATH_COUNT, () -> - container.getValue(PerServerKeys.DEATH_COUNT).orElse(0) - container.getValue(PerServerKeys.PLAYER_DEATH_COUNT).orElse(0) - ); - perServerContainer.put(serverUUID, container); - perServerContainers.put(playerUUID, perServerContainer); - } - } - - for (Map.Entry> entry : allPings.entrySet()) { - UUID uuid = entry.getKey(); - for (Ping ping : entry.getValue()) { - UUID serverUUID = ping.getServerUUID(); - PerServerContainer perServerContainer = perServerContainers.getOrDefault(uuid, new PerServerContainer()); - DataContainer container = perServerContainer.getOrDefault(serverUUID, new SupplierDataContainer()); - - if (!container.supports(PerServerKeys.PING)) { - container.putRawData(PerServerKeys.PING, new ArrayList<>()); - } - container.getUnsafe(PerServerKeys.PING).add(ping); - - perServerContainer.put(serverUUID, container); - perServerContainers.put(uuid, perServerContainer); - } - } - - return perServerContainers; - } - - @Override - public List executeQuery(SQLDB db) { - List containers = new ArrayList<>(); - - Collection users = db.query(BaseUserQueries.fetchAllBaseUsers()); - Map> geoInfo = db.query(GeoInfoQueries.fetchAllGeoInformation()); - Map> allPings = db.query(PingQueries.fetchAllPingData()); - Map> allNicknames = db.query(NicknameQueries.fetchAllNicknameDataByPlayerUUIDs()); - - Map>> sessions = db.query(SessionQueries.fetchAllSessionsWithoutKillOrWorldData()); - Map> allUserInfo = db.query(UserInfoQueries.fetchAllUserInformation()); - Map perServerInfo = getPerServerData(sessions, allUserInfo, allPings); - - for (BaseUser baseUser : users) { - PlayerContainer container = new PlayerContainer(); - UUID uuid = baseUser.getUuid(); - container.putRawData(PlayerKeys.UUID, uuid); - - container.putRawData(PlayerKeys.REGISTERED, baseUser.getRegistered()); - container.putRawData(PlayerKeys.NAME, baseUser.getName()); - container.putRawData(PlayerKeys.KICK_COUNT, baseUser.getTimesKicked()); - container.putRawData(PlayerKeys.GEO_INFO, geoInfo.get(uuid)); - container.putRawData(PlayerKeys.PING, allPings.get(uuid)); - container.putRawData(PlayerKeys.NICKNAMES, allNicknames.get(uuid)); - container.putRawData(PlayerKeys.PER_SERVER, perServerInfo.get(uuid)); - - container.putCachingSupplier(PlayerKeys.SESSIONS, () -> { - List playerSessions = PerServerMutator.forContainer(container).flatMapSessions(); - container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add); - return playerSessions; - } - ); - - // Calculating getters - container.putSupplier(PlayerKeys.LAST_SEEN, () -> SessionsMutator.forContainer(container).toLastSeen()); - - container.putSupplier(PlayerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount()); - container.putSupplier(PlayerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount()); - - containers.add(container); - } - return containers; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/NetworkContainerQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/NetworkContainerQuery.java deleted file mode 100644 index dd8dae581..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/NetworkContainerQuery.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.access.queries.containers; - -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.data.store.containers.ServerContainer; -import com.djrapitops.plan.data.store.keys.NetworkKeys; -import com.djrapitops.plan.data.store.keys.ServerKeys; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.queries.objects.TPSQueries; -import com.djrapitops.plan.system.info.server.Server; - -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; - -/** - * Used to get a NetworkContainer, some limitations apply to values returned by DataContainer keys. - *

- * Limitations: - * - Bungee ServerContainer does not support: ServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT - * - Bungee ServerContainer ServerKeys.TPS only contains playersOnline values - * - NetworkKeys.PLAYERS PlayerContainers: - * - do not support: PlayerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT - * - PlayerKeys.PER_SERVER does not support: PerServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT - *

- * Blocking methods are not called until DataContainer getter methods are called. - * - * @author Rsl1122 - */ -public class NetworkContainerQuery implements Query { - - private static Query getProxyServerContainer() { - return db -> { - Optional proxyInformation = db.query(ServerQueries.fetchProxyServerInformation()); - if (!proxyInformation.isPresent()) { - return new ServerContainer(); - } - - UUID proxyUUID = proxyInformation.get().getUuid(); - ServerContainer container = db.query(ContainerFetchQueries.fetchServerContainer(proxyUUID)); - container.putCachingSupplier(ServerKeys.PLAYERS, () -> db.query(ContainerFetchQueries.fetchAllPlayerContainers())); - container.putCachingSupplier(ServerKeys.TPS, () -> db.query(TPSQueries.fetchTPSDataOfServer(proxyUUID))); - container.putSupplier(ServerKeys.WORLD_TIMES, null); // Additional Session information not supported - container.putSupplier(ServerKeys.PLAYER_KILLS, null); - container.putSupplier(ServerKeys.PLAYER_KILL_COUNT, null); - - return container; - }; - } - - @Override - public NetworkContainer executeQuery(SQLDB db) { - ServerContainer bungeeContainer = db.query(getProxyServerContainer()); - NetworkContainer networkContainer = db.getNetworkContainerFactory().forBungeeContainer(bungeeContainer); - networkContainer.putCachingSupplier(NetworkKeys.BUKKIT_SERVERS, () -> - db.query(ServerQueries.fetchPlanServerInformation()).values() - .stream().filter(Server::isNotProxy).collect(Collectors.toSet()) - ); - return networkContainer; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java deleted file mode 100644 index c1594e184..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.access.queries.containers; - -import com.djrapitops.plan.data.container.BaseUser; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.objects.BaseUserQueries; -import com.djrapitops.plan.db.access.queries.objects.GeoInfoQueries; -import com.djrapitops.plan.db.access.queries.objects.SessionQueries; -import com.djrapitops.plan.db.access.queries.objects.UserInfoQueries; - -import java.util.*; - -/** - * Optimized version of {@link ServerPlayerContainersQuery} for /server page Players table. - * - * @author Rsl1122 - * @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For what needs to be included. - */ -public class ServerPlayersTableContainersQuery implements Query> { - - private final UUID serverUUID; - - public ServerPlayersTableContainersQuery(UUID serverUUID) { - this.serverUUID = serverUUID; - } - - @Override - public List executeQuery(SQLDB db) { - List containers = new ArrayList<>(); - - Collection baseUsers = db.query(BaseUserQueries.fetchServerBaseUsers(serverUUID)); - - Map> geoInformation = db.query(GeoInfoQueries.fetchServerGeoInformation(serverUUID)); - Map> sessions = db.query(SessionQueries.fetchSessionsOfServer(serverUUID)); - Set bannedUsers = db.query(UserInfoQueries.fetchBannedUUIDsOfServer(serverUUID)); - - for (BaseUser user : baseUsers) { - PlayerContainer container = new PlayerContainer(); - - // BaseUser - UUID uuid = user.getUuid(); - container.putRawData(PlayerKeys.UUID, uuid); - container.putRawData(PlayerKeys.NAME, user.getName()); - container.putRawData(PlayerKeys.REGISTERED, user.getRegistered()); - container.putRawData(PlayerKeys.BANNED, bannedUsers.contains(uuid)); - - // GeoInfo - container.putRawData(PlayerKeys.GEO_INFO, geoInformation.getOrDefault(uuid, new ArrayList<>())); - - container.putCachingSupplier(PlayerKeys.SESSIONS, () -> { - List playerSessions = sessions.getOrDefault(uuid, new ArrayList<>()); - container.getValue(PlayerKeys.ACTIVE_SESSION).ifPresent(playerSessions::add); - return playerSessions; - } - ); - - containers.add(container); - } - return containers; - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/PingQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/PingQueries.java deleted file mode 100644 index 7c22ea8d1..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/PingQueries.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.access.queries.objects; - -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.PingTable; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.*; - -import static com.djrapitops.plan.db.sql.parsing.Sql.*; - -/** - * Queries for {@link WebUser} objects. - * - * @author Rsl1122 - */ -public class PingQueries { - - private PingQueries() { - /* Static method class */ - } - - /** - * Query database for all Ping data. - * - * @return Map: Player UUID - List of ping data. - */ - public static Query>> fetchAllPingData() { - String sql = SELECT + - PingTable.DATE + ',' + - PingTable.MAX_PING + ',' + - PingTable.MIN_PING + ',' + - PingTable.AVG_PING + ',' + - PingTable.USER_UUID + ',' + - PingTable.SERVER_UUID + - FROM + PingTable.TABLE_NAME; - return new QueryAllStatement>>(sql, 100000) { - @Override - public Map> processResults(ResultSet set) throws SQLException { - return extractUserPings(set); - } - }; - } - - private static Map> extractUserPings(ResultSet set) throws SQLException { - Map> userPings = new HashMap<>(); - - while (set.next()) { - UUID uuid = UUID.fromString(set.getString(PingTable.USER_UUID)); - UUID serverUUID = UUID.fromString(set.getString(PingTable.SERVER_UUID)); - long date = set.getLong(PingTable.DATE); - double avgPing = set.getDouble(PingTable.AVG_PING); - int minPing = set.getInt(PingTable.MIN_PING); - int maxPing = set.getInt(PingTable.MAX_PING); - - List pings = userPings.getOrDefault(uuid, new ArrayList<>()); - pings.add(new Ping(date, serverUUID, - minPing, - maxPing, - avgPing)); - userPings.put(uuid, pings); - } - - return userPings; - } - - /** - * Query database for Ping data of a specific player. - * - * @param playerUUID UUID of the player. - * @return List of Ping entries for this player. - */ - public static Query> fetchPingDataOfPlayer(UUID playerUUID) { - String sql = SELECT + '*' + FROM + PingTable.TABLE_NAME + - WHERE + PingTable.USER_UUID + "=?"; - - return new QueryStatement>(sql, 10000) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, playerUUID.toString()); - } - - @Override - public List processResults(ResultSet set) throws SQLException { - List pings = new ArrayList<>(); - - while (set.next()) { - pings.add(new Ping( - set.getLong(PingTable.DATE), - UUID.fromString(set.getString(PingTable.SERVER_UUID)), - set.getInt(PingTable.MIN_PING), - set.getInt(PingTable.MAX_PING), - set.getDouble(PingTable.AVG_PING) - ) - ); - } - - return pings; - } - }; - } - - public static Query>> fetchPingDataOfServer(UUID serverUUID) { - String sql = SELECT + - PingTable.DATE + ',' + - PingTable.MAX_PING + ',' + - PingTable.MIN_PING + ',' + - PingTable.AVG_PING + ',' + - PingTable.USER_UUID + ',' + - PingTable.SERVER_UUID + - FROM + PingTable.TABLE_NAME + - WHERE + PingTable.SERVER_UUID + "=?"; - return new QueryStatement>>(sql, 100000) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, serverUUID.toString()); - } - - @Override - public Map> processResults(ResultSet set) throws SQLException { - return extractUserPings(set); - } - }; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/SessionQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/SessionQueries.java deleted file mode 100644 index 6bb2ed444..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/SessionQueries.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.access.queries.objects; - -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.data.time.GMTimes; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.*; -import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.*; -import java.util.stream.Collectors; - -import static com.djrapitops.plan.db.sql.parsing.Sql.*; - -/** - * Queries for {@link com.djrapitops.plan.data.container.Session} objects. - * - * @author Rsl1122 - */ -public class SessionQueries { - - private SessionQueries() { - /* Static method class */ - } - - private static final String SELECT_SESSIONS_STATEMENT = SELECT + - SessionsTable.TABLE_NAME + '.' + SessionsTable.ID + ',' + - SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID + ',' + - SessionsTable.TABLE_NAME + '.' + SessionsTable.SERVER_UUID + ',' + - SessionsTable.SESSION_START + ',' + - SessionsTable.SESSION_END + ',' + - SessionsTable.MOB_KILLS + ',' + - SessionsTable.DEATHS + ',' + - SessionsTable.AFK_TIME + ',' + - WorldTimesTable.SURVIVAL + ',' + - WorldTimesTable.CREATIVE + ',' + - WorldTimesTable.ADVENTURE + ',' + - WorldTimesTable.SPECTATOR + ',' + - WorldTable.NAME + ',' + - KillsTable.VICTIM_UUID + ',' + - UsersTable.USER_NAME + " as victim_name, " + - KillsTable.DATE + ',' + - KillsTable.WEAPON + - FROM + SessionsTable.TABLE_NAME + - LEFT_JOIN + KillsTable.TABLE_NAME + " ON " + SessionsTable.TABLE_NAME + '.' + SessionsTable.ID + "=" + KillsTable.TABLE_NAME + '.' + KillsTable.SESSION_ID + - LEFT_JOIN + UsersTable.TABLE_NAME + " on " + UsersTable.TABLE_NAME + '.' + UsersTable.USER_UUID + "=" + KillsTable.VICTIM_UUID + - INNER_JOIN + WorldTimesTable.TABLE_NAME + " ON " + SessionsTable.TABLE_NAME + '.' + SessionsTable.ID + "=" + WorldTimesTable.TABLE_NAME + '.' + WorldTimesTable.SESSION_ID + - INNER_JOIN + WorldTable.TABLE_NAME + " ON " + WorldTimesTable.TABLE_NAME + '.' + WorldTimesTable.WORLD_ID + "=" + WorldTable.TABLE_NAME + '.' + WorldTable.ID; - - private static final String ORDER_BY_SESSION_START_DESC = ORDER_BY + SessionsTable.SESSION_START + " DESC"; - - /** - * Query the database for Session data without kill, death or world data. - * - * @return Multimap: Server UUID - (Player UUID - List of sessions) - */ - public static Query>>> fetchAllSessionsWithoutKillOrWorldData() { - String sql = SELECT + - SessionsTable.ID + ',' + - SessionsTable.USER_UUID + ',' + - SessionsTable.SERVER_UUID + ',' + - SessionsTable.SESSION_START + ',' + - SessionsTable.SESSION_END + ',' + - SessionsTable.DEATHS + ',' + - SessionsTable.MOB_KILLS + ',' + - SessionsTable.AFK_TIME + - FROM + SessionsTable.TABLE_NAME; - - return new QueryAllStatement>>>(sql, 20000) { - @Override - public Map>> processResults(ResultSet set) throws SQLException { - Map>> map = new HashMap<>(); - while (set.next()) { - UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID)); - UUID uuid = UUID.fromString(set.getString(SessionsTable.USER_UUID)); - - Map> sessionsByUser = map.getOrDefault(serverUUID, new HashMap<>()); - List sessions = sessionsByUser.getOrDefault(uuid, new ArrayList<>()); - - long start = set.getLong(SessionsTable.SESSION_START); - long end = set.getLong(SessionsTable.SESSION_END); - - int deaths = set.getInt(SessionsTable.DEATHS); - int mobKills = set.getInt(SessionsTable.MOB_KILLS); - int id = set.getInt(SessionsTable.ID); - - long timeAFK = set.getLong(SessionsTable.AFK_TIME); - - sessions.add(new Session(id, uuid, serverUUID, start, end, mobKills, deaths, timeAFK)); - - sessionsByUser.put(uuid, sessions); - map.put(serverUUID, sessionsByUser); - } - return map; - } - }; - } - - /** - * Query the database for Session data with kill, death or world data. - * - * @return List of sessions - */ - public static Query> fetchAllSessions() { - String sql = SELECT_SESSIONS_STATEMENT + - ORDER_BY_SESSION_START_DESC; - return new QueryAllStatement>(sql, 50000) { - @Override - public List processResults(ResultSet set) throws SQLException { - return extractDataFromSessionSelectStatement(set); - } - }; - } - - /** - * Query the database for Session data of a server with kill and world data. - * - * @param serverUUID UUID of the Plan server. - * @return Map: Player UUID - List of sessions on the server. - */ - public static Query>> fetchSessionsOfServer(UUID serverUUID) { - String sql = SELECT_SESSIONS_STATEMENT + - WHERE + SessionsTable.TABLE_NAME + '.' + SessionsTable.SERVER_UUID + "=?" + - ORDER_BY_SESSION_START_DESC; - return new QueryStatement>>(sql, 50000) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, serverUUID.toString()); - } - - @Override - public Map> processResults(ResultSet set) throws SQLException { - List sessions = extractDataFromSessionSelectStatement(set); - return SessionsMutator.sortByPlayers(sessions); - } - }; - } - - /** - * Query the database for Session data of a player with kill and world data. - * - * @param playerUUID UUID of the Player. - * @return Map: Server UUID - List of sessions on the server. - */ - public static Query>> fetchSessionsOfPlayer(UUID playerUUID) { - String sql = SELECT_SESSIONS_STATEMENT + - WHERE + SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID + "=?" + - ORDER_BY_SESSION_START_DESC; - return new QueryStatement>>(sql, 50000) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, playerUUID.toString()); - } - - @Override - public Map> processResults(ResultSet set) throws SQLException { - List sessions = extractDataFromSessionSelectStatement(set); - return SessionsMutator.sortByServers(sessions); - } - }; - } - - private static List extractDataFromSessionSelectStatement(ResultSet set) throws SQLException { - // Server UUID - Player UUID - Session Start - Session - Map>> tempSessionMap = new HashMap<>(); - - // Utilities - String[] gms = GMTimes.getGMKeyArray(); - Comparator dateColderRecentComparator = new DateHolderRecentComparator(); - Comparator longRecentComparator = (one, two) -> Long.compare(two, one); // Descending order, most recent first. - - while (set.next()) { - UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID)); - Map> serverSessions = tempSessionMap.getOrDefault(serverUUID, new HashMap<>()); - - UUID playerUUID = UUID.fromString(set.getString(SessionsTable.USER_UUID)); - SortedMap playerSessions = serverSessions.getOrDefault(playerUUID, new TreeMap<>(longRecentComparator)); - - long sessionStart = set.getLong(SessionsTable.SESSION_START); - // id, uuid, serverUUID, sessionStart, sessionEnd, mobKills, deaths, afkTime - Session session = playerSessions.getOrDefault(sessionStart, new Session( - set.getInt(SessionsTable.ID), - playerUUID, - serverUUID, - sessionStart, - set.getLong(SessionsTable.SESSION_END), - set.getInt(SessionsTable.MOB_KILLS), - set.getInt(SessionsTable.DEATHS), - set.getLong(SessionsTable.AFK_TIME) - )); - - WorldTimes worldTimes = session.getValue(SessionKeys.WORLD_TIMES).orElse(new WorldTimes()); - String worldName = set.getString(WorldTable.NAME); - - if (!worldTimes.contains(worldName)) { - Map gmMap = new HashMap<>(); - gmMap.put(gms[0], set.getLong(WorldTimesTable.SURVIVAL)); - gmMap.put(gms[1], set.getLong(WorldTimesTable.CREATIVE)); - gmMap.put(gms[2], set.getLong(WorldTimesTable.ADVENTURE)); - gmMap.put(gms[3], set.getLong(WorldTimesTable.SPECTATOR)); - GMTimes gmTimes = new GMTimes(gmMap); - worldTimes.setGMTimesForWorld(worldName, gmTimes); - } - - String victimName = set.getString("victim_name"); - if (victimName != null) { - UUID victim = UUID.fromString(set.getString(KillsTable.VICTIM_UUID)); - long date = set.getLong(KillsTable.DATE); - String weapon = set.getString(KillsTable.WEAPON); - List playerKills = session.getPlayerKills(); - playerKills.add(new PlayerKill(victim, weapon, date, victimName)); - playerKills.sort(dateColderRecentComparator); - } - - playerSessions.put(sessionStart, session); - serverSessions.put(playerUUID, playerSessions); - tempSessionMap.put(serverUUID, serverSessions); - } - - return tempSessionMap.values().stream() - .map(Map::values) - .flatMap(Collection::stream) - .map(SortedMap::values) - .flatMap(Collection::stream) - .collect(Collectors.toList()); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/TPSQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/TPSQueries.java deleted file mode 100644 index 528e3a863..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/TPSQueries.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.access.queries.objects; - -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.builders.TPSBuilder; -import com.djrapitops.plan.data.store.objects.DateObj; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.parsing.Select; -import com.djrapitops.plan.db.sql.tables.ServerTable; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plugin.api.TimeAmount; -import org.apache.commons.text.TextStringBuilder; - -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.*; - -import static com.djrapitops.plan.db.sql.parsing.Sql.*; -import static com.djrapitops.plan.db.sql.tables.TPSTable.*; - -/** - * Queries for {@link com.djrapitops.plan.data.container.TPS} objects. - * - * @author Rsl1122 - */ -public class TPSQueries { - - private TPSQueries() { - /* Static method class */ - } - - public static Query> fetchTPSDataOfServer(UUID serverUUID) { - String sql = Select.all(TABLE_NAME) - .where(SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID) - .toString(); - - return new QueryStatement>(sql, 50000) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, serverUUID.toString()); - } - - @Override - public List processResults(ResultSet set) throws SQLException { - List data = new ArrayList<>(); - while (set.next()) { - - TPS tps = TPSBuilder.get() - .date(set.getLong(DATE)) - .tps(set.getDouble(TPS)) - .playersOnline(set.getInt(PLAYERS_ONLINE)) - .usedCPU(set.getDouble(CPU_USAGE)) - .usedMemory(set.getLong(RAM_USAGE)) - .entities(set.getInt(ENTITIES)) - .chunksLoaded(set.getInt(CHUNKS)) - .freeDiskSpace(set.getLong(FREE_DISK)) - .toTPS(); - - data.add(tps); - } - return data; - } - }; - } - - public static Query>> fetchPlayerOnlineDataOfServers(Collection servers) { - if (servers.isEmpty()) { - return db -> new HashMap<>(); - } - - TextStringBuilder sql = new TextStringBuilder(SELECT); - sql.append(SERVER_ID).append(',') - .append(DATE).append(',') - .append(PLAYERS_ONLINE) - .append(FROM).append(TABLE_NAME) - .append(WHERE).append(DATE).append(">").append(System.currentTimeMillis() - TimeAmount.WEEK.toMillis(2L)) - .append(AND).append('('); - sql.appendWithSeparators(servers.stream().map(server -> SERVER_ID + "=" + server.getId()).iterator(), OR); - sql.append(')'); - - return new QueryAllStatement>>(sql.toString(), 10000) { - @Override - public Map> processResults(ResultSet set) throws SQLException { - Map> map = new HashMap<>(); - while (set.next()) { - int serverID = set.getInt(SERVER_ID); - int playersOnline = set.getInt(PLAYERS_ONLINE); - long date = set.getLong(DATE); - - List tpsList = map.getOrDefault(serverID, new ArrayList<>()); - - TPS tps = TPSBuilder.get().date(date) - .playersOnline(playersOnline) - .toTPS(); - tpsList.add(tps); - - map.put(serverID, tpsList); - } - return map; - } - }; - } - - public static Query>> fetchPeakPlayerCount(UUID serverUUID, long afterDate) { - String subQuery = '(' + SELECT + "MAX(" + PLAYERS_ONLINE + ')' + FROM + TABLE_NAME + WHERE + SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID + - AND + DATE + ">= ?)"; - String sql = SELECT + - DATE + ',' + PLAYERS_ONLINE + - FROM + TABLE_NAME + - WHERE + SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID + - AND + DATE + ">= ?" + - AND + PLAYERS_ONLINE + "=" + subQuery + - ORDER_BY + DATE + " DESC LIMIT 1"; - - return new QueryStatement>>(sql) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, serverUUID.toString()); - statement.setLong(2, afterDate); - statement.setString(3, serverUUID.toString()); - statement.setLong(4, afterDate); - } - - @Override - public Optional> processResults(ResultSet set) throws SQLException { - if (set.next()) { - return Optional.of(new DateObj<>( - set.getLong(DATE), - set.getInt(PLAYERS_ONLINE) - )); - } - return Optional.empty(); - } - }; - } - - public static Query>> fetchAllTimePeakPlayerCount(UUID serverUUID) { - return fetchPeakPlayerCount(serverUUID, 0); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/CommandStoreTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/CommandStoreTransaction.java deleted file mode 100644 index 42602ddae..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/CommandStoreTransaction.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.access.transactions.events; - -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; - -import java.util.UUID; - -/** - * Transaction to update command usage information in the database. - * - * @author Rsl1122 - */ -public class CommandStoreTransaction extends Transaction { - - private final UUID serverUUID; - private final String commandName; - - public CommandStoreTransaction( - UUID serverUUID, - String commandName - ) { - this.serverUUID = serverUUID; - this.commandName = commandName; - } - - @Override - protected boolean shouldBeExecuted() { - return commandName.length() <= 20; - } - - @Override - protected void performOperations() { - execute(DataStoreQueries.storeUsedCommandInformation(serverUUID, commandName)); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java deleted file mode 100644 index b449a317f..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DeleteIPHashesPatch.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.patches; - -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.sql.tables.GeoInfoTable; - -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.sql.Types; - -import static com.djrapitops.plan.db.sql.parsing.Sql.*; - -/** - * Patch for removing ip_hash values from plan_ips table. - *

- * The patch is a response to a concern: - * "Hashed IP addresses are pseudonymised not anonymised and can be easily decoded using a rainbow table". - * - * @author Rsl1122 - */ -public class DeleteIPHashesPatch extends Patch { - - private static final String IP_HASH = "ip_hash"; - - private boolean hasNoHashColumn; - - @Override - public boolean hasBeenApplied() { - hasNoHashColumn = !hasColumn(GeoInfoTable.TABLE_NAME, IP_HASH); - - String sql = SELECT + "COUNT(1) as c" + FROM + GeoInfoTable.TABLE_NAME + - WHERE + IP_HASH + IS_NOT_NULL; - - return hasNoHashColumn || !query(new HasMoreThanZeroQueryStatement(sql) { - @Override - public void prepare(PreparedStatement statement) { - /* No variables needed */ - } - }); - } - - @Override - protected void applyPatch() { - if (hasNoHashColumn) { - return; - } - - String sql = "UPDATE " + GeoInfoTable.TABLE_NAME + " SET ip_hash=?" + WHERE + IP_HASH + IS_NOT_NULL; - execute(new ExecStatement(sql) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setNull(1, Types.VARCHAR); - } - }); - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java deleted file mode 100644 index 976b9c87c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/IPAnonPatch.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.patches; - -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.db.access.ExecBatchStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.access.queries.objects.GeoInfoQueries; -import com.djrapitops.plan.db.sql.tables.GeoInfoTable; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import static com.djrapitops.plan.db.sql.parsing.Sql.*; - -public class IPAnonPatch extends Patch { - - private String tableName; - private String tempTableName; - - public IPAnonPatch() { - tableName = GeoInfoTable.TABLE_NAME; - tempTableName = "plan_ips_temp"; - } - - @Override - public boolean hasBeenApplied() { - return !containsUnAnonymizedIPs() && !hasTable(tempTableName); - } - - private Boolean containsUnAnonymizedIPs() { - String sql = SELECT + '*' + FROM + tableName + - WHERE + GeoInfoTable.IP + " NOT LIKE ? LIMIT 1"; - - return query(new QueryStatement(sql) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, "%x%"); - } - - @Override - public Boolean processResults(ResultSet set) throws SQLException { - return set.next(); - } - }); - } - - @Override - protected void applyPatch() { - Map> allGeoInfo = query(GeoInfoQueries.fetchAllGeoInformation()); - anonymizeIPs(allGeoInfo); - groupHashedIPs(); - } - - private void anonymizeIPs(Map> allGeoInfo) { - String sql = "UPDATE " + GeoInfoTable.TABLE_NAME + " SET " + - GeoInfoTable.IP + "=?" + - WHERE + GeoInfoTable.IP + "=?"; - - execute(new ExecBatchStatement(sql) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - for (List geoInfos : allGeoInfo.values()) { - for (GeoInfo geoInfo : geoInfos) { - addToBatch(statement, geoInfo); - } - } - } - - private void addToBatch(PreparedStatement statement, GeoInfo geoInfo) throws SQLException { - try { - String oldIP = geoInfo.getIp(); - if (oldIP.endsWith(".xx.xx") || oldIP.endsWith("xx..")) { - return; - } - GeoInfo updatedInfo = new GeoInfo( - InetAddress.getByName(oldIP), - geoInfo.getGeolocation(), - geoInfo.getDate() - ); - statement.setString(1, updatedInfo.getIp()); - statement.setString(2, geoInfo.getIp()); - statement.addBatch(); - } catch (UnknownHostException ignore) { - // This ip is completely unusable. - } - } - }); - } - - private void groupHashedIPs() { - if (!hasTable(tempTableName)) { - tempOldTable(); - } - execute(GeoInfoTable.createTableSQL(dbType)); - - String userIdColumn = "user_id"; - boolean hasUserIdColumn = hasColumn(tempTableName, userIdColumn); - String identifiers = hasUserIdColumn ? userIdColumn : "id, uuid"; - - execute("INSERT INTO plan_ips (" + - identifiers + ", ip, geolocation, last_used" + - ") SELECT " + - identifiers + ", ip, geolocation, MAX(last_used) FROM plan_ips_temp GROUP BY ip, " + - (hasUserIdColumn ? userIdColumn : "uuid") + - ", geolocation, id"); - dropTable(tempTableName); - } - - private void tempOldTable() { - if (!hasTable(tempTableName)) { - renameTable(tableName, tempTableName); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Sql.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Sql.java deleted file mode 100644 index 39128d9b1..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Sql.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.sql.parsing; - -/** - * Duplicate String reducing utility class for SQL language Strings. - */ -public class Sql { - public static final String INT = "integer"; - public static final String DOUBLE = "double"; - public static final String LONG = "bigint"; - public static final String BOOL = "boolean"; - - public static final String SELECT = "SELECT "; - public static final String DISTINCT = "DISTINCT "; - public static final String FROM = " FROM "; - public static final String DELETE_FROM = "DELETE" + FROM; - public static final String WHERE = " WHERE "; - public static final String GROUP_BY = " GROUP BY "; - public static final String ORDER_BY = " ORDER BY "; - public static final String INNER_JOIN = " INNER JOIN "; - public static final String LEFT_JOIN = " LEFT JOIN "; - public static final String AND = " AND "; - public static final String OR = " OR "; - public static final String IS_NULL = " IS NULL"; - public static final String IS_NOT_NULL = " IS NOT NULL"; - - private Sql() { - /* Variable class */ - } - - public static String varchar(int length) { - return "varchar(" + length + ')'; - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/CommandUseTable.java b/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/CommandUseTable.java deleted file mode 100644 index 956a0ab21..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/CommandUseTable.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.db.sql.tables; - -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; - -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; - -/** - * Table information about 'plan_commandusages'. - * - * Patches affecting this table: - * {@link com.djrapitops.plan.db.patches.Version10Patch} - * - * @author Rsl1122 - */ -public class CommandUseTable { - - public static final String TABLE_NAME = "plan_commandusages"; - - public static final String ID = "id"; - public static final String SERVER_ID = "server_id"; - public static final String COMMAND = "command"; - public static final String TIMES_USED = "times_used"; - - public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" - + COMMAND + ',' - + TIMES_USED + ',' - + SERVER_ID - + ") VALUES (?, ?, " + ServerTable.STATEMENT_SELECT_SERVER_ID + ')'; - - public static final String UPDATE_STATEMENT = "UPDATE " + CommandUseTable.TABLE_NAME + " SET " - + CommandUseTable.TIMES_USED + "=" + CommandUseTable.TIMES_USED + "+ 1" + - WHERE + CommandUseTable.SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID + - AND + CommandUseTable.COMMAND + "=?"; - - private CommandUseTable() { - /* Static information class */ - } - - public static String createTableSQL(DBType dbType) { - return CreateTableParser.create(TABLE_NAME, dbType) - .column(ID, Sql.INT).primaryKey() - .column(COMMAND, Sql.varchar(20)).notNull() - .column(TIMES_USED, Sql.INT).notNull() - .column(SERVER_ID, Sql.INT).notNull() - .foreignKey(SERVER_ID, ServerTable.TABLE_NAME, ServerTable.SERVER_ID) - .toString(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/Processors.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/DeliveryUtilities.java similarity index 57% rename from Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/Processors.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/DeliveryUtilities.java index 2bc0341d7..688198b9d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/Processors.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/DeliveryUtilities.java @@ -14,31 +14,36 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.processing.processors; +package com.djrapitops.plan.delivery; -import com.djrapitops.plan.system.processing.processors.info.InfoProcessors; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs; +import dagger.Lazy; import javax.inject.Inject; import javax.inject.Singleton; -/** - * Factory for creating Runnables to run with {@link com.djrapitops.plan.system.processing.Processing}. - * - * @author Rsl1122 - */ @Singleton -public class Processors { +public class DeliveryUtilities { - private final InfoProcessors infoProcessors; + private final Lazy formatters; + private final Lazy graphs; @Inject - public Processors( - InfoProcessors infoProcessors + public DeliveryUtilities( + Lazy formatters, + Lazy graphs ) { - this.infoProcessors = infoProcessors; + this.formatters = formatters; + this.graphs = graphs; } - public InfoProcessors info() { - return infoProcessors; + public Formatters getFormatters() { + return formatters.get(); } -} \ No newline at end of file + + public Graphs getGraphs() { + return graphs.get(); + } + +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateHolder.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateHolder.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateHolder.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateHolder.java index 892d607ed..21cbb2736 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateHolder.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateHolder.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.objects; +package com.djrapitops.plan.delivery.domain; /** * Interface for objects that have a epoch ms date. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateMap.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateMap.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateMap.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateMap.java index 9df1907df..216f43aec 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateMap.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateMap.java @@ -14,8 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.objects; +package com.djrapitops.plan.delivery.domain; +import java.util.Map; import java.util.TreeMap; /** @@ -29,6 +30,10 @@ public class DateMap extends TreeMap { super(Long::compareTo); } + public DateMap(Map map) { + putAll(map); + } + public boolean hasValuesBetween(long after, long before) { return countBetween(after, before) > 0; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateObj.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateObj.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateObj.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateObj.java index 600e1d192..83f048c5e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateObj.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateObj.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.objects; +package com.djrapitops.plan.delivery.domain; /** * Object that has a value tied to a date. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateSet.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateSet.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateSet.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateSet.java index cbad25a77..2bfe65545 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/DateSet.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/DateSet.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.objects; +package com.djrapitops.plan.delivery.domain; import java.util.TreeSet; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/Nickname.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/Nickname.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/Nickname.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/Nickname.java index d69a7d008..c3c984f4c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/objects/Nickname.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/Nickname.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.objects; +package com.djrapitops.plan.delivery.domain; import java.util.Objects; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/TablePlayer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/TablePlayer.java new file mode 100644 index 000000000..7ca2f3b87 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/TablePlayer.java @@ -0,0 +1,182 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain; + +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.gathering.domain.BaseUser; + +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +/** + * Represents a player displayed on a player table on players tab or /players page. + * + * @author Rsl1122 + */ +public class TablePlayer implements Comparable { + + private UUID uuid; + private String name; + private ActivityIndex activityIndex; + private Long playtime; + private Integer sessionCount; + private Long registered; + private Long lastSeen; + private String geolocation; + + private boolean banned = false; + + private TablePlayer() { + } + + public static TablePlayer.Builder builder() { + return new TablePlayer.Builder(); + } + + public static TablePlayer.Builder builderFromBaseUser(BaseUser baseUser) { + return new TablePlayer.Builder() + .uuid(baseUser.getUuid()) + .name(baseUser.getName()) + .registered(baseUser.getRegistered()); + } + + public UUID getPlayerUUID() { + return uuid; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public Optional getCurrentActivityIndex() { + return Optional.ofNullable(activityIndex); + } + + public Optional getPlaytime() { + return Optional.ofNullable(playtime); + } + + public Optional getSessionCount() { + return Optional.ofNullable(sessionCount); + } + + public Optional getRegistered() { + return Optional.ofNullable(registered); + } + + public Optional getLastSeen() { + return Optional.ofNullable(lastSeen); + } + + public Optional getGeolocation() { + return Optional.ofNullable(geolocation); + } + + public boolean isBanned() { + return banned; + } + + @Override + public int compareTo(TablePlayer other) { + // Most recent first + return Long.compare( + other.lastSeen != null ? other.lastSeen : 0L, + this.lastSeen != null ? this.lastSeen : 0L + ); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TablePlayer)) return false; + TablePlayer that = (TablePlayer) o; + return playtime == that.playtime && + sessionCount == that.sessionCount && + registered == that.registered && + lastSeen == that.lastSeen && + name.equals(that.name) && + activityIndex.equals(that.activityIndex) && + geolocation.equals(that.geolocation); + } + + @Override + public int hashCode() { + return Objects.hash(name, activityIndex, playtime, sessionCount, registered, lastSeen, geolocation); + } + + public static class Builder { + private final TablePlayer player; + + public Builder() { + player = new TablePlayer(); + } + + public UUID getPlayerUUID() { + return player.uuid; + } + + public Builder uuid(UUID playerUUID) { + player.uuid = playerUUID; + return this; + } + + public Builder name(String name) { + player.name = name; + return this; + } + + public Builder banned() { + player.banned = true; + return this; + } + + public Builder activityIndex(ActivityIndex activityIndex) { + player.activityIndex = activityIndex; + return this; + } + + public Builder playtime(long playtime) { + player.playtime = playtime; + return this; + } + + public Builder sessionCount(int count) { + player.sessionCount = count; + return this; + } + + public Builder registered(long registered) { + player.registered = registered; + return this; + } + + public Builder lastSeen(long lastSeen) { + player.lastSeen = lastSeen; + return this; + } + + public Builder geolocation(String geolocation) { + player.geolocation = geolocation; + return this; + } + + public TablePlayer build() { + return player; + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/WebUser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/WebUser.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/data/WebUser.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/WebUser.java index de70c4bae..591e07e6d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/WebUser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/WebUser.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data; +package com.djrapitops.plan.delivery.domain; import java.util.Objects; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/CachingSupplier.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/CachingSupplier.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/CachingSupplier.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/CachingSupplier.java index 0b1e8e15c..05f61646b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/CachingSupplier.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/CachingSupplier.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store; +package com.djrapitops.plan.delivery.domain.container; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/DataContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/DataContainer.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/DataContainer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/DataContainer.java index c2f508da8..4394d1531 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/DataContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/DataContainer.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.containers; +package com.djrapitops.plan.delivery.domain.container; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.domain.keys.Key; +import com.djrapitops.plan.delivery.formatting.Formatter; import java.util.Map; import java.util.Optional; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/DynamicDataContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/DynamicDataContainer.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/DynamicDataContainer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/DynamicDataContainer.java index baf3cc93a..c1ef0093d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/DynamicDataContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/DynamicDataContainer.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.containers; +package com.djrapitops.plan.delivery.domain.container; -import com.djrapitops.plan.data.store.Key; +import com.djrapitops.plan.delivery.domain.keys.Key; import java.util.Map; import java.util.Optional; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/PerServerContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PerServerContainer.java similarity index 75% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/PerServerContainer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PerServerContainer.java index 77ddb615d..df0ab97b5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/PerServerContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PerServerContainer.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.containers; +package com.djrapitops.plan.delivery.domain.container; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.container.UserInfo; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.keys.PerServerKeys; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.domain.keys.Key; +import com.djrapitops.plan.delivery.domain.keys.PerServerKeys; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.gathering.domain.Ping; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.UserInfo; import java.util.*; @@ -30,7 +30,7 @@ import java.util.*; * Container for data about a player linked to a single server. * * @author Rsl1122 - * @see com.djrapitops.plan.data.store.keys.PerServerKeys For Key objects. + * @see com.djrapitops.plan.delivery.domain.keys.PerServerKeys For Key objects. */ public class PerServerContainer extends HashMap { @@ -58,15 +58,9 @@ public class PerServerContainer extends HashMap { container.putSupplier(PerServerKeys.LAST_SEEN, () -> SessionsMutator.forContainer(container).toLastSeen()); container.putSupplier(PerServerKeys.WORLD_TIMES, () -> SessionsMutator.forContainer(container).toTotalWorldTimes()); - container.putSupplier(PerServerKeys.PLAYER_DEATHS, () -> SessionsMutator.forContainer(container).toPlayerDeathList()); - container.putSupplier(PerServerKeys.PLAYER_KILLS, () -> SessionsMutator.forContainer(container).toPlayerKillList()); - container.putSupplier(PerServerKeys.PLAYER_KILL_COUNT, () -> container.getValue(PerServerKeys.PLAYER_KILLS).map(Collection::size).orElse(0)); + container.putSupplier(PerServerKeys.PLAYER_KILL_COUNT, () -> SessionsMutator.forContainer(container).toPlayerKillCount()); container.putSupplier(PerServerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount()); container.putSupplier(PerServerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount()); - container.putSupplier(PerServerKeys.PLAYER_DEATH_COUNT, () -> SessionsMutator.forContainer(container).toPlayerDeathCount()); - container.putSupplier(PerServerKeys.MOB_DEATH_COUNT, () -> - container.getValue(PerServerKeys.DEATH_COUNT).orElse(0) - container.getValue(PerServerKeys.PLAYER_DEATH_COUNT).orElse(0) - ); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/PlayerContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PlayerContainer.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/PlayerContainer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PlayerContainer.java index d8423890c..49abc981f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/PlayerContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/PlayerContainer.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.containers; +package com.djrapitops.plan.delivery.domain.container; -import com.djrapitops.plan.data.store.mutators.ActivityIndex; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; import java.util.HashMap; import java.util.Map; @@ -28,7 +28,7 @@ import java.util.Map; * Use {@code getValue(PlayerKeys.REGISTERED).isPresent()} to determine if Plan has data about the player. * * @author Rsl1122 - * @see com.djrapitops.plan.data.store.keys.PlayerKeys For Key objects. + * @see com.djrapitops.plan.delivery.domain.keys.PlayerKeys For Key objects. */ public class PlayerContainer extends DynamicDataContainer { @@ -38,8 +38,8 @@ public class PlayerContainer extends DynamicDataContainer { activityIndexCache = new HashMap<>(); } - public ActivityIndex getActivityIndex(long date, long playtimeMsThreshold, int loginThreshold) { - return activityIndexCache.computeIfAbsent(date, time -> new ActivityIndex(this, time, playtimeMsThreshold, loginThreshold)); + public ActivityIndex getActivityIndex(long date, long playtimeMsThreshold) { + return activityIndexCache.computeIfAbsent(date, time -> new ActivityIndex(this, time, playtimeMsThreshold)); } public boolean playedBetween(long after, long before) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/RawDataContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/RawDataContainer.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/RawDataContainer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/RawDataContainer.java index 842f8e85b..fe93e3472 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/RawDataContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/RawDataContainer.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.containers; +package com.djrapitops.plan.delivery.domain.container; -import com.djrapitops.plan.data.store.Key; +import com.djrapitops.plan.delivery.domain.keys.Key; import java.util.HashMap; import java.util.Map; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/ServerContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/ServerContainer.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/ServerContainer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/ServerContainer.java index 7328ddf0e..8d12d7d50 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/ServerContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/ServerContainer.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.containers; +package com.djrapitops.plan.delivery.domain.container; /** * DataContainer for a single server. * * @author Rsl1122 - * @see com.djrapitops.plan.data.store.keys.ServerKeys For Key objects. + * @see com.djrapitops.plan.delivery.domain.keys.ServerKeys For Key objects. */ public class ServerContainer extends DynamicDataContainer { } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/SupplierDataContainer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/SupplierDataContainer.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/SupplierDataContainer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/SupplierDataContainer.java index b84d6ab6e..b57f77000 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/containers/SupplierDataContainer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/container/SupplierDataContainer.java @@ -14,10 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.containers; +package com.djrapitops.plan.delivery.domain.container; -import com.djrapitops.plan.data.store.CachingSupplier; -import com.djrapitops.plan.data.store.Key; +import com.djrapitops.plan.delivery.domain.keys.Key; import java.util.HashMap; import java.util.Map; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/AnalysisKeys.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/AnalysisKeys.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/AnalysisKeys.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/AnalysisKeys.java index 5194b51bc..84236b379 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/AnalysisKeys.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/AnalysisKeys.java @@ -14,15 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.keys; +package com.djrapitops.plan.delivery.domain.keys; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.PlaceholderKey; -import com.djrapitops.plan.data.store.Type; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.data.store.mutators.PlayersOnlineResolver; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.delivery.domain.mutators.PlayersMutator; +import com.djrapitops.plan.delivery.domain.mutators.PlayersOnlineResolver; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; import java.util.Map; import java.util.Set; @@ -36,7 +33,9 @@ import java.util.UUID; * * @author Rsl1122 * @see com.djrapitops.plan.data.store.containers.AnalysisContainer for Suppliers for each Key. + * @deprecated AnalysisContainer can no longer be obtained, so this is deprecated. */ +@Deprecated public class AnalysisKeys { // Constants (Affected only by config settings) diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/CommonKeys.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/CommonKeys.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/CommonKeys.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/CommonKeys.java index faf53c9be..007cc49e2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/CommonKeys.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/CommonKeys.java @@ -14,19 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.keys; +package com.djrapitops.plan.delivery.domain.keys; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.container.PlayerDeath; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.PlaceholderKey; -import com.djrapitops.plan.data.store.Type; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.store.mutators.TPSMutator; -import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.delivery.domain.mutators.PlayersMutator; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.domain.*; import java.util.*; @@ -51,6 +44,7 @@ public class CommonKeys { public static final Key WORLD_TIMES = new Key<>(WorldTimes.class, "world_times"); public static final PlaceholderKey LAST_SEEN = new PlaceholderKey<>(Long.class, "lastSeen"); + @Deprecated public static final Key> PLAYER_DEATHS = new Key<>(new Type>() {}, "player_deaths"); public static final Key> PLAYER_KILLS = new Key<>(new Type>() {}, "player_kills"); public static final Key PLAYER_KILL_COUNT = new Key<>(Integer.class, "player_kill_count"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/CommonPlaceholderKeys.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/CommonPlaceholderKeys.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/CommonPlaceholderKeys.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/CommonPlaceholderKeys.java index cb2197833..61da25938 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/CommonPlaceholderKeys.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/CommonPlaceholderKeys.java @@ -14,15 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.keys; - -import com.djrapitops.plan.data.store.PlaceholderKey; +package com.djrapitops.plan.delivery.domain.keys; /** - * Similar to {@link CommonKeys}, but for {@link com.djrapitops.plan.data.store.PlaceholderKey}s. + * Similar to {@link CommonKeys}, but for {@link PlaceholderKey}s. * * @author Rsl1122 - * @see com.djrapitops.plan.data.store.PlaceholderKey for placeholder information + * @see PlaceholderKey for placeholder information */ class CommonPlaceholderKeys { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/Key.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/Key.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/Key.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/Key.java index 16e0838b4..94bf72250 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/Key.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/Key.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store; +package com.djrapitops.plan.delivery.domain.keys; import java.util.Objects; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/PerServerKeys.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PerServerKeys.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/PerServerKeys.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PerServerKeys.java index e907a7e5b..6201b27af 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/PerServerKeys.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PerServerKeys.java @@ -14,15 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.keys; +package com.djrapitops.plan.delivery.domain.keys; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.container.PlayerDeath; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.delivery.domain.container.PerServerContainer; +import com.djrapitops.plan.gathering.domain.*; import java.util.List; @@ -30,7 +25,7 @@ import java.util.List; * Key objects for PerServerContainer container. * * @author Rsl1122 - * @see com.djrapitops.plan.db.access.queries.containers.PerServerContainerQuery For Suppliers for each key + * @see com.djrapitops.plan.storage.database.queries.containers.PerServerContainerQuery For Suppliers for each key * @see PerServerContainer For the DataContainer. */ public class PerServerKeys { @@ -50,8 +45,10 @@ public class PerServerKeys { @Deprecated public static final Key> PLAYER_DEATHS = CommonKeys.PLAYER_DEATHS; public static final Key PLAYER_KILL_COUNT = CommonKeys.PLAYER_KILL_COUNT; + @Deprecated public static final Key PLAYER_DEATH_COUNT = CommonKeys.PLAYER_DEATH_COUNT; public static final Key MOB_KILL_COUNT = CommonKeys.MOB_KILL_COUNT; + @Deprecated public static final Key MOB_DEATH_COUNT = CommonKeys.MOB_DEATH_COUNT; public static final Key DEATH_COUNT = CommonKeys.DEATH_COUNT; public static final Key LAST_SEEN = CommonKeys.LAST_SEEN; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/PlaceholderKey.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PlaceholderKey.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/PlaceholderKey.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PlaceholderKey.java index 40c42b069..9d305fb7c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/PlaceholderKey.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PlaceholderKey.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store; +package com.djrapitops.plan.delivery.domain.keys; /** * Special Key object that can be used for placeholders when replacing values in html files. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/PlayerKeys.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PlayerKeys.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/PlayerKeys.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PlayerKeys.java index 6cd87ac9e..e24d73309 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/PlayerKeys.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/PlayerKeys.java @@ -14,15 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.keys; +package com.djrapitops.plan.delivery.domain.keys; -import com.djrapitops.plan.data.container.*; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.PlaceholderKey; -import com.djrapitops.plan.data.store.Type; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.delivery.domain.container.PerServerContainer; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.gathering.domain.*; import java.util.List; import java.util.UUID; @@ -31,8 +28,7 @@ import java.util.UUID; * Class that holds Key objects for PlayerContainer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.database.databases.sql.operation.SQLFetchOps For Suppliers for each key - * @see com.djrapitops.plan.data.store.containers.PlayerContainer For DataContainer. + * @see PlayerContainer For DataContainer. */ public class PlayerKeys { @@ -55,6 +51,9 @@ public class PlayerKeys { public static final Key WORLD_TIMES = CommonKeys.WORLD_TIMES; public static final Key> PLAYER_KILLS = CommonKeys.PLAYER_KILLS; + public static final Key> PLAYER_DEATHS_KILLS = new Key<>(new Type>() { + }, "player_deaths_kills"); + @Deprecated public static final Key> PLAYER_DEATHS = CommonKeys.PLAYER_DEATHS; public static final Key PLAYER_KILL_COUNT = CommonKeys.PLAYER_KILL_COUNT; public static final Key MOB_KILL_COUNT = CommonKeys.MOB_KILL_COUNT; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/ServerKeys.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/ServerKeys.java similarity index 72% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/ServerKeys.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/ServerKeys.java index 40009c56f..73c93ac2c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/ServerKeys.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/ServerKeys.java @@ -14,18 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.keys; +package com.djrapitops.plan.delivery.domain.keys; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.Type; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.objects.DateObj; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.container.ServerContainer; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; +import com.djrapitops.plan.gathering.domain.*; import java.util.List; import java.util.Map; @@ -35,8 +30,8 @@ import java.util.UUID; * Keys for the ServerContainer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.database.databases.sql.operation.SQLFetchOps For Suppliers for each key - * @see com.djrapitops.plan.data.store.containers.ServerContainer For DataContainer. + * @see com.djrapitops.plan.storage.database.databases.sql.operation.SQLFetchOps For Suppliers for each key + * @see ServerContainer For DataContainer. */ public class ServerKeys { @@ -63,6 +58,7 @@ public class ServerKeys { public static final Key> TPS = new Key<>(new Type>() {}, "tps"); public static final Key> ALL_TIME_PEAK_PLAYERS = new Key<>(new Type>() {}, "all_time_peak_players"); public static final Key> RECENT_PEAK_PLAYERS = new Key<>(new Type>() {}, "recent_peak_players"); + @Deprecated public static final Key> COMMAND_USAGE = new Key<>(new Type>() {}, "command_usage"); - public static final Key> EXTENSION_DATA = new Key<>(new Type>() {}, "extension_data"); + public static final Key> EXTENSION_DATA = new Key<>(new Type>() {}, "extension_data"); } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/SessionKeys.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/SessionKeys.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/SessionKeys.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/SessionKeys.java index 9315770dd..21b2f0269 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/keys/SessionKeys.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/SessionKeys.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.keys; +package com.djrapitops.plan.delivery.domain.keys; -import com.djrapitops.plan.data.container.PlayerDeath; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.gathering.domain.PlayerDeath; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.WorldTimes; import java.util.List; import java.util.UUID; @@ -28,13 +28,15 @@ import java.util.UUID; * Class holding Key objects for Session (DataContainer). * * @author Rsl1122 - * @see com.djrapitops.plan.data.container.Session for DataContainer. + * @see Session for DataContainer. */ public class SessionKeys { public static final Key DB_ID = new Key<>(Integer.class, "db_id"); public static final Key UUID = CommonKeys.UUID; public static final Key SERVER_UUID = CommonKeys.SERVER_UUID; + public static final Key NAME = CommonKeys.NAME; + public static final Key SERVER_NAME = new Key<>(String.class, "server_name"); public static final Key START = new Key<>(Long.class, "start"); public static final Key END = new Key<>(Long.class, "end"); @@ -46,9 +48,13 @@ public class SessionKeys { public static final Key PLAYER_KILL_COUNT = CommonKeys.PLAYER_KILL_COUNT; public static final Key MOB_KILL_COUNT = CommonKeys.MOB_KILL_COUNT; public static final Key DEATH_COUNT = CommonKeys.DEATH_COUNT; + public static final Key FIRST_SESSION = new Key<>(Boolean.class, "first_session"); @Deprecated public static final Key> PLAYER_DEATHS = CommonKeys.PLAYER_DEATHS; + /** + * @deprecated use WorldAliasSettings#getLongestWorldPlayed(Session) instead. + */ @Deprecated public static final Key LONGEST_WORLD_PLAYED = new Key<>(String.class, "longest_world_played"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/Type.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/Type.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/Type.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/Type.java index 24289bf8f..db3c17d49 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/Type.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/keys/Type.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store; +package com.djrapitops.plan.delivery.domain.keys; import java.util.Objects; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/ActivityIndex.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/ActivityIndex.java new file mode 100644 index 000000000..663973c3f --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/ActivityIndex.java @@ -0,0 +1,193 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain.mutators; + +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.HtmlLang; +import com.djrapitops.plugin.api.TimeAmount; + +import java.util.List; +import java.util.Optional; + +/** + * Represents Activity index of a player at a certain date. + *

+ * Old formula for activity index was not linear and difficult to turn into a query due to conditional multipliers. + * Thus a new formula was written. + *

+ * {@code T} - Time played after someone is considered active on a particular week + * {@code t1, t2, t3} - Time played that week + *

+ * Activity index takes into account last 3 weeks. + *

+ * Activity for a single week is calculated using {@code A(t) = (1 / (pi/2 * (t/T) + 1))}. + * A(t) is based on function f(x) = 1 / (x + 1), which has property f(0) = 1, decreasing from there, but not in a straight line. + * You can see the function plotted here https://www.wolframalpha.com/input/?i=1+%2F+(x%2B1)+from+-1+to+2 + *

+ * To fine tune the curve pi/2 is used since it felt like a good curve. + *

+ * Activity index A is calculated by using the formula: + * {@code A = 5 - 5 * [A(t1) + A(t2) + A(t3)] / 3} + *

+ * Plot for A and limits + * https://www.wolframalpha.com/input/?i=plot+y+%3D+5+-+5+*+(1+%2F+(pi%2F2+*+x%2B1))+and+y+%3D1+and+y+%3D+2+and+y+%3D+3+and+y+%3D+3.75+from+-0.5+to+3 + *

+ * New Limits for A would thus be + * {@code < 1: Inactive} + * {@code > 1: Irregular} + * {@code > 2: Regular} + * {@code > 3: Active} + * {@code > 3.75: Very Active} + */ +public class ActivityIndex { + + public static final double VERY_ACTIVE = 3.75; + public static final double ACTIVE = 3.0; + public static final double REGULAR = 2.0; + public static final double IRREGULAR = 1.0; + + private final double value; + private final long date; + + private long playtimeMsThreshold; + + public ActivityIndex(DataContainer container, long date, long playtimeMsThreshold) { + this.playtimeMsThreshold = playtimeMsThreshold; + + this.date = date; + value = calculate(container); + } + + public ActivityIndex(List sessions, long date, long playtimeMsThreshold) { + this.playtimeMsThreshold = playtimeMsThreshold; + + this.date = date; + value = calculate(new SessionsMutator(sessions)); + } + + public ActivityIndex(double value, long date) { + this.value = value; + this.date = date; + } + + public static String[] getDefaultGroups() { + return getGroups(null); + } + + public static String[] getGroups(Locale locale) { + if (locale == null) { + return new String[]{ + HtmlLang.INDEX_VERY_ACTIVE.getDefault(), + HtmlLang.INDEX_ACTIVE.getDefault(), + HtmlLang.INDEX_REGULAR.getDefault(), + HtmlLang.INDEX_IRREGULAR.getDefault(), + HtmlLang.INDEX_INACTIVE.getDefault() + }; + } + return new String[]{ + locale.get(HtmlLang.INDEX_VERY_ACTIVE).toString(), + locale.get(HtmlLang.INDEX_ACTIVE).toString(), + locale.get(HtmlLang.INDEX_REGULAR).toString(), + locale.get(HtmlLang.INDEX_IRREGULAR).toString(), + locale.get(HtmlLang.INDEX_INACTIVE).toString() + }; + } + + private double calculate(DataContainer container) { + Optional> sessionsValue = container.getValue(PlayerKeys.SESSIONS); + return sessionsValue + .map(sessions -> calculate(new SessionsMutator(sessions))) + .orElse(0.0); + } + + private double calculate(SessionsMutator sessionsMutator) { + if (sessionsMutator.all().isEmpty()) { + return 0.0; + } + + long week = TimeAmount.WEEK.toMillis(1L); + long weekAgo = date - week; + long twoWeeksAgo = date - 2L * week; + long threeWeeksAgo = date - 3L * week; + + SessionsMutator weekOne = sessionsMutator.filterSessionsBetween(weekAgo, date); + SessionsMutator weekTwo = sessionsMutator.filterSessionsBetween(twoWeeksAgo, weekAgo); + SessionsMutator weekThree = sessionsMutator.filterSessionsBetween(threeWeeksAgo, twoWeeksAgo); + + long playtime1 = weekOne.toActivePlaytime(); + long playtime2 = weekTwo.toActivePlaytime(); + long playtime3 = weekThree.toActivePlaytime(); + + double indexW1 = 1.0 / (Math.PI / 2.0 * (playtime1 * 1.0 / playtimeMsThreshold) + 1.0); + double indexW2 = 1.0 / (Math.PI / 2.0 * (playtime2 * 1.0 / playtimeMsThreshold) + 1.0); + double indexW3 = 1.0 / (Math.PI / 2.0 * (playtime3 * 1.0 / playtimeMsThreshold) + 1.0); + + double average = (indexW1 + indexW2 + indexW3) / 3.0; + + return 5.0 - (5.0 * average); + } + + public double getValue() { + return value; + } + + public long getDate() { + return date; + } + + public String getFormattedValue(Formatter formatter) { + return formatter.apply(value); + } + + public double distance(ActivityIndex other) { + // Logarithm makes the distance function more skewed towards active players + // https://www.wolframalpha.com/input/?i=plot+y+%3D+log(5+-+5+*+(1+%2F+(pi%2F2+*+x%2B1)))+and+5+-+5+*+(1+%2F+(pi%2F2+*+x%2B1))+and+y+%3D1+and+y+%3D+2+and+y+%3D+3+and+y+%3D+3.75+from+-0.5+to+3 + return Math.abs(Math.log(other.value) - Math.log(value)); + } + + public String getGroup() { + if (value >= VERY_ACTIVE) { + return "Very Active"; + } else if (value >= ACTIVE) { + return "Active"; + } else if (value >= REGULAR) { + return "Regular"; + } else if (value >= IRREGULAR) { + return "Irregular"; + } else { + return "Inactive"; + } + } + + public String getColor() { + if (value >= VERY_ACTIVE) { + return "green"; + } else if (value >= ACTIVE) { + return "green"; + } else if (value >= REGULAR) { + return "lime"; + } else if (value >= IRREGULAR) { + return "amber"; + } else { + return "blue-gray"; + } + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/DateHoldersMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/DateHoldersMutator.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/DateHoldersMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/DateHoldersMutator.java index d6e98a8af..d69c0b5f0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/DateHoldersMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/DateHoldersMutator.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.store.objects.DateHolder; +import com.djrapitops.plan.delivery.domain.DateHolder; import java.util.*; import java.util.concurrent.TimeUnit; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/GeoInfoMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/GeoInfoMutator.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/GeoInfoMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/GeoInfoMutator.java index 45e8f10e3..61e6ffb96 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/GeoInfoMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/GeoInfoMutator.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.gathering.domain.GeoInfo; import com.djrapitops.plan.utilities.comparators.GeoInfoComparator; import java.util.ArrayList; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/MutatorFunctions.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/MutatorFunctions.java new file mode 100644 index 000000000..939e7399b --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/MutatorFunctions.java @@ -0,0 +1,106 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain.mutators; + +import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point; +import com.djrapitops.plugin.utilities.Verify; + +import java.util.*; +import java.util.stream.Collectors; + +public class MutatorFunctions { + + public static List toPoints(NavigableMap map) { + return map.entrySet().stream() + .map(entry -> new Point(entry.getKey(), entry.getValue())) + .collect(Collectors.toList()); + } + + public static List toPointsWithRemovedOffset(NavigableMap map, TimeZone timeZone) { + return map.entrySet().stream() + .map(entry -> new Point(entry.getKey() - timeZone.getOffset(entry.getKey()), entry.getValue())) + .collect(Collectors.toList()); + } + + public static NavigableMap addMissing(NavigableMap points, long accuracy, Integer replacement) { + if (Verify.isEmpty(points)) return points; + + NavigableMap filled = new TreeMap<>(); + Long lastX = null; + for (Map.Entry point : points.entrySet()) { + long date = point.getKey(); + + if (lastX != null && date - lastX > accuracy) { + addMissing(lastX, date, filled, accuracy, replacement); + } + lastX = date; + filled.put(point.getKey(), point.getValue()); + } + + long now = System.currentTimeMillis(); + if (lastX != null && now - lastX > accuracy) { + addMissing(lastX, now, filled, accuracy, replacement); + } + + return filled; + } + + private static void addMissing(long from, long to, NavigableMap points, long accuracy, Integer replacement) { + long iterate = from; + while (iterate < to) { + points.putIfAbsent(iterate, replacement); + iterate += accuracy; + } + } + + public static List addMissing(List points, long accuracy, Integer replacement) { + if (Verify.isEmpty(points)) return points; + + List filled = new ArrayList<>(); + Long lastX = null; + for (Point point : points) { + long date = (long) point.getX(); + + if (lastX != null && date - lastX > accuracy) { + addMissing(lastX, date, filled, accuracy, replacement); + } + lastX = date; + filled.add(point); + } + + return filled; + } + + private static void addMissing(long from, long to, List points, long accuracy, Integer replacement) { + long iterate = from; + while (iterate < to) { + points.add(new Point(iterate, replacement)); + iterate += accuracy; + } + } + + public static int average(Map map) { + return (int) map.values().stream() + .mapToInt(i -> i) + .average().orElse(0); + } + + private MutatorFunctions() { + // Static method class. + } + +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/NetworkPerServerMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/NetworkPerServerMutator.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/NetworkPerServerMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/NetworkPerServerMutator.java index be04820a2..697fda473 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/NetworkPerServerMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/NetworkPerServerMutator.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.CommonKeys; -import com.djrapitops.plan.data.store.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.container.PerServerContainer; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.CommonKeys; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; import java.util.*; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PerServerMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PerServerMutator.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PerServerMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PerServerMutator.java index 2062ac002..116779737 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PerServerMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PerServerMutator.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.store.keys.PerServerKeys; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.container.PerServerContainer; +import com.djrapitops.plan.delivery.domain.keys.PerServerKeys; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.WorldTimes; import java.util.*; import java.util.stream.Collectors; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PingMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PingMutator.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PingMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PingMutator.java index 4cb5f62cb..3e1ff7727 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PingMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PingMutator.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.keys.CommonKeys; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.keys.CommonKeys; +import com.djrapitops.plan.gathering.domain.Ping; import java.util.ArrayList; import java.util.List; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayerKillMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayerKillMutator.java new file mode 100644 index 000000000..37c244c6f --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayerKillMutator.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain.mutators; + +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.gathering.domain.PlayerKill; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Mutator functions for {@link PlayerKill} objects. + * + * @author Rsl1122 + */ +public class PlayerKillMutator { + + private final List kills; + + public PlayerKillMutator(List kills) { + this.kills = kills; + } + + public PlayerKillMutator filterNonSelfKills() { + return new PlayerKillMutator(kills.stream().filter(PlayerKill::isNotSelfKill).collect(Collectors.toList())); + } + + public List> toJSONAsMap(Formatters formatters) { + return kills.stream().map( + kill -> { + Map killMap = new HashMap<>(); + killMap.put("date", formatters.secondLong().apply(kill.getDate())); + killMap.put("victim", kill.getVictimName().orElse(kill.getVictim().toString())); + killMap.put("killer", kill.getKillerName().orElse("Missing UUID")); // TODO Kills should support killer UUID + killMap.put("weapon", kill.getWeapon()); + return killMap; + } + ).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayerVersusMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayerVersusMutator.java new file mode 100644 index 000000000..193340d2a --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayerVersusMutator.java @@ -0,0 +1,93 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.domain.mutators; + +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.utilities.Predicates; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class PlayerVersusMutator { + + private final SessionsMutator sessionsMutator; + private final List kills; + private final List deaths; + + public PlayerVersusMutator(SessionsMutator sessionsMutator, List kills, List deaths) { + this.sessionsMutator = sessionsMutator; + this.kills = kills; + this.deaths = deaths; + } + + public static PlayerVersusMutator forContainer(DataContainer container) { + return new PlayerVersusMutator( + SessionsMutator.forContainer(container), + container.getValue(PlayerKeys.PLAYER_KILLS).orElse(Collections.emptyList()), + container.getValue(PlayerKeys.PLAYER_DEATHS_KILLS).orElse(Collections.emptyList()) + ); + } + + public PlayerVersusMutator filterBetween(long after, long before) { + Predicate killWithinDate = Predicates.within(after, before); + + return new PlayerVersusMutator( + sessionsMutator.filterSessionsBetween(after, before), + kills.stream().filter(killWithinDate).collect(Collectors.toList()), + deaths.stream().filter(killWithinDate).collect(Collectors.toList()) + ); + } + + public int toPlayerKillCount() { + return kills.size(); + } + + public int toMobKillCount() { + return sessionsMutator.toMobKillCount(); + } + + public int toPlayerDeathCount() { + return deaths.size(); + } + + public int toMobDeathCount() { + return toDeathCount() - toPlayerDeathCount(); + } + + public int toDeathCount() { + return sessionsMutator.toDeathCount(); + } + + public List toTopWeapons(int limit) { + return kills.stream() + .map(PlayerKill::getWeapon) + .collect(HashMap::new, (map, weapon) -> map.put(weapon, map.getOrDefault(weapon, 0) + 1), (mapOne, mapTwo) -> { + }) // Collected to Map with weapon counts + .entrySet().stream() + .sorted((one, two) -> Integer.compare(two.getValue(), one.getValue())) // Highest first + .limit(limit) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayersMutator.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayersMutator.java index 1b1d60b36..2a93fdb44 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayersMutator.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.keys.ServerKeys; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.DateObj; +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.keys.ServerKeys; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.gathering.domain.GeoInfo; +import com.djrapitops.plan.gathering.domain.Ping; +import com.djrapitops.plan.gathering.domain.Session; import com.djrapitops.plugin.api.TimeAmount; import java.util.*; @@ -32,7 +32,7 @@ import java.util.function.Predicate; import java.util.stream.Collectors; /** - * Mutator for a bunch of {@link com.djrapitops.plan.data.store.containers.PlayerContainer}s. + * Mutator for a bunch of {@link PlayerContainer}s. * * @author Rsl1122 */ @@ -86,8 +86,8 @@ public class PlayersMutator { ); } - public PlayersMutator filterActive(long date, long msThreshold, int loginThreshold, double limit) { - return filterBy(player -> player.getActivityIndex(date, msThreshold, loginThreshold).getValue() >= limit); + public PlayersMutator filterActive(long date, long msThreshold, double limit) { + return filterBy(player -> player.getActivityIndex(date, msThreshold).getValue() >= limit); } public PlayersMutator filterPlayedOnServer(UUID serverUUID) { @@ -139,7 +139,7 @@ public class PlayersMutator { return pingPerCountry; } - public TreeMap>> toActivityDataMap(long date, long msThreshold, int loginThreshold) { + public TreeMap>> toActivityDataMap(long date, long msThreshold) { TreeMap>> activityData = new TreeMap<>(); for (long time = date; time >= date - TimeAmount.MONTH.toMillis(2L); time -= TimeAmount.WEEK.toMillis(1L)) { Map> map = activityData.getOrDefault(time, new HashMap<>()); @@ -148,7 +148,7 @@ public class PlayersMutator { if (player.getValue(PlayerKeys.REGISTERED).orElse(0L) > time) { continue; } - ActivityIndex activityIndex = player.getActivityIndex(time, msThreshold, loginThreshold); + ActivityIndex activityIndex = player.getActivityIndex(time, msThreshold); String activityGroup = activityIndex.getGroup(); Set uuids = map.getOrDefault(activityGroup, new HashSet<>()); @@ -199,8 +199,7 @@ public class PlayersMutator { Iterable compareTo, long dateLimit, PlayersOnlineResolver onlineResolver, - long activityMsThreshold, - int activityLoginThreshold + long activityMsThreshold ) { Collection retainedAfterMonth = new ArrayList<>(); Collection notRetainedAfterMonth = new ArrayList<>(); @@ -227,10 +226,10 @@ public class PlayersMutator { } List retained = retainedAfterMonth.stream() - .map(player -> new RetentionData(player, onlineResolver, activityMsThreshold, activityLoginThreshold)) + .map(player -> new RetentionData(player, onlineResolver, activityMsThreshold)) .collect(Collectors.toList()); List notRetained = notRetainedAfterMonth.stream() - .map(player -> new RetentionData(player, onlineResolver, activityMsThreshold, activityLoginThreshold)) + .map(player -> new RetentionData(player, onlineResolver, activityMsThreshold)) .collect(Collectors.toList()); RetentionData avgRetained = RetentionData.average(retained); @@ -238,7 +237,7 @@ public class PlayersMutator { List toBeRetained = new ArrayList<>(); for (PlayerContainer player : compareTo) { - RetentionData retentionData = new RetentionData(player, onlineResolver, activityMsThreshold, activityLoginThreshold); + RetentionData retentionData = new RetentionData(player, onlineResolver, activityMsThreshold); if (retentionData.distance(avgRetained) < retentionData.distance(avgNotRetained)) { toBeRetained.add(player); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersOnlineResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayersOnlineResolver.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersOnlineResolver.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayersOnlineResolver.java index 6ef9d22cf..376cd46ac 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayersOnlineResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PlayersOnlineResolver.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.utilities.html.graphs.line.Point; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point; import java.util.List; import java.util.Map; @@ -49,6 +49,14 @@ public class PlayersOnlineResolver extends TreeMap { return Optional.of(entry.getValue()); } + public int findLonelyJoins(List joinDates) { + int lonely = 0; + for (Long joinDate : joinDates) { + if (getOnlineOn(joinDate).orElse(-1) == 0) lonely++; + } + return lonely; + } + public boolean isServerOnline(long date, long timeLimit) { Long lastEntry = floorKey(date); return date - lastEntry < timeLimit; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PvpInfoMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PvpInfoMutator.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PvpInfoMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PvpInfoMutator.java index 0932167cb..479b73a5a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PvpInfoMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/PvpInfoMutator.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.DataContainer; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.gathering.domain.Session; import java.util.List; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/RetentionData.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/RetentionData.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/RetentionData.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/RetentionData.java index f3b46c812..5a5ccc923 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/RetentionData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/RetentionData.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.google.common.base.Objects; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; import java.util.Collection; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -60,16 +60,14 @@ public class RetentionData { public RetentionData( PlayerContainer player, PlayersOnlineResolver onlineOnJoin, - long activityMsThreshold, - int activityLoginThreshold + long activityMsThreshold ) { Optional registeredValue = player.getValue(PlayerKeys.REGISTERED); activityIndex = registeredValue .map(registered -> new ActivityIndex( player, registered + TimeUnit.DAYS.toMillis(1L), - activityMsThreshold, - activityLoginThreshold + activityMsThreshold ).getValue()) .orElse(0.0); this.onlineOnJoin = registeredValue @@ -77,6 +75,18 @@ public class RetentionData { .orElse(0); } + public static int countRetentionPrediction( + Collection newPlayers, + ActivityIndex retained, + ActivityIndex nonRetained + ) { + int count = 0; + for (ActivityIndex player : newPlayers) { + if (player.distance(retained) < player.distance(nonRetained)) count++; + } + return count; + } + public double distance(RetentionData data) { double num = 0; num += Math.abs(data.activityIndex - activityIndex) * 2.0; @@ -93,12 +103,12 @@ public class RetentionData { if (o == null || getClass() != o.getClass()) return false; RetentionData that = (RetentionData) o; return Double.compare(that.activityIndex, activityIndex) == 0 && - Objects.equal(onlineOnJoin, that.onlineOnJoin); + Double.compare(that.onlineOnJoin, onlineOnJoin) == 0; } @Override public int hashCode() { - return Objects.hashCode(activityIndex, onlineOnJoin); + return Objects.hash(activityIndex, onlineOnJoin); } public double getOnlineOnJoin() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/SessionsMutator.java similarity index 60% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/SessionsMutator.java index de1e11be3..1a960d919 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/SessionsMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/SessionsMutator.java @@ -14,18 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.container.PlayerDeath; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.keys.CommonKeys; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.keys.CommonKeys; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs; +import com.djrapitops.plan.delivery.rendering.json.graphs.pie.WorldPie; +import com.djrapitops.plan.gathering.domain.PlayerDeath; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.WorldTimes; +import com.djrapitops.plan.settings.config.WorldAliasSettings; import com.djrapitops.plan.utilities.analysis.Median; import java.util.*; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -52,6 +57,11 @@ public class SessionsMutator { return sessions; } + public SessionsMutator sort(Comparator sessionComparator) { + sessions.sort(sessionComparator); + return this; + } + public SessionsMutator filterBy(Predicate predicate) { return new SessionsMutator(sessions.stream() .filter(predicate) @@ -91,11 +101,12 @@ public class SessionsMutator { .collect(Collectors.toList()); } + /** + * @deprecated Incorrect results. + */ + @Deprecated public List toPlayerDeathList() { - return sessions.stream() - .map(session -> session.getValue(SessionKeys.PLAYER_DEATHS).orElse(new ArrayList<>())) - .flatMap(Collection::stream) - .collect(Collectors.toList()); + return Collections.emptyList(); } public int toMobKillCount() { @@ -228,7 +239,75 @@ public class SessionsMutator { } public int toPlayerDeathCount() { - return toPlayerDeathList().size(); + return sessions.stream().mapToInt(session -> session.getValue(SessionKeys.DEATH_COUNT).orElse(0)).sum(); } + public List toSessionStarts() { + return sessions.stream() + .map(Session::getDate) + .sorted() + .collect(Collectors.toList()); + } + + public double toAveragePlayersOnline(PlayersOnlineResolver playersOnlineResolver) { + return sessions.stream().map(session -> playersOnlineResolver.getOnlineOn(session.getDate())) + .filter(Optional::isPresent) + .mapToDouble(Optional::get) + .average().orElse(0.0); + } + + public List> toPlayerNameJSONMaps( + Graphs graphs, + WorldAliasSettings worldAliasSettings, + Formatters formatters + ) { + return toJSONMaps(graphs, worldAliasSettings, formatters, + sessionMap -> sessionMap.get("player_name")); + } + + public List> toServerNameJSONMaps( + Graphs graphs, + WorldAliasSettings worldAliasSettings, + Formatters formatters + ) { + return toJSONMaps(graphs, worldAliasSettings, formatters, + sessionMap -> sessionMap.get("server_name")); + } + + private List> toJSONMaps( + Graphs graphs, + WorldAliasSettings worldAliasSettings, + Formatters formatters, + Function, Object> nameFunction + ) { + return sessions.stream().map(session -> { + Map sessionMap = new HashMap<>(); + sessionMap.put("player_name", session.getValue(SessionKeys.NAME).orElse(session.getUnsafe(SessionKeys.UUID).toString())); + sessionMap.put("server_name", session.getValue(SessionKeys.SERVER_NAME).orElse(session.getUnsafe(SessionKeys.SERVER_UUID).toString())); + sessionMap.put("name", nameFunction.apply(sessionMap)); + sessionMap.put("start", session.getValue(SessionKeys.START).map(formatters.yearLong()).orElse("-") + + (session.supports(SessionKeys.END) ? "" : " (Online)")); + sessionMap.put("end", session.getValue(SessionKeys.END).map(formatters.yearLong()).orElse("Online")); + sessionMap.put("most_used_world", worldAliasSettings.getLongestWorldPlayed(session)); + sessionMap.put("length", session.getValue(SessionKeys.LENGTH).map(formatters.timeAmount()).orElse("-")); + sessionMap.put("afk_time", session.getValue(SessionKeys.AFK_TIME).map(formatters.timeAmount()).orElse("-")); + sessionMap.put("mob_kills", session.getValue(SessionKeys.MOB_KILL_COUNT).orElse(0)); + sessionMap.put("deaths", session.getValue(SessionKeys.DEATH_COUNT).orElse(0)); + sessionMap.put("player_kills", session.getPlayerKills().stream().map( + kill -> { + Map killMap = new HashMap<>(); + killMap.put("date", formatters.secondLong().apply(kill.getDate())); + killMap.put("victim", kill.getVictimName()); + killMap.put("killer", sessionMap.get("player_name")); + killMap.put("weapon", kill.getWeapon()); + return killMap; + } + ).collect(Collectors.toList())); + sessionMap.put("first_session", session.getValue(SessionKeys.FIRST_SESSION).orElse(false)); + WorldPie worldPie = graphs.pie().worldPie(session.getValue(SessionKeys.WORLD_TIMES).orElse(new WorldTimes())); + sessionMap.put("world_series", worldPie.getSlices()); + sessionMap.put("gm_series", worldPie.toHighChartsDrillDownMaps()); + return sessionMap; + }).collect(Collectors.toList()); + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/TPSMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/TPSMutator.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/TPSMutator.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/TPSMutator.java index 7ac0f7994..aa92e9241 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/TPSMutator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/mutators/TPSMutator.java @@ -14,16 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.store.mutators; +package com.djrapitops.plan.delivery.domain.mutators; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.keys.ServerKeys; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.keys.ServerKeys; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point; +import com.djrapitops.plan.gathering.domain.TPS; import com.djrapitops.plan.utilities.comparators.TPSComparator; -import com.djrapitops.plan.utilities.html.graphs.line.Point; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -61,6 +62,10 @@ public class TPSMutator { return filterBy(tps -> tps.getDate() >= after && tps.getDate() <= before); } + public TPSMutator filterTPSBetween(int above, int below) { + return filterBy(tps -> tps.getTicksPerSecond() > above && tps.getTicksPerSecond() < below); + } + public List all() { return tpsData; } @@ -130,30 +135,27 @@ public class TPSMutator { return downTime; } - public long serverIdleTime() { + public long serverOccupiedTime() { long lastDate = -1; - int lastPlayers = 0; - long idleTime = 0; + long activeTime = 0; tpsData.sort(new TPSComparator()); for (TPS tps : tpsData) { long date = tps.getDate(); - int players = tps.getPlayers(); if (lastDate == -1) { lastDate = date; - lastPlayers = players; continue; } + int players = tps.getPlayers(); long diff = date - lastDate; - if (lastPlayers == 0 && players == 0) { - idleTime += diff; + if (players > 0 && diff <= TimeUnit.MINUTES.toMillis(3L)) { + activeTime += diff; } lastDate = date; - lastPlayers = players; } - return idleTime; + return activeTime; } public double percentageTPSAboveThreshold(int threshold) { @@ -245,4 +247,17 @@ public class TPSMutator { .filter(num -> num >= 0) .min().orElse(-1); } + + public double averagePlayersOnline() { + return tpsData.stream() + .mapToDouble(TPS::getPlayers) + .average().orElse(-1); + } + + public Optional getLast() { + if (tpsData.isEmpty()) return Optional.empty(); + // else + tpsData.sort(new TPSComparator()); + return Optional.of(tpsData.get(tpsData.size() - 1)); + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/NetworkPageLang.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportPaths.java similarity index 51% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/NetworkPageLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportPaths.java index 2af6e06f3..684b4cad0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/NetworkPageLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportPaths.java @@ -14,34 +14,34 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.delivery.export; + +import org.apache.commons.lang3.StringUtils; + +import java.util.ArrayList; +import java.util.List; /** - * {@link Lang} implementation for player.html replacement values. + * Export utility that takes care of path replacement for different resources referenced in other files. * * @author Rsl1122 */ -public enum NetworkPageLang implements Lang { - NETWORK("Network"), - NETWORK_INFORMATION("NETWORK INFORMATION"), - PLAYERS_TEXT("Players"), - PLAYERS("PLAYERS"), - NEW_TEXT("New"), - HEALTH_ESTIMATE("Health Estimate"); +public class ExportPaths { - private final String defaultValue; + private List replace; + private List with; - NetworkPageLang(String defaultValue) { - this.defaultValue = defaultValue; + public ExportPaths() { + replace = new ArrayList<>(); + with = new ArrayList<>(); } - @Override - public String getIdentifier() { - return "HTML - " + name(); + public String resolveExportPaths(String original) { + return StringUtils.replaceEach(original, replace.toArray(new String[0]), with.toArray(new String[0])); } - @Override - public String getDefault() { - return defaultValue; + public void put(String replace, String with) { + this.replace.add(replace); + this.with.add(with); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportScheduler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportScheduler.java new file mode 100644 index 000000000..ab5247b01 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportScheduler.java @@ -0,0 +1,108 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.TaskSystem; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.ExportSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plugin.api.TimeAmount; +import com.djrapitops.plugin.logging.console.PluginLogger; +import com.djrapitops.plugin.logging.error.ErrorHandler; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Collection; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +/** + * Schedules export tasks so that they are not all run at once. + * + * @author Rsl1122 + */ +@Singleton +public class ExportScheduler { + + private final PlanConfig config; + private final DBSystem dbSystem; + private final TaskSystem taskSystem; + + private final Exporter exporter; + private final PluginLogger logger; + private final ErrorHandler errorHandler; + + @Inject + public ExportScheduler( + PlanConfig config, + DBSystem dbSystem, + TaskSystem taskSystem, + Exporter exporter, + PluginLogger logger, + ErrorHandler errorHandler + ) { + this.config = config; + this.dbSystem = dbSystem; + this.taskSystem = taskSystem; + this.exporter = exporter; + this.logger = logger; + this.errorHandler = errorHandler; + } + + public void scheduleExport() { + scheduleServerPageExport(); + schedulePlayersPageExport(); + } + + private void schedulePlayersPageExport() { + long period = TimeAmount.toTicks(config.get(ExportSettings.EXPORT_PERIOD), TimeUnit.MILLISECONDS); + taskSystem.registerTask("Players page export", + new ExportTask(exporter, Exporter::exportPlayersPage, logger, errorHandler) + ).runTaskTimerAsynchronously(0L, period); + } + + private void scheduleServerPageExport() { + if (!config.get(ExportSettings.SERVER_PAGE)) return; + + Collection servers = dbSystem.getDatabase().query(ServerQueries.fetchPlanServerInformationCollection()); + int serverCount = servers.size(); + if (serverCount == 0) return; + + long period = TimeAmount.toTicks(config.get(ExportSettings.EXPORT_PERIOD), TimeUnit.MILLISECONDS); + long offset = period / serverCount; + + Optional proxy = servers.stream().filter(Server::isProxy).findFirst(); + proxy.ifPresent(mainServer -> taskSystem.registerTask("Network export", + new ExportTask(exporter, exporter -> exporter.exportServerPage(mainServer), logger, errorHandler)) + .runTaskTimerAsynchronously(0L, period) + ); + + int offsetMultiplier = proxy.isPresent() ? 1 : 0; // Delay first server export if on a network. + for (Server server : servers) { + taskSystem.registerTask("Server export", + new ExportTask(exporter, same -> { + same.exportServerPage(server); + same.exportServerJSON(server); + }, logger, errorHandler)) + .runTaskTimerAsynchronously(offset * offsetMultiplier, period); + offsetMultiplier++; + } + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportSystem.java new file mode 100644 index 000000000..96648a234 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportSystem.java @@ -0,0 +1,66 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * System in charge of exporting html. + * + * @author Rsl1122 + */ +@Singleton +public class ExportSystem implements SubSystem { + + private final DBSystem dbSystem; + private final ServerInfo serverInfo; + private final ExportScheduler exportScheduler; + + @Inject + public ExportSystem( + DBSystem dbSystem, + ServerInfo serverInfo, + ExportScheduler exportScheduler + ) { + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + this.exportScheduler = exportScheduler; + } + + @Override + public void enable() { + Database database = dbSystem.getDatabase(); + boolean hasProxy = database.query(ServerQueries.fetchProxyServerInformation()).isPresent(); + if (serverInfo.getServer().isNotProxy() && hasProxy) { + return; + } + + exportScheduler.scheduleExport(); + } + + @Override + public void disable() { + // Nothing to disable + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportTask.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportTask.java new file mode 100644 index 000000000..d27c05ee1 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ExportTask.java @@ -0,0 +1,57 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.exceptions.ExportException; +import com.djrapitops.plan.utilities.java.ThrowingConsumer; +import com.djrapitops.plugin.logging.L; +import com.djrapitops.plugin.logging.console.PluginLogger; +import com.djrapitops.plugin.logging.error.ErrorHandler; +import com.djrapitops.plugin.task.AbsRunnable; + +public class ExportTask extends AbsRunnable { + + private final Exporter exporter; + private final ThrowingConsumer exportAction; + private final PluginLogger logger; + private final ErrorHandler errorHandler; + + public ExportTask( + Exporter exporter, + ThrowingConsumer exportAction, + PluginLogger logger, + ErrorHandler errorHandler + ) { + this.exporter = exporter; + this.exportAction = exportAction; + this.logger = logger; + this.errorHandler = errorHandler; + } + + @Override + public void run() { + try { + exportAction.accept(exporter); + } catch (ExportException e) { + errorHandler.log(L.WARN, this.getClass(), e); + } catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) { + logger.error("Export Task Disabled due to error, reload Plan to re-enable."); + errorHandler.log(L.ERROR, this.getClass(), e); + cancel(); + } + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/Exporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/Exporter.java new file mode 100644 index 000000000..a7298c0a1 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/Exporter.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.exceptions.ExportException; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.exceptions.connection.NotFoundException; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.ExportSettings; +import com.djrapitops.plan.storage.file.PlanFiles; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * Handles export for different pages. + * + * @author Rsl1122 + */ +@Singleton +public class Exporter extends FileExporter { + + private final PlanFiles files; + private final PlanConfig config; + private final PlayerJSONExporter playerJSONExporter; + private final PlayerPageExporter playerPageExporter; + private final PlayersPageExporter playersPageExporter; + private final ServerPageExporter serverPageExporter; + private final NetworkPageExporter networkPageExporter; + + private final Set failedServers; + + @Inject + public Exporter( + PlanFiles files, + PlanConfig config, + PlayerJSONExporter playerJSONExporter, + PlayerPageExporter playerPageExporter, + PlayersPageExporter playersPageExporter, + ServerPageExporter serverPageExporter, + NetworkPageExporter networkPageExporter + ) { + this.files = files; + this.config = config; + this.playerJSONExporter = playerJSONExporter; + this.playerPageExporter = playerPageExporter; + this.playersPageExporter = playersPageExporter; + this.serverPageExporter = serverPageExporter; + this.networkPageExporter = networkPageExporter; + + failedServers = new HashSet<>(); + } + + private Path getPageExportDirectory() { + Path exportDirectory = Paths.get(config.get(ExportSettings.HTML_EXPORT_PATH)); + return exportDirectory.isAbsolute() + ? exportDirectory + : files.getDataDirectory().resolve(exportDirectory); + } + + private Path getJSONExportDirectory() { + Path exportDirectory = Paths.get(config.get(ExportSettings.JSON_EXPORT_PATH)); + return exportDirectory.isAbsolute() + ? exportDirectory + : files.getDataDirectory().resolve(exportDirectory); + } + + /** + * Export a page of a server. + * + * @param server Server which page is going to be exported + * @return false if the page was not exported due to previous failure or is disabled in config. + * @throws ExportException If the export failed due to IO, NotFound or ParseException. + */ + public boolean exportServerPage(Server server) throws ExportException { + UUID serverUUID = server.getUuid(); + if (failedServers.contains(serverUUID) || !config.get(ExportSettings.SERVER_PAGE)) return false; + + try { + Path toDirectory = getPageExportDirectory(); + if (server.isProxy()) { + networkPageExporter.export(toDirectory, server); + } else { + serverPageExporter.export(toDirectory, server); + } + return true; + } catch (IOException | NotFoundException | ParseException e) { + failedServers.add(serverUUID); + throw new ExportException("Failed to export server: " + server.getIdentifiableName() + " (Attempts disabled until next reload), " + e.toString(), e); + } + } + + public boolean exportServerJSON(Server server) throws ExportException { + UUID serverUUID = server.getUuid(); + if (failedServers.contains(serverUUID) || !config.get(ExportSettings.SERVER_JSON)) return false; + + try { + Path toDirectory = getJSONExportDirectory().resolve(toFileName(server.getName())); + if (server.isProxy()) { + networkPageExporter.exportJSON(toDirectory, server); + } else { + serverPageExporter.exportJSON(toDirectory, server); + } + return true; + } catch (IOException | NotFoundException e) { + failedServers.add(serverUUID); + throw new ExportException("Failed to export server: " + server.getIdentifiableName() + " (Attempts disabled until next reload), " + e.toString(), e); + } + } + + /** + * Export page of a player. + * + * @param playerUUID UUID of the player. + * @param playerName Name of the player. + * @return false if the page was not exported due to config settings. + * @throws ExportException If the export failed due to IO, NotFound or ParseException. + */ + public boolean exportPlayerPage(UUID playerUUID, String playerName) throws ExportException { + Path toDirectory = getPageExportDirectory(); + if (!config.get(ExportSettings.PLAYER_PAGES)) return false; + + try { + playerPageExporter.export(toDirectory, playerUUID, playerName); + return true; + } catch (IOException | NotFoundException | ParseException e) { + throw new ExportException("Failed to export player: " + playerName + ", " + e.toString(), e); + } + } + + public boolean exportPlayersPage() throws ExportException { + Path toDirectory = getPageExportDirectory(); + if (!config.get(ExportSettings.PLAYERS_PAGE)) return false; + + try { + playersPageExporter.export(toDirectory); + return true; + } catch (IOException | NotFoundException | ParseException e) { + throw new ExportException("Failed to export players page, " + e.toString(), e); + } + } + + /** + * Export Raw Data JSON of a player. + * + * @param playerUUID UUID of the player. + * @param playerName Name of the player. + * @return false if the json was not exported due to config settings. + * @throws ExportException If the export failed due to IOException. + */ + public boolean exportPlayerJSON(UUID playerUUID, String playerName) throws ExportException { + Path toDirectory = getJSONExportDirectory(); + if (!config.get(ExportSettings.PLAYER_JSON)) return false; + + try { + playerJSONExporter.export(toDirectory, playerUUID, playerName); + return true; + } catch (IOException e) { + throw new ExportException("Failed to export player: " + playerName + ", " + e.toString(), e); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/FileExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/FileExporter.java new file mode 100644 index 000000000..2ca58d048 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/FileExporter.java @@ -0,0 +1,83 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.storage.file.Resource; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.List; + +/** + * Code that handles writing the actual files that are exported. + * + * @author Rsl1122 + */ +abstract class FileExporter { + + private static void copy(InputStream in, OutputStream out) throws IOException { + int read; + byte[] bytes = new byte[1024]; + + while ((read = in.read(bytes)) != -1) { + out.write(bytes, 0, read); + } + } + + void export(Path to, List content) throws IOException { + Files.createDirectories(to.getParent()); + Files.write(to, content, StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); + } + + void export(Path to, String content) throws IOException { + Files.createDirectories(to.getParent()); + Files.write(to, Arrays.asList(StringUtils.split(content, "\r\n")), StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); + } + + void export(Path to, Resource resource) throws IOException { + Files.createDirectories(to.getParent()); + + try ( + InputStream in = resource.asInputStream(); + OutputStream out = Files.newOutputStream(to, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE) + ) { + copy(in, out); + } + } + + String toFileName(String resourceName) { + try { + return StringUtils.replaceEach( + URLEncoder.encode(resourceName, "UTF-8"), + new String[]{".", "%2F"}, + new String[]{"%2E", "-"} + ); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException("Unexpected: UTF-8 encoding not supported", e); + } + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/NetworkPageExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/NetworkPageExporter.java new file mode 100644 index 000000000..dda24d30a --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/NetworkPageExporter.java @@ -0,0 +1,202 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.delivery.rendering.pages.Page; +import com.djrapitops.plan.delivery.rendering.pages.PageFactory; +import com.djrapitops.plan.delivery.webserver.RequestTarget; +import com.djrapitops.plan.delivery.webserver.pages.json.RootJSONHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.errors.ErrorResponse; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.exceptions.connection.NotFoundException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.storage.file.Resource; +import org.apache.commons.lang3.StringUtils; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; + +/** + * Handles exporting of /network page html, data and resources. + * + * @author Rsl1122 + */ +@Singleton +public class NetworkPageExporter extends FileExporter { + + private final PlanFiles files; + private final PageFactory pageFactory; + private final RootJSONHandler jsonHandler; + private final Locale locale; + private final Theme theme; + + private final ExportPaths exportPaths; + + @Inject + public NetworkPageExporter( + PlanFiles files, + PageFactory pageFactory, + RootJSONHandler jsonHandler, + Locale locale, + Theme theme + ) { + this.files = files; + this.pageFactory = pageFactory; + this.jsonHandler = jsonHandler; + this.locale = locale; + this.theme = theme; + + exportPaths = new ExportPaths(); + } + + public void export(Path toDirectory, Server server) throws IOException, NotFoundException, ParseException { + exportPaths.put("./players", toRelativePathFromRoot("players")); + exportRequiredResources(toDirectory); + exportJSON(toDirectory, server); + exportHtml(toDirectory); + } + + private void exportHtml(Path toDirectory) throws IOException, ParseException { + Path to = toDirectory + .resolve("network") + .resolve("index.html"); + + Page page = pageFactory.networkPage(); + export(to, exportPaths.resolveExportPaths(locale.replaceLanguageInHtml(page.toHtml()))); + } + + public void exportJSON(Path toDirectory, Server server) throws IOException, NotFoundException { + String serverUUID = server.getUuid().toString(); + + exportJSON(toDirectory, + "network/overview", + "network/servers", + "network/sessionsOverview", + "network/playerbaseOverview", + "graph?type=playersOnline&server=" + serverUUID, + "graph?type=uniqueAndNew", + "graph?type=serverPie", + "graph?type=activity", + "graph?type=geolocation", + "graph?type=uniqueAndNew", + "network/pingTable", + "sessions" + ); + } + + private void exportJSON(Path toDirectory, String... resources) throws NotFoundException, IOException { + for (String resource : resources) { + exportJSON(toDirectory, resource); + } + } + + private void exportJSON(Path toDirectory, String resource) throws NotFoundException, IOException { + Response found = getJSONResponse(resource); + if (found instanceof ErrorResponse) { + throw new NotFoundException(resource + " was not properly exported: " + found.getContent()); + } + + String jsonResourceName = toFileName(toJSONResourceName(resource)) + ".json"; + + export(toDirectory.resolve("data").resolve(jsonResourceName), found.getContent()); + exportPaths.put("../v1/" + resource, toRelativePathFromRoot("data/" + jsonResourceName)); + } + + private String toJSONResourceName(String resource) { + return StringUtils.replaceEach(resource, new String[]{"?", "&", "type=", "server="}, new String[]{"-", "_", "", ""}); + } + + private Response getJSONResponse(String resource) { + try { + return jsonHandler.getResponse(null, new RequestTarget(URI.create(resource))); + } catch (WebException e) { + // The rest of the exceptions should not be thrown + throw new IllegalStateException("Unexpected exception thrown: " + e.toString(), e); + } + } + + private void exportRequiredResources(Path toDirectory) throws IOException { + exportImage(toDirectory, "img/Flaticon_circle.png"); + + exportResources(toDirectory, + "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", + "js/sb-admin-2.js", + "js/xmlhttprequests.js", + "js/color-selector.js", + "js/sessionAccordion.js", + "js/pingTable.js", + "js/graphs.js", + "js/network-values.js" + ); + } + + private void exportResources(Path toDirectory, String... resourceNames) throws IOException { + for (String resourceName : resourceNames) { + exportResource(toDirectory, resourceName); + } + } + + private void exportResource(Path toDirectory, String resourceName) throws IOException { + Resource resource = files.getCustomizableResourceOrDefault("web/" + resourceName); + Path to = toDirectory.resolve(resourceName); + if (resourceName.endsWith(".css")) { + export(to, theme.replaceThemeColors(resource.asString())); + } else { + export(to, resource.asLines()); + } + + exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); + } + + private void exportImage(Path toDirectory, String resourceName) throws IOException { + Resource resource = files.getCustomizableResourceOrDefault("web/" + resourceName); + Path to = toDirectory.resolve(resourceName); + export(to, resource); + + exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); + } + + private String toRelativePathFromRoot(String resourceName) { + // Network html is exported at /network//index.html or /server/index.html + return "../" + toNonRelativePath(resourceName); + } + + private String toNonRelativePath(String resourceName) { + return StringUtils.remove(resourceName, "../"); + } + +} \ No newline at end of file diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/VelocityBridge.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerJSONExporter.java similarity index 50% rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/VelocityBridge.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerJSONExporter.java index 6572d25fb..0536c1d2f 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/VelocityBridge.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerJSONExporter.java @@ -14,40 +14,39 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.pluginbridge.plan; +package com.djrapitops.plan.delivery.export; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.djrapitops.pluginbridge.plan.buycraft.BuyCraftHook; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; import javax.inject.Inject; import javax.inject.Singleton; +import java.io.IOException; +import java.nio.file.Path; +import java.util.UUID; /** - * Plugin bridge for Velocity plugins. + * Handles exporting of player json. * * @author Rsl1122 */ @Singleton -public class VelocityBridge extends AbstractBridge { +public class PlayerJSONExporter extends FileExporter { - private final BuyCraftHook buyCraftHook; + private final ResponseFactory responseFactory; @Inject - public VelocityBridge( - PlanConfig config, - ErrorHandler errorHandler, - - BuyCraftHook buyCraftHook + public PlayerJSONExporter( + ResponseFactory responseFactory ) { - super(config, errorHandler); - this.buyCraftHook = buyCraftHook; + this.responseFactory = responseFactory; } - @Override - Hook[] getHooks() { - return new Hook[]{ - buyCraftHook - }; + public void export(Path toDirectory, UUID playerUUID, String playerName) throws IOException { + Path to = toDirectory.resolve("player/" + toFileName(playerName) + ".json"); + exportJSON(to, playerUUID); + } + + private void exportJSON(Path to, UUID playerUUID) throws IOException { + export(to, responseFactory.rawPlayerPageResponse(playerUUID).getContent()); } } \ No newline at end of file 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 new file mode 100644 index 000000000..1113f65cd --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayerPageExporter.java @@ -0,0 +1,190 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.delivery.rendering.pages.Page; +import com.djrapitops.plan.delivery.rendering.pages.PageFactory; +import com.djrapitops.plan.delivery.webserver.RequestTarget; +import com.djrapitops.plan.delivery.webserver.pages.json.RootJSONHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.errors.ErrorResponse; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.exceptions.connection.NotFoundException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.storage.file.Resource; +import org.apache.commons.lang3.StringUtils; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.UUID; + +/** + * Handles exporting of /player page html, data and resources. + * + * @author Rsl1122 + */ +@Singleton +public class PlayerPageExporter extends FileExporter { + + private final PlanFiles files; + private final PageFactory pageFactory; + private final RootJSONHandler jsonHandler; + private final Locale locale; + private final Theme theme; + + private final ExportPaths exportPaths; + + @Inject + public PlayerPageExporter( + PlanFiles files, + PageFactory pageFactory, + RootJSONHandler jsonHandler, + Locale locale, + Theme theme + ) { + this.files = files; + this.pageFactory = pageFactory; + this.jsonHandler = jsonHandler; + this.locale = locale; + this.theme = theme; + + exportPaths = new ExportPaths(); + } + + public void export(Path toDirectory, UUID playerUUID, String playerName) throws IOException, NotFoundException, ParseException { + exportPaths.put("../network", toRelativePathFromRoot("network")); + exportPaths.put("../server", toRelativePathFromRoot("server")); + exportRequiredResources(toDirectory); + + Path playerDirectory = toDirectory.resolve("player/" + toFileName(playerName)); + exportJSON(playerDirectory, playerUUID); + exportHtml(playerDirectory, playerUUID); + } + + private void exportHtml(Path playerDirectory, UUID playerUUID) throws IOException, ParseException, NotFoundException { + Path to = playerDirectory.resolve("index.html"); + + try { + Page page = pageFactory.playerPage(playerUUID); + export(to, exportPaths.resolveExportPaths(locale.replaceLanguageInHtml(page.toHtml()))); + } catch (IllegalStateException notFound) { + throw new NotFoundException(notFound.getMessage()); + } + } + + private void exportJSON(Path toDirectory, UUID playerUUID) throws IOException, NotFoundException { + exportJSON(toDirectory, "player?player=" + playerUUID); + } + + private void exportJSON(Path toDirectory, String resource) throws NotFoundException, IOException { + Response found = getJSONResponse(resource); + if (found instanceof ErrorResponse) { + throw new NotFoundException(resource + " was not properly exported: " + found.getContent()); + } + + String jsonResourceName = toFileName(toJSONResourceName(resource)) + ".json"; + + export(toDirectory.resolve(jsonResourceName), found.getContent()); + exportPaths.put("../v1/" + resource, "./" + jsonResourceName); + } + + private String toJSONResourceName(String resource) { + return StringUtils.replaceEach(resource, new String[]{"?", "&", "type=", "server="}, new String[]{"-", "_", "", ""}); + } + + private Response getJSONResponse(String resource) { + try { + return jsonHandler.getResponse(null, new RequestTarget(URI.create(resource))); + } catch (WebException e) { + // The rest of the exceptions should not be thrown + throw new IllegalStateException("Unexpected exception thrown: " + e.toString(), e); + } + } + + private void exportRequiredResources(Path toDirectory) throws IOException { + exportImage(toDirectory, "img/Flaticon_circle.png"); + + // Style + exportResources(toDirectory, + "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", + "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(Path toDirectory, String... resourceNames) throws IOException { + for (String resourceName : resourceNames) { + exportResource(toDirectory, resourceName); + } + } + + private void exportResource(Path toDirectory, String resourceName) throws IOException { + Resource resource = files.getCustomizableResourceOrDefault("web/" + resourceName); + Path to = toDirectory.resolve(resourceName); + + if (resourceName.endsWith(".css")) { + export(to, theme.replaceThemeColors(resource.asString())); + } else { + export(to, resource.asLines()); + } + + exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); + } + + private void exportImage(Path toDirectory, String resourceName) throws IOException { + Resource resource = files.getCustomizableResourceOrDefault("web/" + resourceName); + Path to = toDirectory.resolve(resourceName); + export(to, resource); + + exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); + } + + private String toRelativePathFromRoot(String resourceName) { + // Player html is exported at /player//index.html + return "../../" + toNonRelativePath(resourceName); + } + + private String toNonRelativePath(String resourceName) { + return StringUtils.remove(resourceName, "../"); + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayersPageExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayersPageExporter.java new file mode 100644 index 000000000..c8f0f5c0d --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/PlayersPageExporter.java @@ -0,0 +1,172 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.delivery.rendering.pages.Page; +import com.djrapitops.plan.delivery.rendering.pages.PageFactory; +import com.djrapitops.plan.delivery.webserver.RequestTarget; +import com.djrapitops.plan.delivery.webserver.pages.json.RootJSONHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.errors.ErrorResponse; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.exceptions.connection.NotFoundException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.storage.file.Resource; +import org.apache.commons.lang3.StringUtils; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; + +/** + * Handles exporting of /players page html, data and resources. + * + * @author Rsl1122 + */ +@Singleton +public class PlayersPageExporter extends FileExporter { + + private final PlanFiles files; + private final PageFactory pageFactory; + private final RootJSONHandler jsonHandler; + private final Locale locale; + private final Theme theme; + private final ServerInfo serverInfo; + + private final ExportPaths exportPaths; + + @Inject + public PlayersPageExporter( + PlanFiles files, + PageFactory pageFactory, + RootJSONHandler jsonHandler, + Locale locale, + Theme theme, + ServerInfo serverInfo + ) { + this.files = files; + this.pageFactory = pageFactory; + this.jsonHandler = jsonHandler; + this.locale = locale; + this.theme = theme; + this.serverInfo = serverInfo; + + exportPaths = new ExportPaths(); + } + + public void export(Path toDirectory) throws IOException, NotFoundException, ParseException { + exportPaths.put("/", toRelativePathFromRoot(serverInfo.getServer().isProxy() ? "network" : "server")); + exportRequiredResources(toDirectory); + exportJSON(toDirectory); + exportHtml(toDirectory); + } + + private void exportHtml(Path toDirectory) throws IOException, ParseException { + Path to = toDirectory + .resolve("players") + .resolve("index.html"); + + Page page = pageFactory.playersPage(); + export(to, exportPaths.resolveExportPaths(locale.replaceLanguageInHtml(page.toHtml()))); + } + + private void exportJSON(Path toDirectory) throws NotFoundException, IOException { + Response found = getJSONResponse("players"); + if (found instanceof ErrorResponse) { + throw new NotFoundException("players page was not properly exported: " + found.getContent()); + } + + String jsonResourceName = toFileName(toJSONResourceName("players")) + ".json"; + + export(toDirectory.resolve("data").resolve(jsonResourceName), found.getContent()); + exportPaths.put("../v1/players", toRelativePathFromRoot("data/" + jsonResourceName)); + } + + private String toJSONResourceName(String resource) { + return StringUtils.replaceEach(resource, new String[]{"?", "&", "type=", "server="}, new String[]{"-", "_", "", ""}); + } + + private Response getJSONResponse(String resource) { + try { + return jsonHandler.getResponse(null, new RequestTarget(URI.create(resource))); + } catch (WebException e) { + // The rest of the exceptions should not be thrown + throw new IllegalStateException("Unexpected exception thrown: " + e.toString(), e); + } + } + + private void exportRequiredResources(Path toDirectory) throws IOException { + exportImage(toDirectory, "img/Flaticon_circle.png"); + + // Style + exportResources(toDirectory, + "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", + "js/sb-admin-2.js", + "js/xmlhttprequests.js", + "js/color-selector.js" + ); + } + + private void exportResources(Path toDirectory, String... resourceNames) throws IOException { + for (String resourceName : resourceNames) { + exportResource(toDirectory, resourceName); + } + } + + private void exportResource(Path toDirectory, String resourceName) throws IOException { + Resource resource = files.getCustomizableResourceOrDefault("web/" + resourceName); + Path to = toDirectory.resolve(resourceName); + + if (resourceName.endsWith(".css")) { + export(to, theme.replaceThemeColors(resource.asString())); + } else { + export(to, resource.asLines()); + } + + exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); + } + + private void exportImage(Path toDirectory, String resourceName) throws IOException { + Resource resource = files.getCustomizableResourceOrDefault("web/" + resourceName); + Path to = toDirectory.resolve(resourceName); + export(to, resource); + + exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); + } + + private String toRelativePathFromRoot(String resourceName) { + // Players html is exported at /players/index.html or /server/index.html + return "../" + toNonRelativePath(resourceName); + } + + private String toNonRelativePath(String resourceName) { + return StringUtils.remove(resourceName, "../"); + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ServerPageExporter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ServerPageExporter.java new file mode 100644 index 000000000..7f419ac82 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/export/ServerPageExporter.java @@ -0,0 +1,219 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.export; + +import com.djrapitops.plan.delivery.rendering.pages.Page; +import com.djrapitops.plan.delivery.rendering.pages.PageFactory; +import com.djrapitops.plan.delivery.webserver.RequestTarget; +import com.djrapitops.plan.delivery.webserver.pages.json.RootJSONHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.errors.ErrorResponse; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.exceptions.connection.NotFoundException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.storage.file.Resource; +import org.apache.commons.lang3.StringUtils; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.UUID; + +/** + * Handles exporting of /server page html, data and resources. + * + * @author Rsl1122 + */ +@Singleton +public class ServerPageExporter extends FileExporter { + + private final PlanFiles files; + private final PageFactory pageFactory; + private final RootJSONHandler jsonHandler; + private final Locale locale; + private final Theme theme; + private final ServerInfo serverInfo; + + private final ExportPaths exportPaths; + + @Inject + public ServerPageExporter( + PlanFiles files, + PageFactory pageFactory, + RootJSONHandler jsonHandler, + Locale locale, + Theme theme, + ServerInfo serverInfo // To know if current server is a Proxy + ) { + this.files = files; + this.pageFactory = pageFactory; + this.jsonHandler = jsonHandler; + this.locale = locale; + this.theme = theme; + this.serverInfo = serverInfo; + + exportPaths = new ExportPaths(); + } + + public void export(Path toDirectory, Server server) throws IOException, NotFoundException, ParseException { + exportPaths.put("../network", toRelativePathFromRoot("network")); + exportRequiredResources(toDirectory); + exportJSON(toDirectory, server); + exportHtml(toDirectory, server); + } + + private void exportHtml(Path toDirectory, Server server) throws IOException, NotFoundException, ParseException { + UUID serverUUID = server.getUuid(); + Path to = toDirectory + .resolve(serverInfo.getServer().isProxy() ? "server/" + toFileName(server.getName()) : "server") + .resolve("index.html"); + + Page page = pageFactory.serverPage(serverUUID); + export(to, exportPaths.resolveExportPaths(locale.replaceLanguageInHtml(page.toHtml()))); + } + + public void exportJSON(Path toDirectory, Server server) throws IOException, NotFoundException { + String serverUUID = server.getUuid().toString(); + + exportJSON(toDirectory, + "serverOverview?server=" + serverUUID, + "onlineOverview?server=" + serverUUID, + "sessionsOverview?server=" + serverUUID, + "playerVersus?server=" + serverUUID, + "playerbaseOverview?server=" + serverUUID, + "performanceOverview?server=" + serverUUID, + "graph?type=performance&server=" + serverUUID, + "graph?type=aggregatedPing&server=" + serverUUID, + "graph?type=worldPie&server=" + serverUUID, + "graph?type=activity&server=" + serverUUID, + "graph?type=geolocation&server=" + serverUUID, + "graph?type=uniqueAndNew&server=" + serverUUID, + "graph?type=serverCalendar&server=" + serverUUID, + "graph?type=punchCard&server=" + serverUUID, + "players?server=" + serverUUID, + "kills?server=" + serverUUID, + "pingTable?server=" + serverUUID, + "sessions?server=" + serverUUID + ); + } + + private void exportJSON(Path toDirectory, String... resources) throws NotFoundException, IOException { + for (String resource : resources) { + exportJSON(toDirectory, resource); + } + } + + private void exportJSON(Path toDirectory, String resource) throws NotFoundException, IOException { + Response found = getJSONResponse(resource); + if (found instanceof ErrorResponse) { + throw new NotFoundException(resource + " was not properly exported: " + found.getContent()); + } + + String jsonResourceName = toFileName(toJSONResourceName(resource)) + ".json"; + + export(toDirectory.resolve("data").resolve(jsonResourceName), found.getContent()); + exportPaths.put("../v1/" + resource, toRelativePathFromRoot("data/" + jsonResourceName)); + } + + private String toJSONResourceName(String resource) { + return StringUtils.replaceEach(resource, new String[]{"?", "&", "type=", "server="}, new String[]{"-", "_", "", ""}); + } + + private Response getJSONResponse(String resource) { + try { + return jsonHandler.getResponse(null, new RequestTarget(URI.create(resource))); + } catch (WebException e) { + // The rest of the exceptions should not be thrown + throw new IllegalStateException("Unexpected exception thrown: " + e.toString(), e); + } + } + + private void exportRequiredResources(Path toDirectory) throws IOException { + exportImage(toDirectory, "img/Flaticon_circle.png"); + + // Style + exportResources(toDirectory, + "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", + "js/sb-admin-2.js", + "js/xmlhttprequests.js", + "js/color-selector.js", + "js/sessionAccordion.js", + "js/pingTable.js", + "js/graphs.js", + "js/server-values.js" + ); + } + + private void exportResources(Path toDirectory, String... resourceNames) throws IOException { + for (String resourceName : resourceNames) { + exportResource(toDirectory, resourceName); + } + } + + private void exportResource(Path toDirectory, String resourceName) throws IOException { + Resource resource = files.getCustomizableResourceOrDefault("web/" + resourceName); + Path to = toDirectory.resolve(resourceName); + + if (resourceName.endsWith(".css")) { + export(to, theme.replaceThemeColors(resource.asString())); + } else { + export(to, resource.asLines()); + } + + exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); + } + + private void exportImage(Path toDirectory, String resourceName) throws IOException { + Resource resource = files.getCustomizableResourceOrDefault("web/" + resourceName); + Path to = toDirectory.resolve(resourceName); + export(to, resource); + + exportPaths.put(resourceName, toRelativePathFromRoot(resourceName)); + } + + private String toRelativePathFromRoot(String resourceName) { + // Server html is exported at /server//index.html or /server/index.html + return (serverInfo.getServer().isProxy() ? "../../" : "../") + toNonRelativePath(resourceName); + } + + private String toNonRelativePath(String resourceName) { + return StringUtils.remove(resourceName, "../"); + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/DecimalFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/DecimalFormatter.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/DecimalFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/DecimalFormatter.java index ba289a4c9..24bbb51de 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/DecimalFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/DecimalFormatter.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting; +package com.djrapitops.plan.delivery.formatting; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.FormatSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.FormatSettings; import java.text.DecimalFormat; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/EntityNameFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/EntityNameFormatter.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/EntityNameFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/EntityNameFormatter.java index 1c062e591..3b3228d3f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/EntityNameFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/EntityNameFormatter.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting; +package com.djrapitops.plan.delivery.formatting; import com.djrapitops.plugin.utilities.Format; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/Formatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatter.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/Formatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatter.java index f3ad0b0e1..92fc5456d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/Formatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatter.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting; +package com.djrapitops.plan.delivery.formatting; import java.util.function.Function; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/Formatters.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatters.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/Formatters.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatters.java index 45f513291..697e14ffe 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/Formatters.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/Formatters.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting; +package com.djrapitops.plan.delivery.formatting; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.utilities.formatting.time.*; +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.formatting.time.*; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.locale.Locale; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/ItemNameFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/ItemNameFormatter.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/ItemNameFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/ItemNameFormatter.java index 14c404a3f..8190ccd14 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/ItemNameFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/ItemNameFormatter.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting; +package com.djrapitops.plan.delivery.formatting; import com.djrapitops.plugin.utilities.Format; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.TextStringBuilder; import java.util.Arrays; @@ -30,7 +31,7 @@ public class ItemNameFormatter implements Formatter { @Override public String apply(String name) { - String[] parts = name.split("_"); + String[] parts = StringUtils.split(name, '_'); TextStringBuilder builder = new TextStringBuilder(); builder.appendWithSeparators(Arrays.stream(parts).map(part -> new Format(part).capitalize()).iterator(), " "); return builder.toString(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/PercentageFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/PercentageFormatter.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/PercentageFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/PercentageFormatter.java index cdd9966a9..fe5890dd3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/PercentageFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/PercentageFormatter.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting; +package com.djrapitops.plan.delivery.formatting; /** * Formatter for percentages. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/PlaceholderReplacer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/PlaceholderReplacer.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/PlaceholderReplacer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/PlaceholderReplacer.java index 651cfdbe9..c37cf18af 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/PlaceholderReplacer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/PlaceholderReplacer.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting; +package com.djrapitops.plan.delivery.formatting; -import com.djrapitops.plan.data.store.PlaceholderKey; -import com.djrapitops.plan.data.store.containers.DataContainer; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.keys.PlaceholderKey; import org.apache.commons.text.StringSubstitutor; import java.io.Serializable; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/ClockFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/ClockFormatter.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/ClockFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/ClockFormatter.java index 0aff0214d..cc1a658b6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/ClockFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/ClockFormatter.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting.time; +package com.djrapitops.plan.delivery.formatting.time; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.FormatSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.FormatSettings; +import com.djrapitops.plan.settings.locale.Locale; /** * Formatter for a timestamp that only includes a clock. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DateFormatter.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DateFormatter.java index df692f256..8c8376163 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DateFormatter.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting.time; +package com.djrapitops.plan.delivery.formatting.time; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.GenericLang; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.FormatSettings; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.FormatSettings; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.GenericLang; import java.text.SimpleDateFormat; import java.util.TimeZone; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateHolderFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DateHolderFormatter.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateHolderFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DateHolderFormatter.java index 66056e393..83126743d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DateHolderFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DateHolderFormatter.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting.time; +package com.djrapitops.plan.delivery.formatting.time; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.formatting.Formatter; /** * Formatter for a DateHolder object that uses a different formatter. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DayFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DayFormatter.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DayFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DayFormatter.java index 60ff58052..e3bcc8e04 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/DayFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/DayFormatter.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting.time; +package com.djrapitops.plan.delivery.formatting.time; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.FormatSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.FormatSettings; +import com.djrapitops.plan.settings.locale.Locale; /** * Formatter for a timestamp which includes days as the smallest entry. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/ISO8601NoClockFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/ISO8601NoClockFormatter.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/ISO8601NoClockFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/ISO8601NoClockFormatter.java index 26ad793ed..0f41b05a2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/ISO8601NoClockFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/ISO8601NoClockFormatter.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting.time; +package com.djrapitops.plan.delivery.formatting.time; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.locale.Locale; /** * Formatter for a timestamp in ISO-8601 format without the clock. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/SecondFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/SecondFormatter.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/SecondFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/SecondFormatter.java index e77dc8ceb..f0785ca3c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/SecondFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/SecondFormatter.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting.time; +package com.djrapitops.plan.delivery.formatting.time; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.FormatSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.FormatSettings; +import com.djrapitops.plan.settings.locale.Locale; /** * Formatter for timestamp which includes seconds as the smallest entry. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/TimeAmountFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/TimeAmountFormatter.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/TimeAmountFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/TimeAmountFormatter.java index 1daeacc49..f7321bdaa 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/TimeAmountFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/TimeAmountFormatter.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting.time; +package com.djrapitops.plan.delivery.formatting.time; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.FormatSettings; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.FormatSettings; import org.apache.commons.lang3.StringUtils; /** @@ -45,7 +45,7 @@ public class TimeAmountFormatter implements Formatter { @Override public String apply(Long ms) { - if (ms <= 0) { + if (ms == null || ms < 0) { return "-"; } StringBuilder builder = new StringBuilder(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/YearFormatter.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/YearFormatter.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/YearFormatter.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/YearFormatter.java index 994680cb9..8c6367345 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/formatting/time/YearFormatter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/formatting/time/YearFormatter.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.formatting.time; +package com.djrapitops.plan.delivery.formatting.time; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.FormatSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.FormatSettings; +import com.djrapitops.plan.settings.locale.Locale; /** * Formatter for a timestamp which includes year, but not seconds. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/Html.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Html.java similarity index 58% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/Html.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Html.java index 895516579..5700db9b3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/Html.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/Html.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html; +package com.djrapitops.plan.delivery.rendering.html; import org.apache.commons.text.StringSubstitutor; +import org.apache.commons.text.TextStringBuilder; import java.io.Serializable; import java.util.HashMap; @@ -44,57 +45,22 @@ public enum Html { COLOR_E(""), COLOR_F(""), - /** - * @deprecated Use com.djrapitops.plan.utilities.html.icon.Icon instead - */ - @Deprecated - FONT_AWESOME_ICON(""), - /** - * @deprecated Use com.djrapitops.plan.utilities.html.icon.Icon instead - */ - @Deprecated - FA_COLORED_ICON(""), SPAN("${0}"), - BUTTON("${1}"), - BUTTON_CLASS("class=\"button\""), LINK("${1}"), - LINK_A("${1}"), - LINK_TOOLTIP("${1}"), - LINK_EXTERNAL("${1}"), - LINK_CLASS("class=\"link\""), - IMG(""), + LINK_EXTERNAL("${1}"), - PARAGRAPH("

${0}

"), - HEADER("

${0}

"), - HEADER_2("

${0}

"), - - DIV_W_CLASS("
${1}
"), - DIV_W_CLASS_STYLE("
${2}
"), - - ROW("
${0}
"), - CARD("
${0}
"), - BODY("
${0}
"), - PANEL("
${0}
"), - PANEL_BODY("
${0}
"), - HELP_BUBBLE(""), - - TABLE_END("
"), - TABLE(""), - TABLE_SCROLL("
"), - TABLE_JQUERY("
"), - TABLE_COLORED("
"), - TABLE_HEAD("${0}"), - TABLE_BODY("${0}"), - TABLE_START_2("
"), - TABLE_START_3("
${0}${1}
"), - TABLE_START_4("
${0}${1}${2}
"), - TABLELINE_2(""), - TABLELINE_3(""), - TABLELINE_4(""), - TABLELINE_PLAYERS("" + ""), - TABLELINE_PLAYERS_PLAYERS_PAGE("" + ""), - TABLELINE_3_CUSTOMKEY(""), - TABLELINE_3_CUSTOMKEY_1(""); + BACK_BUTTON_NETWORK("" + + "" + + "" + + "" + + "Network page" + + ""), + BACK_BUTTON_SERVER("" + + "" + + "" + + "" + + "Server page" + + ""); private final String html; @@ -109,6 +75,12 @@ public enum Html { return html; } + public static String separateWithDots(String... elements) { + TextStringBuilder builder = new TextStringBuilder(); + builder.appendWithSeparators(elements, " • "); + return builder.toString(); + } + /** * Changes Minecraft color codes to HTML span elements with correct color class assignments. * @@ -124,6 +96,8 @@ public enum Html { }; Map colorMap = new HashMap<>(); + String splitWith = string.contains("§") ? "§" : "§"; + for (Html html : replacer) { colorMap.put(Character.toLowerCase(html.name().charAt(6)), html.parse()); colorMap.put('k', ""); @@ -134,9 +108,9 @@ public enum Html { } StringBuilder result = new StringBuilder(string.length()); - String[] split = string.split("§"); + String[] split = string.split(splitWith); // Skip first part if it does not start with § - boolean skipFirst = !string.startsWith("§"); + boolean skipFirst = !string.startsWith(splitWith); int placedSpans = 0; for (String part : split) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Color.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Color.java new file mode 100644 index 000000000..45427809e --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Color.java @@ -0,0 +1,74 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.html.icon; + +import java.util.Optional; + +public enum Color { + RED("col-red"), + PINK("col-pink"), + PURPLE("col-purple"), + DEEP_PURPLE("col-deep-purple"), + INDIGO("col-indigo"), + BLUE("col-blue"), + LIGHT_BLUE("col-light-blue"), + CYAN("col-cyan"), + TEAL("col-teal"), + GREEN("col-green"), + LIGHT_GREEN("col-light-green"), + LIME("col-lime"), + YELLOW("col-yellow"), + AMBER("col-amber"), + ORANGE("col-orange"), + DEEP_ORANGE("col-deep-orange"), + BROWN("col-brown"), + GREY("col-grey"), + BLUE_GREY("col-blue-grey"), + BLACK("col-black"), + NONE(""); + + private final String htmlClass; + + Color(String htmlClass) { + this.htmlClass = htmlClass; + } + + public static Color matchString(String name) { + String lowerCaseName = name.toLowerCase(); + for (Color color : values()) { + if (color.htmlClass.contains(lowerCaseName)) { + return color; + } + } + return Color.BLACK; + } + + public static Optional getByName(String name) { + if (name == null) { + return Optional.empty(); + } + try { + return Optional.of(valueOf(name)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + } + + public String getHtmlClass() { + return htmlClass; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Family.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Family.java new file mode 100644 index 000000000..d02e99284 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Family.java @@ -0,0 +1,50 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.html.icon; + +import java.util.Optional; + +public enum Family { + SOLID(" fa fa-", "\">"), + REGULAR(" far fa-", "\">"), + BRAND(" fab fa-", "\">"), + @Deprecated + LINE(" material-icons\">", ""); + + private final String middle; + private final String suffix; + + Family(String middle, String suffix) { + this.middle = middle; + this.suffix = suffix; + } + + public String appendAround(String color, String name) { + return " getByName(String name) { + if (name == null) { + return Optional.empty(); + } + try { + return Optional.of(valueOf(name)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Icon.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Icon.java new file mode 100644 index 000000000..0bb7245b0 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Icon.java @@ -0,0 +1,119 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.html.icon; + +import com.djrapitops.plugin.utilities.Verify; + +public class Icon { + + private Family type; + private String name; + private Color color; + + private Icon() { + type = Family.SOLID; + color = Color.NONE; + } + + public static Icon fromExtensionIcon(com.djrapitops.plan.extension.icon.Icon icon) { + if (icon == null) { + return Icon.called("question").build(); + } + return new Icon( + Family.getByName(icon.getFamily().name()).orElse(Family.SOLID), + icon.getName(), + Color.getByName(icon.getColor().name()).orElse(Color.NONE) + ); + } + + public Icon(Family type, String name, Color color) { + this.type = type; + this.name = name; + this.color = color; + } + + public Family getFamily() { + return type; + } + + public String getName() { + return name; + } + + public Color getColor() { + return color; + } + + public static Builder called(String name) { + return new Builder().called(name); + } + + public static Builder of(Family type) { + return new Builder().of(type); + } + + public static Builder of(Color color) { + return new Builder().of(color); + } + + public void setColor(Color color) { + this.color = color; + } + + public String toHtml() { + return type.appendAround(color.getHtmlClass(), name); + } + + @Override + public String toString() { + return toHtml(); + } + + public static class Builder { + + private final Icon icon; + + Builder() { + this.icon = new Icon(); + } + + public Builder called(String name) { + icon.name = name; + return this; + } + + public Builder of(Color color) { + icon.color = color; + return this; + } + + public Builder of(Family type) { + icon.type = type; + return this; + } + + public Icon build() { + Verify.nullCheck(icon.name, () -> new IllegalStateException("'name' was not defined yet!")); + return icon; + } + + @Override + public String toString() { + return build().toHtml(); + } + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Icons.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Icons.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Icons.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Icons.java index e6ba0441c..48cd85615 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/icon/Icons.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/icon/Icons.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.icon; +package com.djrapitops.plan.delivery.rendering.html.icon; /** * Class that contains commonly used {@link Icon}s. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/NavLink.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/NavLink.java new file mode 100644 index 000000000..2b3f8770a --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/NavLink.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.html.structure; + +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plugin.utilities.Format; + +/** + * Html utility for creating navigation link html. + * + * @author Rsl1122 + */ +public class NavLink { + + private final Icon icon; + private final String tabName; + private final boolean collapsed; + + public NavLink(Icon icon, String tabName) { + this(icon, tabName, false); + } + + private NavLink(Icon icon, String tabName, boolean collapsed) { + this.icon = icon; + this.tabName = tabName; + this.collapsed = collapsed; + } + + public static NavLink main(Icon icon, String tabName) { + return new NavLink(icon, tabName, false); + } + + public static NavLink collapsed(Icon icon, String tabName) { + return new NavLink(icon, tabName, true); + } + + public String toHtml() { + String tabID = new Format(tabName).justLetters().lowerCase().toString(); + if (collapsed) { + return "" + + icon.toHtml() + ' ' + + tabName + ""; + } + return "
  • " + + "" + + icon.toHtml() + + "" + tabName + "" + + "
  • "; + } + +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/TabsElement.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/TabsElement.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/TabsElement.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/TabsElement.java index 52f8f9fc0..417cff62a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/TabsElement.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/html/structure/TabsElement.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.structure; +package com.djrapitops.plan.delivery.rendering.html.structure; import com.djrapitops.plugin.utilities.Format; @@ -48,10 +48,10 @@ public class TabsElement { String navText = tab.getNavText(); String contentHtml = tab.getContentHtml(); - nav.append("
  • ") + nav.append("
  • ") .append(navText).append("
  • "); - content.append("
    ") .append(contentHtml).append("
    "); first = false; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/JSONFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/JSONFactory.java new file mode 100644 index 000000000..654b64326 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/JSONFactory.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.delivery.domain.mutators.PlayerKillMutator; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs; +import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerPlayerDataTableQuery; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Ping; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.HtmlLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.storage.database.queries.objects.*; +import com.djrapitops.plan.utilities.comparators.SessionStartComparator; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Factory with different JSON parsing placed to a single class. + * + * @author Rsl1122 + */ +@Singleton +public class JSONFactory { + + private final PlanConfig config; + private final Locale locale; + private final DBSystem dbSystem; + private final ServerInfo serverInfo; + private final Graphs graphs; + private final Formatters formatters; + + @Inject + public JSONFactory( + PlanConfig config, + Locale locale, + DBSystem dbSystem, + ServerInfo serverInfo, + Graphs graphs, + Formatters formatters + ) { + this.config = config; + this.locale = locale; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + this.graphs = graphs; + this.formatters = formatters; + } + + public String serverPlayersTableJSON(UUID serverUUID) { + Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_SERVER_PAGE); + Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + Boolean openPlayerLinksInNewTab = config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB); + + Database database = dbSystem.getDatabase(); + + return new PlayersTableJSONParser( + database.query(new ServerTablePlayersQuery(serverUUID, System.currentTimeMillis(), playtimeThreshold, xMostRecentPlayers)), + database.query(new ExtensionServerPlayerDataTableQuery(serverUUID, xMostRecentPlayers)), + openPlayerLinksInNewTab, + formatters, locale + ).toJSONString(); + } + + public String networkPlayersTableJSON() { + Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_PLAYERS_PAGE); + Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + Boolean openPlayerLinksInNewTab = config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB); + + Database database = dbSystem.getDatabase(); + + return new PlayersTableJSONParser( + database.query(new NetworkTablePlayersQuery(System.currentTimeMillis(), playtimeThreshold, xMostRecentPlayers)), + Collections.emptyMap(), + openPlayerLinksInNewTab, + formatters, locale + ).toJSONString(); + } + + public List> serverSessionsAsJSONMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + + Integer perPageLimit = config.get(DisplaySettings.SESSIONS_PER_PAGE); + List sessions = db.query(SessionQueries.fetchLatestSessionsOfServer(serverUUID, perPageLimit)); + // Add online sessions + if (serverUUID.equals(serverInfo.getServerUUID())) { + sessions.addAll(SessionCache.getActiveSessions().values()); + sessions.sort(new SessionStartComparator()); + while (true) { + int size = sessions.size(); + if (size <= perPageLimit) break; + sessions.remove(size - 1); // Remove last until it fits. + } + } + + return new SessionsMutator(sessions).toPlayerNameJSONMaps(graphs, config.getWorldAliasSettings(), formatters); + } + + public List> networkSessionsAsJSONMap() { + Database db = dbSystem.getDatabase(); + Integer perPageLimit = config.get(DisplaySettings.SESSIONS_PER_PAGE); + + List sessions = db.query(SessionQueries.fetchLatestSessions(perPageLimit)); + // Add online sessions + if (serverInfo.getServer().isProxy()) { + sessions.addAll(SessionCache.getActiveSessions().values()); + sessions.sort(new SessionStartComparator()); + while (true) { + int size = sessions.size(); + if (size <= perPageLimit) break; + sessions.remove(size - 1); // Remove last until it fits. + } + } + + List> sessionMaps = new SessionsMutator(sessions).toPlayerNameJSONMaps(graphs, config.getWorldAliasSettings(), formatters); + // Add network_server property so that sessions have a server page link + sessionMaps.forEach(map -> map.put("network_server", map.get("server_name"))); + return sessionMaps; + } + + public List> serverPlayerKillsAsJSONMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + List kills = db.query(KillQueries.fetchPlayerKillsOnServer(serverUUID, 100)); + return new PlayerKillMutator(kills).toJSONAsMap(formatters); + } + + public List> serversAsJSONMaps() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + Formatter year = formatters.yearLong(); + Formatter decimals = formatters.decimals(); + Formatter timeAmount = formatters.timeAmount(); + + Map serverInformation = db.query(ServerQueries.fetchPlanServerInformation()); + UUID proxyUUID = serverInformation.values().stream() + .filter(Server::isProxy) + .findFirst() + .map(Server::getUuid).orElse(null); + + Map> tpsData = db.query( + TPSQueries.fetchTPSDataOfAllServersBut(monthAgo, now, proxyUUID) + ); + Map totalPlayerCounts = db.query(PlayerCountQueries.newPlayerCounts(0, now)); + Map newPlayerCounts = db.query(PlayerCountQueries.newPlayerCounts(monthAgo, now)); + Map uniquePlayerCounts = db.query(PlayerCountQueries.uniquePlayerCounts(monthAgo, now)); + + List> servers = new ArrayList<>(); + serverInformation.entrySet() + .stream() // Sort alphabetically + .sorted(Comparator.comparing(entry -> entry.getValue().getIdentifiableName().toLowerCase())) + .filter(entry -> entry.getValue().isNotProxy()) + .forEach(entry -> { + UUID serverUUID = entry.getKey(); + Map server = new HashMap<>(); + server.put("name", entry.getValue().getIdentifiableName()); + + Optional> recentPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, now - TimeUnit.DAYS.toMillis(2L))); + Optional> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID)); + server.put("last_peak_date", recentPeak.map(DateObj::getDate).map(year).orElse("-")); + server.put("best_peak_date", allTimePeak.map(DateObj::getDate).map(year).orElse("-")); + server.put("last_peak_players", recentPeak.map(DateObj::getValue).orElse(0)); + server.put("best_peak_players", allTimePeak.map(DateObj::getValue).orElse(0)); + + TPSMutator tpsMonth = new TPSMutator(tpsData.getOrDefault(serverUUID, Collections.emptyList())); + server.put("playersOnline", tpsMonth.all().stream() + .map(tps -> new double[]{tps.getDate(), tps.getPlayers()}) + .toArray(double[][]::new)); + server.put("players", totalPlayerCounts.getOrDefault(serverUUID, 0)); + server.put("new_players", newPlayerCounts.getOrDefault(serverUUID, 0)); + server.put("unique_players", uniquePlayerCounts.getOrDefault(serverUUID, 0)); + TPSMutator tpsWeek = tpsMonth.filterDataBetween(weekAgo, now); + double averageTPS = tpsWeek.averageTPS(); + server.put("avg_tps", averageTPS != -1 ? decimals.apply(averageTPS) : locale.get(HtmlLang.UNIT_NO_DATA).toString()); + server.put("low_tps_spikes", tpsWeek.lowTpsSpikeCount(config.getNumber(DisplaySettings.GRAPH_TPS_THRESHOLD_MED))); + server.put("downtime", timeAmount.apply(tpsWeek.serverDownTime())); + + Optional online = tpsWeek.getLast(); + server.put("online", online.isPresent() ? + online.get().getDate() >= now - TimeUnit.MINUTES.toMillis(3L) ? + online.get().getPlayers() : "Possibly offline" + : locale.get(HtmlLang.UNIT_NO_DATA).toString()); + servers.add(server); + }); + return servers; + } + + public List> pingPerGeolocation(UUID serverUUID) { + Map pingByGeolocation = dbSystem.getDatabase().query(PingQueries.fetchPingDataOfServerByGeolocation(serverUUID)); + return turnToTableEntries(pingByGeolocation); + } + + public List> pingPerGeolocation() { + Map pingByGeolocation = dbSystem.getDatabase().query(PingQueries.fetchPingDataOfNetworkByGeolocation()); + return turnToTableEntries(pingByGeolocation); + } + + private List> turnToTableEntries(Map pingByGeolocation) { + List> tableEntries = new ArrayList<>(); + for (Map.Entry entry : pingByGeolocation.entrySet()) { + String geolocation = entry.getKey(); + Ping ping = entry.getValue(); + + Map tableEntry = new HashMap<>(); + tableEntry.put("country", geolocation); + tableEntry.put("avg_ping", formatters.decimals().apply(ping.getAverage()) + " ms"); + tableEntry.put("min_ping", ping.getMin() + " ms"); + tableEntry.put("max_ping", ping.getMax() + " ms"); + tableEntries.add(tableEntry); + } + return tableEntries; + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/OnlineActivityOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/OnlineActivityOverviewJSONParser.java new file mode 100644 index 000000000..8b8008ff5 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/OnlineActivityOverviewJSONParser.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.domain.mutators.PlayersOnlineResolver; +import com.djrapitops.plan.delivery.domain.mutators.RetentionData; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.analysis.ActivityIndexQueries; +import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; +import com.djrapitops.plan.storage.database.queries.objects.TPSQueries; +import com.djrapitops.plan.storage.database.queries.objects.UserInfoQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /server-page Online Activity Overview tab. + * + * @author Rsl1122 + */ +@Singleton +public class OnlineActivityOverviewJSONParser implements ServerTabJSONParser> { + + private PlanConfig config; + private DBSystem dbSystem; + + private Formatter timeAmountFormatter; + private Formatter decimalFormatter; + private Formatter percentageFormatter; + private final TimeZone timeZone; + + @Inject + public OnlineActivityOverviewJSONParser( + PlanConfig config, + DBSystem dbSystem, + Formatters formatters + ) { + this.config = config; + this.dbSystem = dbSystem; + + timeAmountFormatter = formatters.timeAmount(); + decimalFormatter = formatters.decimals(); + percentageFormatter = formatters.percentage(); + this.timeZone = config.getTimeZone(); + } + + public Map createJSONAsMap(UUID serverUUID) { + Map serverOverview = new HashMap<>(); + serverOverview.put("numbers", createNumbersMap(serverUUID)); + serverOverview.put("insights", createInsightsMap(serverUUID)); + return serverOverview; + } + + private Map createNumbersMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long dayAgo = now - TimeUnit.DAYS.toMillis(1L); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + long halfMonthAgo = now - TimeUnit.DAYS.toMillis(15L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + int timeZoneOffset = timeZone.getOffset(now); + Long playThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map numbers = new HashMap<>(); + + numbers.put("unique_players_30d", db.query(PlayerCountQueries.uniquePlayerCount(monthAgo, now, serverUUID))); + numbers.put("unique_players_30d_trend", new Trend( + db.query(PlayerCountQueries.uniquePlayerCount(monthAgo, halfMonthAgo, serverUUID)), + db.query(PlayerCountQueries.uniquePlayerCount(halfMonthAgo, now, serverUUID)), + false + )); + numbers.put("unique_players_7d", db.query(PlayerCountQueries.uniquePlayerCount(weekAgo, now, serverUUID))); + numbers.put("unique_players_24h", db.query(PlayerCountQueries.uniquePlayerCount(dayAgo, now, serverUUID))); + + numbers.put("unique_players_30d_avg", db.query(PlayerCountQueries.averageUniquePlayerCount(monthAgo, now, timeZoneOffset, serverUUID))); + numbers.put("unique_players_30d_avg_trend", new Trend( + db.query(PlayerCountQueries.averageUniquePlayerCount(monthAgo, halfMonthAgo, timeZoneOffset, serverUUID)), + db.query(PlayerCountQueries.averageUniquePlayerCount(halfMonthAgo, now, timeZoneOffset, serverUUID)), + false + )); + numbers.put("unique_players_7d_avg", db.query(PlayerCountQueries.averageUniquePlayerCount(weekAgo, now, timeZoneOffset, serverUUID))); + numbers.put("unique_players_24h_avg", db.query(PlayerCountQueries.averageUniquePlayerCount(dayAgo, now, timeZoneOffset, serverUUID))); + + Integer new30d = db.query(PlayerCountQueries.newPlayerCount(monthAgo, now, serverUUID)); + Integer new7d = db.query(PlayerCountQueries.newPlayerCount(weekAgo, now, serverUUID)); + Integer new1d = db.query(PlayerCountQueries.newPlayerCount(dayAgo, now, serverUUID)); + numbers.put("new_players_30d", new30d); + numbers.put("new_players_30d_trend", new Trend( + db.query(PlayerCountQueries.newPlayerCount(monthAgo, halfMonthAgo, serverUUID)), + db.query(PlayerCountQueries.newPlayerCount(halfMonthAgo, now, serverUUID)), + false + )); + numbers.put("new_players_7d", new7d); + numbers.put("new_players_24h", new1d); + + numbers.put("new_players_30d_avg", db.query(PlayerCountQueries.averageNewPlayerCount(monthAgo, now, timeZoneOffset, serverUUID))); + numbers.put("new_players_30d_avg_trend", new Trend( + db.query(PlayerCountQueries.averageNewPlayerCount(monthAgo, halfMonthAgo, timeZoneOffset, serverUUID)), + db.query(PlayerCountQueries.averageNewPlayerCount(halfMonthAgo, now, timeZoneOffset, serverUUID)), + false + )); + numbers.put("new_players_7d_avg", db.query(PlayerCountQueries.averageNewPlayerCount(weekAgo, now, timeZoneOffset, serverUUID))); + numbers.put("new_players_24h_avg", db.query(PlayerCountQueries.averageNewPlayerCount(dayAgo, now, timeZoneOffset, serverUUID))); + + int retained30d = db.query(PlayerCountQueries.retainedPlayerCount(monthAgo, now, serverUUID)); + int retained7d = db.query(PlayerCountQueries.retainedPlayerCount(weekAgo, now, serverUUID)); + double retentionPerc30d = new30d != 0 ? (double) retained30d / new30d : -1; + double retentionPerc7d = new7d != 0 ? (double) retained7d / new7d : -1; + numbers.put("new_players_retention_30d", retained30d); + numbers.put("new_players_retention_30d_perc", percentageFormatter.apply(retentionPerc30d)); + numbers.put("new_players_retention_7d", retained7d); + numbers.put("new_players_retention_7d_perc", percentageFormatter.apply(retentionPerc7d)); + + int prediction1d = RetentionData.countRetentionPrediction( + db.query(ActivityIndexQueries.activityIndexForNewPlayers(dayAgo, now, serverUUID, playThreshold)), + db.query(ActivityIndexQueries.averageActivityIndexForRetainedPlayers(monthAgo, now, serverUUID, playThreshold)), + db.query(ActivityIndexQueries.averageActivityIndexForNonRetainedPlayers(monthAgo, now, serverUUID, playThreshold)) + ); + double retentionPerc1d = new1d != 0 ? (double) prediction1d / new1d : -1; + numbers.put("new_players_retention_24h", prediction1d); + numbers.put("new_players_retention_24h_perc", percentageFormatter.apply(retentionPerc1d)); + + Long playtimeMonth = db.query(SessionQueries.playtime(monthAgo, now, serverUUID)); + Long playtimeWeek = db.query(SessionQueries.playtime(weekAgo, now, serverUUID)); + Long playtimeDay = db.query(SessionQueries.playtime(dayAgo, now, serverUUID)); + Long playtimeBefore = db.query(SessionQueries.playtime(monthAgo, halfMonthAgo, serverUUID)); + Long playtimeAfter = db.query(SessionQueries.playtime(halfMonthAgo, now, serverUUID)); + numbers.put("playtime_30d", timeAmountFormatter.apply(playtimeMonth)); + numbers.put("playtime_30d_trend", new Trend(playtimeBefore, playtimeAfter, false, timeAmountFormatter)); + numbers.put("playtime_7d", timeAmountFormatter.apply(playtimeWeek)); + numbers.put("playtime_24h", timeAmountFormatter.apply(playtimeDay)); + + numbers.put("playtime_30d_avg", timeAmountFormatter.apply(db.query(SessionQueries.averagePlaytimePerDay(monthAgo, now, timeZoneOffset, serverUUID)))); + numbers.put("playtime_30d_avg_trend", new Trend( + db.query(SessionQueries.averagePlaytimePerDay(monthAgo, halfMonthAgo, timeZoneOffset, serverUUID)), + db.query(SessionQueries.averagePlaytimePerDay(halfMonthAgo, now, timeZoneOffset, serverUUID)), + false, + timeAmountFormatter + )); + numbers.put("playtime_7d_avg", timeAmountFormatter.apply(db.query(SessionQueries.averagePlaytimePerDay(weekAgo, now, timeZoneOffset, serverUUID)))); + numbers.put("playtime_24h_avg", timeAmountFormatter.apply(db.query(SessionQueries.playtime(dayAgo, now, serverUUID)))); + + Long sessionsMonth = db.query(SessionQueries.sessionCount(monthAgo, now, serverUUID)); + Long sessionsWeek = db.query(SessionQueries.sessionCount(weekAgo, now, serverUUID)); + Long sessionsDay = db.query(SessionQueries.sessionCount(dayAgo, now, serverUUID)); + Long sessionsBefore = db.query(SessionQueries.sessionCount(monthAgo, halfMonthAgo, serverUUID)); + Long sessionsAfter = db.query(SessionQueries.sessionCount(halfMonthAgo, now, serverUUID)); + numbers.put("sessions_30d", sessionsMonth); + numbers.put("sessions_30d_trend", new Trend(sessionsBefore, sessionsAfter, false)); + numbers.put("sessions_7d", sessionsWeek); + numbers.put("sessions_24h", sessionsDay); + + Long sessionLengthAvgMonth = sessionsMonth != 0 ? playtimeMonth / sessionsMonth : 0; + Long sessionLengthAvgWeek = sessionsWeek != 0 ? playtimeWeek / sessionsWeek : 0; + Long sessionLengthAvgDay = sessionsDay != 0 ? playtimeDay / sessionsDay : 0; + numbers.put("session_length_30d_avg", timeAmountFormatter.apply(sessionLengthAvgMonth)); + numbers.put("session_length_30d_trend", new Trend( + sessionsBefore != 0 ? playtimeBefore / sessionsBefore : 0, + sessionsAfter != 0 ? playtimeAfter / sessionsAfter : 0, + false, + timeAmountFormatter + )); + numbers.put("session_length_7d_avg", timeAmountFormatter.apply(sessionLengthAvgWeek)); + numbers.put("session_length_24h_avg", timeAmountFormatter.apply(sessionLengthAvgDay)); + + TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(monthAgo, now, serverUUID))); + numbers.put("average_tps", decimalFormatter.apply(tpsMutator.averageTPS())); + numbers.put("low_tps_spikes", tpsMutator.lowTpsSpikeCount(config.getNumber(DisplaySettings.GRAPH_TPS_THRESHOLD_MED))); + numbers.put("downtime", timeAmountFormatter.apply(tpsMutator.serverDownTime())); + + return numbers; + } + + private Map createInsightsMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long halfMonthAgo = now - TimeUnit.DAYS.toMillis(15L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + Map insights = new HashMap<>(); + + SessionsMutator sessions = new SessionsMutator(db.query(SessionQueries.fetchServerSessionsWithoutKillOrWorldData(monthAgo, now, serverUUID))); + List tpsData = db.query(TPSQueries.fetchTPSDataOfServer(monthAgo, now, serverUUID)); + Map registerDates = db.query(UserInfoQueries.fetchRegisterDates(monthAgo, now, serverUUID)); + + PlayersOnlineResolver playersOnlineResolver = new PlayersOnlineResolver(new TPSMutator(tpsData)); + SessionsMutator firstSessions = sessions.filterBy(session -> { + long registered = registerDates.getOrDefault(session.getValue(SessionKeys.UUID).orElse(null), -501L); + long start = session.getDate(); + return Math.abs(registered - start) < 500L; + }); + SessionsMutator firstSessionsBefore = firstSessions.filterSessionsBetween(monthAgo, halfMonthAgo); + SessionsMutator firstSessionsAfter = firstSessions.filterSessionsBetween(halfMonthAgo, now); + + long avgSessionLength = firstSessions.toAverageSessionLength(); + long avgSessionLengthBefore = firstSessionsBefore.toAverageSessionLength(); + long avgSessionLengthAfter = firstSessionsAfter.toAverageSessionLength(); + insights.put("first_session_length_avg", timeAmountFormatter.apply(avgSessionLength)); + insights.put("first_session_length_trend", new Trend(avgSessionLengthBefore, avgSessionLengthAfter, false, timeAmountFormatter)); + + int lonelyJoins = playersOnlineResolver.findLonelyJoins(sessions.toSessionStarts()); + int loneJoinsBefore = playersOnlineResolver.findLonelyJoins(sessions.filterSessionsBetween(monthAgo, halfMonthAgo).toSessionStarts()); + int loneJoinsAfter = playersOnlineResolver.findLonelyJoins(sessions.filterSessionsBetween(halfMonthAgo, now).toSessionStarts()); + insights.put("lone_joins", lonelyJoins); + insights.put("lone_joins_trend", new Trend(loneJoinsBefore, loneJoinsAfter, true)); + + int newLonelyJoins = playersOnlineResolver.findLonelyJoins(firstSessions.toSessionStarts()); + int newLoneJoinsBefore = playersOnlineResolver.findLonelyJoins(firstSessionsBefore.toSessionStarts()); + int newLoneJoinsAfter = playersOnlineResolver.findLonelyJoins(firstSessionsAfter.toSessionStarts()); + insights.put("lone_new_joins", newLonelyJoins); + insights.put("lone_new_joins_trend", new Trend(newLoneJoinsBefore, newLoneJoinsAfter, true)); + + double playersOnlineOnRegister = firstSessions.toAveragePlayersOnline(playersOnlineResolver); + double playersOnlineOnRegisterBefore = firstSessionsBefore.toAveragePlayersOnline(playersOnlineResolver); + double playersOnlineOnRegisterAfter = firstSessionsAfter.toAveragePlayersOnline(playersOnlineResolver); + insights.put("players_first_join_avg", decimalFormatter.apply(playersOnlineOnRegister)); + insights.put("players_first_join_trend", new Trend(playersOnlineOnRegisterBefore, playersOnlineOnRegisterAfter, false, decimalFormatter)); + + return insights; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PerformanceJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PerformanceJSONParser.java new file mode 100644 index 000000000..02640aeb8 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PerformanceJSONParser.java @@ -0,0 +1,160 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.GenericLang; +import com.djrapitops.plan.settings.locale.lang.HtmlLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.TPSQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /server-page Performance tab. + * + * @author Rsl1122 + */ +@Singleton +public class PerformanceJSONParser implements ServerTabJSONParser> { + + private final PlanConfig config; + private final Locale locale; + private final DBSystem dbSystem; + + private final Formatter decimals; + private final Formatter timeAmountFormatter; + private final Formatter percentageFormatter; + + @Inject + public PerformanceJSONParser( + PlanConfig config, + Locale locale, + DBSystem dbSystem, + Formatters formatters + ) { + this.config = config; + this.locale = locale; + this.dbSystem = dbSystem; + + decimals = formatters.decimals(); + percentageFormatter = formatters.percentage(); + timeAmountFormatter = formatters.timeAmount(); + } + + public Map createJSONAsMap(UUID serverUUID) { + Map serverOverview = new HashMap<>(); + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + List tpsData = db.query(TPSQueries.fetchTPSDataOfServer(monthAgo, now, serverUUID)); + + serverOverview.put("numbers", createNumbersMap(tpsData)); + serverOverview.put("insights", createInsightsMap(tpsData)); + return serverOverview; + } + + private Map createNumbersMap(List tpsData) { + long now = System.currentTimeMillis(); + long dayAgo = now - TimeUnit.DAYS.toMillis(1L); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + + Map numbers = new HashMap<>(); + + TPSMutator tpsDataMonth = new TPSMutator(tpsData); + TPSMutator tpsDataWeek = tpsDataMonth.filterDataBetween(weekAgo, now); + TPSMutator tpsDataDay = tpsDataWeek.filterDataBetween(dayAgo, now); + + Integer tpsThreshold = config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED); + numbers.put("low_tps_spikes_30d", tpsDataMonth.lowTpsSpikeCount(tpsThreshold)); + numbers.put("low_tps_spikes_7d", tpsDataWeek.lowTpsSpikeCount(tpsThreshold)); + numbers.put("low_tps_spikes_24h", tpsDataDay.lowTpsSpikeCount(tpsThreshold)); + + numbers.put("server_downtime_30d", timeAmountFormatter.apply(tpsDataMonth.serverDownTime())); + numbers.put("server_downtime_7d", timeAmountFormatter.apply(tpsDataWeek.serverDownTime())); + numbers.put("server_downtime_24h", timeAmountFormatter.apply(tpsDataDay.serverDownTime())); + + numbers.put("tps_30d", format(tpsDataMonth.averageTPS())); + numbers.put("tps_7d", format(tpsDataWeek.averageTPS())); + numbers.put("tps_24h", format(tpsDataDay.averageTPS())); + numbers.put("cpu_30d", formatPerc(tpsDataMonth.averageCPU())); + numbers.put("cpu_7d", formatPerc(tpsDataWeek.averageCPU())); + numbers.put("cpu_24h", formatPerc(tpsDataDay.averageCPU())); + numbers.put("ram_30d", format(tpsDataMonth.averageRAM(), " MB")); + numbers.put("ram_7d", format(tpsDataWeek.averageRAM(), " MB")); + numbers.put("ram_24h", format(tpsDataDay.averageRAM(), " MB")); + numbers.put("entities_30d", format((int) tpsDataMonth.averageEntities())); + numbers.put("entities_7d", format((int) tpsDataWeek.averageEntities())); + numbers.put("entities_24h", format((int) tpsDataDay.averageEntities())); + numbers.put("chunks_30d", format((int) tpsDataMonth.averageChunks())); + numbers.put("chunks_7d", format((int) tpsDataWeek.averageChunks())); + numbers.put("chunks_24h", format((int) tpsDataDay.averageChunks())); + + numbers.put("max_disk_30d", format(tpsDataMonth.maxFreeDisk(), " MB")); + numbers.put("max_disk_7d", format(tpsDataWeek.maxFreeDisk(), " MB")); + numbers.put("max_disk_24h", format(tpsDataDay.maxFreeDisk(), " MB")); + numbers.put("min_disk_30d", format(tpsDataMonth.minFreeDisk(), " MB")); + numbers.put("min_disk_7d", format(tpsDataWeek.minFreeDisk(), " MB")); + numbers.put("min_disk_24h", format(tpsDataDay.minFreeDisk(), " MB")); + + return numbers; + } + + private String format(double value) { + return value != -1 ? decimals.apply(value) : locale.get(GenericLang.UNAVAILABLE).toString(); + } + + private String format(double value, String suffix) { + return value != -1 ? decimals.apply(value) + suffix : locale.get(GenericLang.UNAVAILABLE).toString(); + } + + private String formatPerc(double value) { + return value != -1 ? percentageFormatter.apply(value / 100.0) : locale.get(GenericLang.UNAVAILABLE).toString(); + } + + private Map createInsightsMap(List tpsData) { + TPSMutator tpsMutator = new TPSMutator(tpsData); + Integer tpsThreshold = config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED); + TPSMutator lowTPS = tpsMutator.filterTPSBetween(-1, tpsThreshold); + + Map insights = new HashMap<>(); + + double avgPlayersOnline = lowTPS.averagePlayersOnline(); + double averageCPU = lowTPS.averageCPU(); + double averageEntities = lowTPS.averageEntities(); + double averageChunks = lowTPS.averageChunks(); + insights.put("low_tps_players", avgPlayersOnline != -1 ? decimals.apply(avgPlayersOnline) : locale.get(HtmlLang.TEXT_NO_LOW_TPS).toString()); + insights.put("low_tps_cpu", averageCPU != -1 ? decimals.apply(averageCPU) : "-"); + insights.put("low_tps_entities", averageEntities != -1 ? decimals.apply(averageEntities) : "-"); + insights.put("low_tps_chunks", averageChunks != -1 ? decimals.apply(averageChunks) : "-"); + + return insights; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerBaseOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerBaseOverviewJSONParser.java new file mode 100644 index 000000000..5704a3628 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerBaseOverviewJSONParser.java @@ -0,0 +1,151 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.analysis.ActivityIndexQueries; +import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /server-page Playerbase Overview tab. + * + * @author Rsl1122 + */ +@Singleton +public class PlayerBaseOverviewJSONParser implements ServerTabJSONParser> { + + private PlanConfig config; + private DBSystem dbSystem; + + private Formatter timeAmount; + private Formatter percentage; + + @Inject + public PlayerBaseOverviewJSONParser( + PlanConfig config, + DBSystem dbSystem, + Formatters formatters + ) { + this.config = config; + this.dbSystem = dbSystem; + + timeAmount = formatters.timeAmount(); + percentage = formatters.percentage(); + } + + public Map createJSONAsMap(UUID serverUUID) { + Map serverOverview = new HashMap<>(); + serverOverview.put("trends", createTrendsMap(serverUUID)); + serverOverview.put("insights", createInsightsMap(serverUUID)); + return serverOverview; + } + + private Map createTrendsMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + long twoMonthsAgo = now - TimeUnit.DAYS.toMillis(60L); + Long playThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map trends = new HashMap<>(); + + Integer playersBefore = db.query(PlayerCountQueries.newPlayerCount(0L, monthAgo, serverUUID)); + Integer playersAfter = db.query(PlayerCountQueries.newPlayerCount(0L, now, serverUUID)); + trends.put("total_players_then", playersBefore); + trends.put("total_players_now", playersAfter); + trends.put("total_players_trend", new Trend(playersBefore, playersAfter, false)); + + Integer regularBefore = db.query(ActivityIndexQueries.fetchRegularPlayerCount(monthAgo, serverUUID, playThreshold)); + Integer regularAfter = db.query(ActivityIndexQueries.fetchRegularPlayerCount(now, serverUUID, playThreshold)); + trends.put("regular_players_then", regularBefore); + trends.put("regular_players_now", regularAfter); + trends.put("regular_players_trend", new Trend(regularBefore, regularAfter, false)); + + Long avgPlaytimeBefore = db.query(SessionQueries.averagePlaytimePerPlayer(twoMonthsAgo, monthAgo, serverUUID)); + Long avgPlaytimeAfter = db.query(SessionQueries.averagePlaytimePerPlayer(monthAgo, now, serverUUID)); + trends.put("playtime_avg_then", timeAmount.apply(avgPlaytimeBefore)); + trends.put("playtime_avg_now", timeAmount.apply(avgPlaytimeAfter)); + trends.put("playtime_avg_trend", new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false, timeAmount)); + + Long avgAfkBefore = db.query(SessionQueries.averageAfkPerPlayer(twoMonthsAgo, monthAgo, serverUUID)); + Long avgAfkAfter = db.query(SessionQueries.averageAfkPerPlayer(monthAgo, now, serverUUID)); + double afkPercBefore = avgPlaytimeBefore != 0 ? (double) avgAfkBefore / avgPlaytimeBefore : 0; + double afkPercAfter = avgPlaytimeAfter != 0 ? (double) avgAfkAfter / avgPlaytimeAfter : 0; + trends.put("afk_then", percentage.apply(afkPercBefore)); + trends.put("afk_now", percentage.apply(afkPercAfter)); + trends.put("afk_trend", new Trend(afkPercBefore, afkPercAfter, Trend.REVERSED, percentage)); + + Long avgRegularPlaytimeBefore = db.query(ActivityIndexQueries.averagePlaytimePerRegularPlayer(twoMonthsAgo, monthAgo, serverUUID, playThreshold)); + Long avgRegularPlaytimeAfter = db.query(ActivityIndexQueries.averagePlaytimePerRegularPlayer(monthAgo, now, serverUUID, playThreshold)); + trends.put("regular_playtime_avg_then", timeAmount.apply(avgRegularPlaytimeBefore)); + trends.put("regular_playtime_avg_now", timeAmount.apply(avgRegularPlaytimeAfter)); + trends.put("regular_playtime_avg_trend", new Trend(avgRegularPlaytimeBefore, avgRegularPlaytimeAfter, false, timeAmount)); + + Long avgRegularSessionLengthBefore = db.query(ActivityIndexQueries.averageSessionLengthPerRegularPlayer(twoMonthsAgo, monthAgo, serverUUID, playThreshold)); + Long avgRegularSessionLengthAfter = db.query(ActivityIndexQueries.averageSessionLengthPerRegularPlayer(monthAgo, now, serverUUID, playThreshold)); + trends.put("regular_session_avg_then", timeAmount.apply(avgRegularSessionLengthBefore)); + trends.put("regular_session_avg_now", timeAmount.apply(avgRegularSessionLengthAfter)); + trends.put("regular_session_avg_trend", new Trend(avgRegularSessionLengthBefore, avgRegularSessionLengthAfter, false, timeAmount)); + + Long avgRegularAfkBefore = db.query(ActivityIndexQueries.averageAFKPerRegularPlayer(twoMonthsAgo, monthAgo, serverUUID, playThreshold)); + Long avgRegularAfkAfter = db.query(ActivityIndexQueries.averageAFKPerRegularPlayer(monthAgo, now, serverUUID, playThreshold)); + double afkRegularPercBefore = avgRegularPlaytimeBefore != 0 ? (double) avgRegularAfkBefore / avgRegularPlaytimeBefore : 0; + double afkRegularPercAfter = avgRegularPlaytimeAfter != 0 ? (double) avgRegularAfkAfter / avgRegularPlaytimeAfter : 0; + trends.put("regular_afk_avg_then", percentage.apply(afkRegularPercBefore)); + trends.put("regular_afk_avg_now", percentage.apply(afkRegularPercAfter)); + trends.put("regular_afk_avg_trend", new Trend(afkRegularPercBefore, afkRegularPercAfter, Trend.REVERSED, percentage)); + + return trends; + } + + private Map createInsightsMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long halfMonthAgo = now - TimeUnit.DAYS.toMillis(15L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + Long playThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map insights = new HashMap<>(); + + int newToRegular = db.query(ActivityIndexQueries.countNewPlayersTurnedRegular(monthAgo, now, serverUUID, playThreshold)); + Integer newToRegularBefore = db.query(ActivityIndexQueries.countNewPlayersTurnedRegular(monthAgo, halfMonthAgo, serverUUID, playThreshold)); + Integer newToRegularAfter = db.query(ActivityIndexQueries.countNewPlayersTurnedRegular(halfMonthAgo, now, serverUUID, playThreshold)); + insights.put("new_to_regular", newToRegular); + insights.put("new_to_regular_trend", new Trend(newToRegularBefore, newToRegularAfter, false)); + + Integer regularToInactive = db.query(ActivityIndexQueries.countRegularPlayersTurnedInactive(monthAgo, now, serverUUID, playThreshold)); + Integer regularToInactiveBefore = db.query(ActivityIndexQueries.countRegularPlayersTurnedInactive(monthAgo, halfMonthAgo, serverUUID, playThreshold)); + Integer regularToInactiveAfter = db.query(ActivityIndexQueries.countRegularPlayersTurnedInactive(halfMonthAgo, now, serverUUID, playThreshold)); + insights.put("regular_to_inactive", regularToInactive); + insights.put("regular_to_inactive_trend", new Trend(regularToInactiveBefore, regularToInactiveAfter, Trend.REVERSED)); + + return insights; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerJSONParser.java new file mode 100644 index 000000000..f8e784843 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayerJSONParser.java @@ -0,0 +1,312 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.mutators.*; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.html.Html; +import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs; +import com.djrapitops.plan.delivery.rendering.json.graphs.pie.WorldPie; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.GeoInfo; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.gathering.domain.WorldTimes; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.GenericLang; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.containers.PlayerContainerQuery; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; +import org.apache.commons.text.StringEscapeUtils; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Singleton +public class PlayerJSONParser { + + private final PlanConfig config; + private final Locale locale; + private final Theme theme; + private final DBSystem dbSystem; + private final Graphs graphs; + private final Formatters formatters; + + private final Formatter timeAmount; + private final Formatter decimals; + private final Formatter year; + + @Inject + public PlayerJSONParser( + PlanConfig config, + Locale locale, + Theme theme, + DBSystem dbSystem, + Formatters formatters, + Graphs graphs + ) { + this.config = config; + this.locale = locale; + this.theme = theme; + this.dbSystem = dbSystem; + + this.formatters = formatters; + timeAmount = formatters.timeAmount(); + decimals = formatters.decimals(); + year = formatters.yearLong(); + this.graphs = graphs; + } + + public Map createJSONAsMap(UUID playerUUID) { + Database db = dbSystem.getDatabase(); + + Map serverNames = db.query(ServerQueries.fetchServerNames()); + String[] pieColors = theme.getPieColors(ThemeVal.GRAPH_WORLD_PIE); + + PlayerContainer player = db.query(new PlayerContainerQuery(playerUUID)); + SessionsMutator sessionsMutator = SessionsMutator.forContainer(player); + Map worldTimesPerServer = PerServerMutator.forContainer(player).worldTimesPerServer(); + List> serverAccordion = new ServerAccordion(player, serverNames, graphs, year, timeAmount, locale.get(GenericLang.UNKNOWN).toString()).asMaps(); + List kills = player.getValue(PlayerKeys.PLAYER_KILLS).orElse(Collections.emptyList()); + List deaths = player.getValue(PlayerKeys.PLAYER_DEATHS_KILLS).orElse(Collections.emptyList()); + + Map data = new HashMap<>(); + data.put("info", createInfoJSONMap(player, serverNames)); + data.put("online_activity", createOnlineActivityJSONMap(sessionsMutator)); + data.put("kill_data", createPvPPvEMap(player)); + + data.put("nicknames", player.getValue(PlayerKeys.NICKNAMES) + .map(nicks -> Nickname.fromDataNicknames(nicks, serverNames, year)) + .orElse(Collections.emptyList())); + data.put("connections", player.getValue(PlayerKeys.GEO_INFO) + .map(geoInfo -> ConnectionInfo.fromGeoInfo(geoInfo, year)) + .orElse(Collections.emptyList())); + data.put("player_kills", new PlayerKillMutator(kills).filterNonSelfKills().toJSONAsMap(formatters)); + data.put("player_deaths", new PlayerKillMutator(deaths).toJSONAsMap(formatters)); + data.put("sessions", sessionsMutator.toServerNameJSONMaps(graphs, config.getWorldAliasSettings(), formatters)); + data.put("sessions_per_page", config.get(DisplaySettings.SESSIONS_PER_PAGE)); + data.put("servers", serverAccordion); + data.put("punchcard_series", graphs.special().punchCard(sessionsMutator).getDots()); + WorldPie worldPie = graphs.pie().worldPie(player.getValue(PlayerKeys.WORLD_TIMES).orElse(new WorldTimes())); + data.put("world_pie_series", worldPie.getSlices()); + data.put("gm_series", worldPie.toHighChartsDrillDownMaps()); + data.put("calendar_series", graphs.calendar().playerCalendar(player).getEntries()); + data.put("server_pie_series", graphs.pie().serverPreferencePie(serverNames, worldTimesPerServer).getSlices()); + data.put("server_pie_colors", pieColors); + data.put("first_day", 1); // Monday + return data; + } + + private Map createOnlineActivityJSONMap(SessionsMutator sessionsMutator) { + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + SessionsMutator sessions30d = sessionsMutator.filterSessionsBetween(monthAgo, now); + SessionsMutator sessions7d = sessions30d.filterSessionsBetween(weekAgo, now); + + Map onlineActivity = new HashMap<>(); + + onlineActivity.put("playtime_30d", timeAmount.apply(sessions30d.toPlaytime())); + onlineActivity.put("active_playtime_30d", timeAmount.apply(sessions30d.toActivePlaytime())); + onlineActivity.put("afk_time_30d", timeAmount.apply(sessions30d.toAfkTime())); + onlineActivity.put("average_session_length_30d", timeAmount.apply(sessions30d.toAverageSessionLength())); + onlineActivity.put("session_count_30d", sessions30d.count()); + onlineActivity.put("player_kill_count_30d", sessions30d.toPlayerKillCount()); + onlineActivity.put("mob_kill_count_30d", sessions30d.toMobKillCount()); + onlineActivity.put("death_count_30d", sessions30d.toDeathCount()); + + onlineActivity.put("playtime_7d", timeAmount.apply(sessions7d.toPlaytime())); + onlineActivity.put("active_playtime_7d", timeAmount.apply(sessions7d.toActivePlaytime())); + onlineActivity.put("afk_time_7d", timeAmount.apply(sessions7d.toAfkTime())); + onlineActivity.put("average_session_length_7d", timeAmount.apply(sessions7d.toAverageSessionLength())); + onlineActivity.put("session_count_7d", sessions7d.count()); + onlineActivity.put("player_kill_count_7d", sessions7d.toPlayerKillCount()); + onlineActivity.put("mob_kill_count_7d", sessions7d.toMobKillCount()); + onlineActivity.put("death_count_7d", sessions7d.toDeathCount()); + + return onlineActivity; + } + + private Map createInfoJSONMap(PlayerContainer player, Map serverNames) { + SessionsMutator sessions = SessionsMutator.forContainer(player); + ActivityIndex activityIndex = player.getActivityIndex(System.currentTimeMillis(), config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD)); + PerServerMutator perServer = PerServerMutator.forContainer(player); + PingMutator ping = PingMutator.forContainer(player); + + Map info = new HashMap<>(); + + info.put("online", SessionCache.getCachedSession(player.getUnsafe(PlayerKeys.UUID)).isPresent()); + info.put("operator", player.getValue(PlayerKeys.OPERATOR).orElse(false)); + info.put("banned", player.getValue(PlayerKeys.BANNED).orElse(false)); + info.put("kick_count", player.getValue(PlayerKeys.KICK_COUNT).orElse(0)); + info.put("player_kill_count", player.getValue(PlayerKeys.PLAYER_KILL_COUNT).orElse(0)); + info.put("mob_kill_count", player.getValue(PlayerKeys.MOB_KILL_COUNT).orElse(0)); + info.put("death_count", player.getValue(PlayerKeys.DEATH_COUNT).orElse(0)); + info.put("playtime", timeAmount.apply(sessions.toPlaytime())); + info.put("active_playtime", timeAmount.apply(sessions.toActivePlaytime())); + info.put("afk_time", timeAmount.apply(sessions.toAfkTime())); + info.put("session_count", sessions.count()); + info.put("longest_session_length", timeAmount.apply(sessions.toLongestSessionLength())); + info.put("session_median", timeAmount.apply(sessions.toMedianSessionLength())); + info.put("activity_index", decimals.apply(activityIndex.getValue())); + info.put("activity_index_group", activityIndex.getGroup()); + UUID favoriteServer = perServer.favoriteServer(); + info.put("favorite_server", serverNames.getOrDefault(favoriteServer, favoriteServer.toString())); + double averagePing = ping.average(); + int worstPing = ping.max(); + int bestPing = ping.min(); + + String unavailable = locale.get(GenericLang.UNAVAILABLE).toString(); + info.put("average_ping", averagePing != -1.0 ? decimals.apply(averagePing) + " ms" : unavailable); + info.put("worst_ping", worstPing != -1.0 ? worstPing + " ms" : unavailable); + info.put("best_ping", bestPing != -1.0 ? bestPing + " ms" : unavailable); + info.put("registered", player.getValue(PlayerKeys.REGISTERED).map(year).orElse("-")); + info.put("last_seen", player.getValue(PlayerKeys.LAST_SEEN).map(year).orElse("-")); + + return info; + } + + private Map createPvPPvEMap(PlayerContainer playerContainer) { + long now = System.currentTimeMillis(); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + PlayerVersusMutator playerVersus = PlayerVersusMutator.forContainer(playerContainer); + PlayerVersusMutator playerVersus30d = playerVersus.filterBetween(monthAgo, now); + PlayerVersusMutator playerVersus7d = playerVersus30d.filterBetween(weekAgo, now); + + Map killData = new HashMap<>(); + int pks = playerVersus.toPlayerKillCount(); + int pks7d = playerVersus7d.toPlayerKillCount(); + int pks30d = playerVersus30d.toPlayerKillCount(); + killData.put("player_kills_total", pks); + killData.put("player_kills_30d", pks30d); + killData.put("player_kills_7d", pks7d); + + int playerDeaths = playerVersus.toPlayerDeathCount(); + int playerDeaths30d = playerVersus30d.toPlayerDeathCount(); + int playerDeaths7d = playerVersus7d.toPlayerDeathCount(); + killData.put("player_deaths_total", playerDeaths); + killData.put("player_deaths_30d", playerDeaths30d); + killData.put("player_deaths_7d", playerDeaths7d); + + double kdr = playerDeaths != 0 ? (double) pks / playerDeaths : pks; + double kdr30d = playerDeaths30d != 0 ? (double) pks30d / playerDeaths30d : pks30d; + double krd7d = playerDeaths7d != 0 ? (double) pks7d / playerDeaths7d : pks7d; + killData.put("player_kdr_total", decimals.apply(kdr)); + killData.put("player_kdr_30d", decimals.apply(kdr30d)); + killData.put("player_kdr_7d", decimals.apply(krd7d)); + + int mobKills = playerVersus.toMobKillCount(); + int mobKills30d = playerVersus30d.toMobKillCount(); + int mobKills7d = playerVersus7d.toMobKillCount(); + killData.put("mob_kills_total", mobKills); + killData.put("mob_kills_30d", mobKills30d); + killData.put("mob_kills_7d", mobKills7d); + + int deaths = playerVersus.toDeathCount(); + int deaths30d = playerVersus30d.toDeathCount(); + int deaths7d = playerVersus7d.toDeathCount(); + killData.put("deaths_total", deaths); + killData.put("deaths_30d", deaths30d); + killData.put("deaths_7d", deaths7d); + + int mobDeaths = deaths - playerDeaths; + int mobDeaths30d = deaths30d - playerDeaths30d; + int mobDeaths7d = deaths7d - playerDeaths7d; + + killData.put("mob_deaths_total", mobDeaths); + killData.put("mob_deaths_30d", mobDeaths30d); + killData.put("mob_deaths_7d", mobDeaths7d); + + double mobKdr = mobDeaths != 0 ? (double) mobKills / mobDeaths : mobKills; + double mobKdr30d = mobDeaths30d != 0 ? (double) mobKills30d / mobDeaths30d : mobKills30d; + double mobKdr7d = mobDeaths7d != 0 ? (double) mobKills7d / mobDeaths7d : mobKills7d; + killData.put("mob_kdr_total", decimals.apply(mobKdr)); + killData.put("mob_kdr_30d", decimals.apply(mobKdr30d)); + killData.put("mob_kdr_7d", decimals.apply(mobKdr7d)); + + List topWeapons = playerVersus.toTopWeapons(3); + killData.put("weapon_1st", getWeapon(topWeapons, 0).orElse("-")); + killData.put("weapon_2nd", getWeapon(topWeapons, 1).orElse("-")); + killData.put("weapon_3rd", getWeapon(topWeapons, 2).orElse("-")); + + return killData; + } + + private Optional getWeapon(List list, int index) { + return list.size() <= index ? Optional.empty() : Optional.of(list.get(index)); + } + + public static class Nickname { + private String nickname; + private String server; + private String date; + + public Nickname(String nickname, String server, String date) { + this.nickname = nickname; + this.server = server; + this.date = date; + } + + public static List fromDataNicknames( + List nicknames, + Map serverNames, + Formatter dateFormatter + ) { + nicknames.sort(new DateHolderRecentComparator()); + List mapped = new ArrayList<>(); + for (com.djrapitops.plan.delivery.domain.Nickname nickname : nicknames) { + mapped.add(new Nickname( + Html.swapColorCodesToSpan(StringEscapeUtils.escapeHtml4(nickname.getName())), + serverNames.getOrDefault(nickname.getServerUUID(), nickname.getServerUUID().toString()), + dateFormatter.apply(nickname.getDate()) + )); + } + return mapped; + } + } + + public static class ConnectionInfo { + private String geolocation; + private String date; + + public ConnectionInfo(String geolocation, String date) { + this.geolocation = geolocation; + this.date = date; + } + + public static List fromGeoInfo(List geoInfo, Formatter dateFormatter) { + return geoInfo.stream() + .map(i -> new ConnectionInfo(i.getGeolocation(), dateFormatter.apply(i.getDate()))) + .collect(Collectors.toList()); + } + } + +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONParser.java similarity index 72% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONParser.java index 0a4dee239..ce13fd312 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PlayersTableJSONParser.java @@ -14,24 +14,20 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.tables; +package com.djrapitops.plan.delivery.rendering.json; -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.mutators.ActivityIndex; -import com.djrapitops.plan.data.store.mutators.GeoInfoMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.domain.TablePlayer; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.html.Html; +import com.djrapitops.plan.delivery.rendering.html.icon.Family; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.implementation.results.*; -import com.djrapitops.plan.utilities.comparators.PlayerContainerLastPlayedComparator; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.icon.Family; -import com.djrapitops.plan.utilities.html.icon.Icon; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.HtmlLang; import java.util.*; @@ -44,13 +40,11 @@ import java.util.*; */ public class PlayersTableJSONParser { - private final List players; + private final List players; private final List extensionDescriptives; private final Map extensionData; + private final Locale locale; - private final int maxPlayers; - private final long activeMsThreshold; - private final int activeLoginThreshold; private final boolean openPlayerPageInNewTab; private Map> numberFormatters; @@ -58,26 +52,23 @@ public class PlayersTableJSONParser { private Formatter decimalFormatter; public PlayersTableJSONParser( - // Data - List players, + List players, Map extensionData, // Settings - int maxPlayers, long activeMsThreshold, int activeLoginThreshold, boolean openPlayerPageInNewTab, - // Formatters - Formatters formatters + boolean openPlayerPageInNewTab, + Formatters formatters, + Locale locale ) { // Data this.players = players; this.extensionData = extensionData; + this.locale = locale; extensionDescriptives = new ArrayList<>(); addExtensionDescriptives(extensionData); extensionDescriptives.sort((one, two) -> String.CASE_INSENSITIVE_ORDER.compare(one.getName(), two.getName())); // Settings - this.maxPlayers = maxPlayers; - this.activeMsThreshold = activeMsThreshold; - this.activeLoginThreshold = activeLoginThreshold; this.openPlayerPageInNewTab = openPlayerPageInNewTab; // Formatters numberFormatters = new EnumMap<>(FormatType.class); @@ -110,16 +101,9 @@ public class PlayersTableJSONParser { private String parseData() { StringBuilder dataJSON = new StringBuilder("["); - PlanAPI planAPI = PlanAPI.getInstance(); - long now = System.currentTimeMillis(); - players.sort(new PlayerContainerLastPlayedComparator()); - int currentPlayerNumber = 0; - for (PlayerContainer player : players) { - if (currentPlayerNumber >= maxPlayers) { - break; - } - UUID playerUUID = player.getValue(PlayerKeys.UUID).orElse(null); + for (TablePlayer player : players) { + UUID playerUUID = player.getPlayerUUID(); if (playerUUID == null) { continue; } @@ -129,7 +113,7 @@ public class PlayersTableJSONParser { } dataJSON.append('{'); // Start new item - appendPlayerData(dataJSON, planAPI, now, player); + appendPlayerData(dataJSON, player); appendExtensionData(dataJSON, extensionData.getOrDefault(playerUUID, new ExtensionTabData.Factory(null).build())); dataJSON.append('}'); // Close new item @@ -139,22 +123,21 @@ public class PlayersTableJSONParser { return dataJSON.append(']').toString(); } - private void appendPlayerData(StringBuilder dataJSON, PlanAPI planAPI, long now, PlayerContainer player) { - String name = player.getValue(PlayerKeys.NAME).orElse("Unknown"); - String url = planAPI.getPlayerInspectPageLink(name); + private void appendPlayerData(StringBuilder dataJSON, TablePlayer player) { + String name = player.getName().orElse(player.getPlayerUUID().toString()); + String url = "../player/" + name; - SessionsMutator sessionsMutator = SessionsMutator.forContainer(player); - int loginTimes = sessionsMutator.count(); - long playtime = sessionsMutator.toPlaytime(); - long registered = player.getValue(PlayerKeys.REGISTERED).orElse(0L); - long lastSeen = sessionsMutator.toLastSeen(); + int loginTimes = player.getSessionCount().orElse(0); + long playtime = player.getPlaytime().orElse(-1L); + long registered = player.getRegistered().orElse(-1L); + long lastSeen = player.getLastSeen().orElse(-1L); - ActivityIndex activityIndex = player.getActivityIndex(now, activeMsThreshold, activeLoginThreshold); - boolean isBanned = player.getValue(PlayerKeys.BANNED).orElse(false); + ActivityIndex activityIndex = player.getCurrentActivityIndex().orElseGet(() -> new ActivityIndex(0.0, 0)); + boolean isBanned = player.isBanned(); String activityString = activityIndex.getFormattedValue(decimalFormatter) - + (isBanned ? " (Banned)" : " (" + activityIndex.getGroup() + ")"); + + (isBanned ? " (" + locale.get(HtmlLang.LABEL_BANNED) + ")" : " (" + activityIndex.getGroup() + ")"); - String geolocation = GeoInfoMutator.forContainer(player).mostRecent().map(GeoInfo::getGeolocation).orElse("-"); + String geolocation = player.getGeolocation().orElse("-"); Html link = openPlayerPageInNewTab ? Html.LINK_EXTERNAL : Html.LINK; @@ -210,13 +193,13 @@ public class PlayersTableJSONParser { // Is the data for the column formatted columnHeaders - .append(makeColumnHeader(Icon.called("user") + " Name", "name")).append(',') - .append(makeFColumnHeader(Icon.called("check") + " Activity Index", "index")).append(',') - .append(makeFColumnHeader(Icon.called("clock").of(Family.REGULAR) + " Playtime", "playtime")).append(',') - .append(makeColumnHeader(Icon.called("calendar-plus").of(Family.REGULAR) + " Sessions", "sessions")).append(',') - .append(makeFColumnHeader(Icon.called("user-plus") + " Registered", "registered")).append(',') - .append(makeFColumnHeader(Icon.called("calendar-check").of(Family.REGULAR) + " Last Seen", "seen")).append(',') - .append(makeColumnHeader(Icon.called("globe") + " Geolocation", "geolocation")); + .append(makeColumnHeader(Icon.called("user") + " " + locale.get(HtmlLang.LABEL_NAME), "name")).append(',') + .append(makeFColumnHeader(Icon.called("check") + " " + locale.get(HtmlLang.LABEL_ACTIVITY_INDEX), "index")).append(',') + .append(makeFColumnHeader(Icon.called("clock").of(Family.REGULAR) + " " + locale.get(HtmlLang.LABEL_PLAYTIME), "playtime")).append(',') + .append(makeColumnHeader(Icon.called("calendar-plus").of(Family.REGULAR) + " " + locale.get(HtmlLang.SIDE_SESSIONS), "sessions")).append(',') + .append(makeFColumnHeader(Icon.called("user-plus") + " " + locale.get(HtmlLang.LABEL_REGISTERED), "registered")).append(',') + .append(makeFColumnHeader(Icon.called("calendar-check").of(Family.REGULAR) + " " + locale.get(HtmlLang.LABEL_LAST_SEEN), "seen")).append(',') + .append(makeColumnHeader(Icon.called("globe") + " " + locale.get(HtmlLang.TITLE_COUNTRY), "geolocation")); appendExtensionHeaders(columnHeaders); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PvPPvEJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PvPPvEJSONParser.java new file mode 100644 index 000000000..0840b2fd4 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/PvPPvEJSONParser.java @@ -0,0 +1,127 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.KillQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /server-page PvP & PvE tab. + * + * @author Rsl1122 + */ +@Singleton +public class PvPPvEJSONParser implements ServerTabJSONParser> { + + private DBSystem dbSystem; + + private Formatter decimals; + + @Inject + public PvPPvEJSONParser( + DBSystem dbSystem, + Formatters formatters + ) { + this.dbSystem = dbSystem; + + decimals = formatters.decimals(); + } + + public Map createJSONAsMap(UUID serverUUID) { + Map serverOverview = new HashMap<>(); + serverOverview.put("numbers", createNumbersMap(serverUUID)); + serverOverview.put("insights", createInsightsMap(serverUUID)); + return serverOverview; + } + + private Map createNumbersMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + Map numbers = new HashMap<>(); + Long pks = db.query(KillQueries.playerKillCount(0L, now, serverUUID)); + Long pks7d = db.query(KillQueries.playerKillCount(weekAgo, now, serverUUID)); + Long pks30d = db.query(KillQueries.playerKillCount(monthAgo, now, serverUUID)); + numbers.put("player_kills_total", pks); + numbers.put("player_kills_30d", pks30d); + numbers.put("player_kills_7d", pks7d); + + numbers.put("player_kdr_avg", decimals.apply(db.query(KillQueries.averageKDR(0L, now, serverUUID)))); + numbers.put("player_kdr_avg_30d", decimals.apply(db.query(KillQueries.averageKDR(monthAgo, now, serverUUID)))); + numbers.put("player_kdr_avg_7d", decimals.apply(db.query(KillQueries.averageKDR(weekAgo, now, serverUUID)))); + + Long mobKills = db.query(KillQueries.mobKillCount(0L, now, serverUUID)); + Long mobKills30d = db.query(KillQueries.mobKillCount(monthAgo, now, serverUUID)); + Long mobKills7d = db.query(KillQueries.mobKillCount(weekAgo, now, serverUUID)); + numbers.put("mob_kills_total", mobKills); + numbers.put("mob_kills_30d", mobKills30d); + numbers.put("mob_kills_7d", mobKills7d); + + Long deaths = db.query(KillQueries.deathCount(0L, now, serverUUID)); + Long deaths30d = db.query(KillQueries.deathCount(monthAgo, now, serverUUID)); + Long deaths7d = db.query(KillQueries.deathCount(weekAgo, now, serverUUID)); + numbers.put("deaths_total", deaths); + numbers.put("deaths_30d", deaths30d); + numbers.put("deaths_7d", deaths7d); + + long mobDeaths = deaths - pks; + long mobDeaths30d = deaths30d - pks30d; + long mobDeaths7d = deaths7d - pks7d; + + numbers.put("mob_deaths_total", mobDeaths); + numbers.put("mob_deaths_30d", mobDeaths30d); + numbers.put("mob_deaths_7d", mobDeaths7d); + + double mobKdr = mobDeaths != 0 ? (double) mobKills / mobDeaths : mobKills; + double mobKdr30d = mobDeaths30d != 0 ? (double) mobKills30d / mobDeaths30d : mobKills30d; + double mobKdr7d = mobDeaths7d != 0 ? (double) mobKills7d / mobDeaths7d : mobKills7d; + numbers.put("mob_kdr_total", decimals.apply(mobKdr)); + numbers.put("mob_kdr_30d", decimals.apply(mobKdr30d)); + numbers.put("mob_kdr_7d", decimals.apply(mobKdr7d)); + + return numbers; + } + + private Map createInsightsMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + Map insights = new HashMap<>(); + + List top3Weapons = db.query(KillQueries.topWeaponsOfServer(monthAgo, now, serverUUID, 3)); + insights.put("weapon_1st", getWeapon(top3Weapons, 0).orElse("-")); + insights.put("weapon_2nd", getWeapon(top3Weapons, 1).orElse("-")); + insights.put("weapon_3rd", getWeapon(top3Weapons, 2).orElse("-")); + + return insights; + } + + private Optional getWeapon(List list, int index) { + return list.size() <= index ? Optional.empty() : Optional.of(list.get(index)); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerAccordion.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerAccordion.java new file mode 100644 index 000000000..ca8b22bdb --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerAccordion.java @@ -0,0 +1,101 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.container.PerServerContainer; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PerServerKeys; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs; +import com.djrapitops.plan.delivery.rendering.json.graphs.pie.WorldPie; +import com.djrapitops.plan.gathering.domain.WorldTimes; + +import java.util.*; + +/** + * Utility for creating JSON for Server Accordion + * + * @author Rsl1122 + */ +public class ServerAccordion { + + private final Map serverNames; + private final PerServerContainer perServer; + private final String unknown; + + private final Graphs graphs; + private final Formatter year; + private final Formatter timeAmount; + + public ServerAccordion( + PlayerContainer container, Map serverNames, + Graphs graphs, + Formatter year, + Formatter timeAmount, + String unknown + ) { + this.graphs = graphs; + this.year = year; + this.timeAmount = timeAmount; + + this.serverNames = serverNames; + perServer = container.getValue(PlayerKeys.PER_SERVER) + .orElse(new PerServerContainer()); + this.unknown = unknown; + } + + public List> asMaps() { + List> servers = new ArrayList<>(); + + for (Map.Entry entry : perServer.entrySet()) { + UUID serverUUID = entry.getKey(); + DataContainer perServer = entry.getValue(); + Map server = new HashMap<>(); + + String serverName = serverNames.getOrDefault(serverUUID, unknown); + WorldTimes worldTimes = perServer.getValue(PerServerKeys.WORLD_TIMES).orElse(new WorldTimes()); + SessionsMutator sessionsMutator = SessionsMutator.forContainer(perServer); + + server.put("server_name", serverName); + + server.put("banned", perServer.getValue(PerServerKeys.BANNED).orElse(false)); + server.put("operator", perServer.getValue(PerServerKeys.OPERATOR).orElse(false)); + server.put("registered", year.apply(perServer.getValue(PerServerKeys.REGISTERED).orElse(0L))); + server.put("last_seen", year.apply(sessionsMutator.toLastSeen())); + + server.put("session_count", sessionsMutator.count()); + server.put("playtime", timeAmount.apply(sessionsMutator.toPlaytime())); + server.put("afk_time", timeAmount.apply(sessionsMutator.toAfkTime())); + server.put("session_median", timeAmount.apply(sessionsMutator.toMedianSessionLength())); + server.put("longest_session_length", timeAmount.apply(sessionsMutator.toLongestSessionLength())); + + server.put("mob_kills", sessionsMutator.toMobKillCount()); + server.put("player_kills", sessionsMutator.toPlayerKillCount()); + server.put("deaths", sessionsMutator.toDeathCount()); + + WorldPie worldPie = graphs.pie().worldPie(worldTimes); + server.put("world_pie_series", worldPie.getSlices()); + server.put("gm_series", worldPie.toHighChartsDrillDownMaps()); + + servers.add(server); + } + return servers; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerOverviewJSONParser.java new file mode 100644 index 000000000..d4fe2acb3 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerOverviewJSONParser.java @@ -0,0 +1,229 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.GenericLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.ServerAggregateQueries; +import com.djrapitops.plan.storage.database.queries.analysis.ActivityIndexQueries; +import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.storage.database.queries.objects.KillQueries; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; +import com.djrapitops.plan.storage.database.queries.objects.TPSQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /server-page Server Overview tab. + * + * @author Rsl1122 + */ +@Singleton +public class ServerOverviewJSONParser implements ServerTabJSONParser> { + + private final Formatter day; + private final PlanConfig config; + private final Locale locale; + private final DBSystem dbSystem; + private final ServerInfo serverInfo; + + private final Formatter timeAmount; + private final Formatter decimals; + private final Formatter percentage; + private final Formatter year; + private final TimeZone timeZone; + + @Inject + public ServerOverviewJSONParser( + PlanConfig config, + Locale locale, + DBSystem dbSystem, + ServerInfo serverInfo, + Formatters formatters + ) { + this.config = config; + this.locale = locale; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + + year = formatters.year(); + day = formatters.dayLong(); + timeAmount = formatters.timeAmount(); + decimals = formatters.decimals(); + percentage = formatters.percentage(); + this.timeZone = config.getTimeZone(); + } + + public Map createJSONAsMap(UUID serverUUID) { + Map serverOverview = new HashMap<>(); + serverOverview.put("last_7_days", createLast7DaysMap(serverUUID)); + serverOverview.put("numbers", createNumbersMap(serverUUID)); + serverOverview.put("weeks", createWeeksMap(serverUUID)); + return serverOverview; + } + + private Map createLast7DaysMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + + Map sevenDays = new HashMap<>(); + + sevenDays.put("unique_players", db.query(PlayerCountQueries.uniquePlayerCount(weekAgo, now, serverUUID))); + sevenDays.put("unique_players_day", db.query(PlayerCountQueries.averageUniquePlayerCount(weekAgo, now, timeZone.getOffset(now), serverUUID))); + + int new7d = db.query(PlayerCountQueries.newPlayerCount(weekAgo, now, serverUUID)); + int retained7d = db.query(PlayerCountQueries.retainedPlayerCount(weekAgo, now, serverUUID)); + double retentionPerc7d = new7d != 0 ? (double) retained7d / new7d : -1; + + sevenDays.put("new_players", new7d); + sevenDays.put("new_players_retention", retained7d); + sevenDays.put("new_players_retention_perc", percentage.apply(retentionPerc7d)); + TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(weekAgo, now, serverUUID))); + double averageTPS = tpsMutator.averageTPS(); + sevenDays.put("average_tps", averageTPS != -1 ? decimals.apply(averageTPS) : locale.get(GenericLang.UNAVAILABLE).toString()); + sevenDays.put("low_tps_spikes", tpsMutator.lowTpsSpikeCount(config.getNumber(DisplaySettings.GRAPH_TPS_THRESHOLD_MED))); + sevenDays.put("downtime", timeAmount.apply(tpsMutator.serverDownTime())); + + return sevenDays; + } + + private Map createNumbersMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long twoDaysAgo = now - TimeUnit.DAYS.toMillis(2L); + Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map numbers = new HashMap<>(); + + Integer userCount = db.query(ServerAggregateQueries.serverUserCount(serverUUID)); + numbers.put("total_players", userCount); + numbers.put("regular_players", db.query(ActivityIndexQueries.fetchRegularPlayerCount(now, serverUUID, playtimeThreshold))); + numbers.put("online_players", getOnlinePlayers(serverUUID, db)); + Optional> lastPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo)); + Optional> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID)); + numbers.put("last_peak_date", lastPeak.map(year).orElse("-")); + numbers.put("last_peak_players", lastPeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); + numbers.put("best_peak_date", allTimePeak.map(year).orElse("-")); + numbers.put("best_peak_players", allTimePeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); + Long totalPlaytime = db.query(SessionQueries.playtime(0L, now, serverUUID)); + numbers.put("playtime", timeAmount.apply(totalPlaytime)); + numbers.put("player_playtime", userCount != 0 ? timeAmount.apply(totalPlaytime / userCount) : "-"); + numbers.put("sessions", db.query(SessionQueries.sessionCount(0L, now, serverUUID))); + numbers.put("player_kills", db.query(KillQueries.playerKillCount(0L, now, serverUUID))); + numbers.put("mob_kills", db.query(KillQueries.mobKillCount(0L, now, serverUUID))); + numbers.put("deaths", db.query(KillQueries.deathCount(0L, now, serverUUID))); + + return numbers; + } + + private Object getOnlinePlayers(UUID serverUUID, Database db) { + return serverUUID.equals(serverInfo.getServerUUID()) + ? serverInfo.getServerProperties().getOnlinePlayers() + : db.query(TPSQueries.fetchLatestTPSEntryForServer(serverUUID)) + .map(TPS::getPlayers).map(Object::toString) + .orElse(locale.get(GenericLang.UNKNOWN).toString()); + } + + private Map createWeeksMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long oneWeekAgo = now - TimeUnit.DAYS.toMillis(7L); + long twoWeeksAgo = now - TimeUnit.DAYS.toMillis(14L); + Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map weeks = new HashMap<>(); + + weeks.put("start", day.apply(twoWeeksAgo)); + weeks.put("midpoint", day.apply(oneWeekAgo)); + weeks.put("end", day.apply(now)); + + Integer uniqueBefore = db.query(PlayerCountQueries.uniquePlayerCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Integer uniqueAfter = db.query(PlayerCountQueries.uniquePlayerCount(oneWeekAgo, now, serverUUID)); + Trend uniqueTrend = new Trend(uniqueBefore, uniqueAfter, false); + weeks.put("unique_before", uniqueBefore); + weeks.put("unique_after", uniqueAfter); + weeks.put("unique_trend", uniqueTrend); + + Integer newBefore = db.query(PlayerCountQueries.newPlayerCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Integer newAfter = db.query(PlayerCountQueries.newPlayerCount(oneWeekAgo, now, serverUUID)); + Trend newTrend = new Trend(newBefore, newAfter, false); + weeks.put("new_before", newBefore); + weeks.put("new_after", newAfter); + weeks.put("new_trend", newTrend); + + int regularBefore = db.query(ActivityIndexQueries.fetchRegularPlayerCount(oneWeekAgo, serverUUID, playtimeThreshold)); + int regularAfter = db.query(ActivityIndexQueries.fetchRegularPlayerCount(now, serverUUID, playtimeThreshold)); + weeks.put("regular_before", regularBefore); + weeks.put("regular_after", regularAfter); + weeks.put("regular_trend", new Trend(regularBefore, regularAfter, false)); + + Long playtimeBefore = db.query(SessionQueries.playtime(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long playtimeAfter = db.query(SessionQueries.playtime(oneWeekAgo, now, serverUUID)); + long avgPlaytimeBefore = uniqueBefore != 0 ? playtimeBefore / uniqueBefore : 0L; + long avgPlaytimeAfter = uniqueAfter != 0 ? playtimeAfter / uniqueAfter : 0L; + Trend avgPlaytimeTrend = new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false, timeAmount); + weeks.put("average_playtime_before", timeAmount.apply(avgPlaytimeBefore)); + weeks.put("average_playtime_after", timeAmount.apply(avgPlaytimeAfter)); + weeks.put("average_playtime_trend", avgPlaytimeTrend); + + Long sessionsBefore = db.query(SessionQueries.sessionCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long sessionsAfter = db.query(SessionQueries.sessionCount(oneWeekAgo, now, serverUUID)); + Trend sessionsTrend = new Trend(sessionsBefore, sessionsAfter, false); + weeks.put("sessions_before", sessionsBefore); + weeks.put("sessions_after", sessionsAfter); + weeks.put("sessions_trend", sessionsTrend); + + Long pksBefore = db.query(KillQueries.playerKillCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long pksAfter = db.query(KillQueries.playerKillCount(oneWeekAgo, now, serverUUID)); + Trend pksTrend = new Trend(pksBefore, pksAfter, false); + weeks.put("player_kills_before", pksBefore); + weeks.put("player_kills_after", pksAfter); + weeks.put("player_kills_trend", pksTrend); + + Long mkBefore = db.query(KillQueries.mobKillCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long mkAfter = db.query(KillQueries.mobKillCount(oneWeekAgo, now, serverUUID)); + Trend mkTrend = new Trend(mkBefore, mkAfter, false); + weeks.put("mob_kills_before", mkBefore); + weeks.put("mob_kills_after", mkAfter); + weeks.put("mob_kills_trend", mkTrend); + + Long deathsBefore = db.query(KillQueries.deathCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long deathsAfter = db.query(KillQueries.deathCount(oneWeekAgo, now, serverUUID)); + Trend deathTrend = new Trend(deathsBefore, deathsAfter, true); + weeks.put("deaths_before", deathsBefore); + weeks.put("deaths_after", deathsAfter); + weeks.put("deaths_trend", deathTrend); + + return weeks; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerTabJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerTabJSONParser.java new file mode 100644 index 000000000..7421f4d06 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/ServerTabJSONParser.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import java.util.UUID; +import java.util.function.Function; + +/** + * Interface for different tab JSON parsers. + * + * @author Rsl1122 + */ +public interface ServerTabJSONParser extends Function { + + T createJSONAsMap(UUID serverUUID); + + @Override + default T apply(UUID uuid) { + return createJSONAsMap(uuid); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/SessionsOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/SessionsOverviewJSONParser.java new file mode 100644 index 000000000..fff4396cf --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/SessionsOverviewJSONParser.java @@ -0,0 +1,93 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.gathering.domain.GMTimes; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; +import com.djrapitops.plan.storage.database.queries.objects.TPSQueries; +import com.djrapitops.plan.storage.database.queries.objects.WorldTimesQueries; +import org.apache.commons.text.WordUtils; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /server-page Sessions tab. + * + * @author Rsl1122 + */ +@Singleton +public class SessionsOverviewJSONParser implements ServerTabJSONParser> { + + private DBSystem dbSystem; + + private Formatter timeAmount; + private Formatter percentage; + + @Inject + public SessionsOverviewJSONParser( + DBSystem dbSystem, + Formatters formatters + ) { + this.dbSystem = dbSystem; + + timeAmount = formatters.timeAmount(); + percentage = formatters.percentage(); + } + + public Map createJSONAsMap(UUID serverUUID) { + return Collections.singletonMap("insights", createInsightsMap(serverUUID)); + } + + private Map createInsightsMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + List tpsData = db.query(TPSQueries.fetchTPSDataOfServer(monthAgo, now, serverUUID)); + TPSMutator tpsMutator = new TPSMutator(tpsData); + + Map insights = new HashMap<>(); + + long uptime = TimeUnit.DAYS.toMillis(30L) - tpsMutator.serverDownTime(); + long occupied = tpsMutator.serverOccupiedTime(); + insights.put("server_occupied", timeAmount.apply(occupied)); + insights.put("server_occupied_perc", uptime != 0 ? percentage.apply(1.0 * occupied / uptime) : "-"); + + Long playtime = db.query(SessionQueries.playtime(monthAgo, now, serverUUID)); + Long afkTime = db.query(SessionQueries.afkTime(monthAgo, now, serverUUID)); + insights.put("total_playtime", timeAmount.apply(playtime)); + insights.put("afk_time", timeAmount.apply(afkTime)); + insights.put("afk_time_perc", playtime != 0 ? percentage.apply(1.0 * afkTime / playtime) : "-"); + + GMTimes gmTimes = db.query(WorldTimesQueries.fetchGMTimes(monthAgo, now, serverUUID)); + Optional mostUsedGameMode = gmTimes.getMostUsedGameMode(); + Long longestGMTime = mostUsedGameMode.map(gmTimes::getTime).orElse(-1L); + insights.put("most_active_gamemode", mostUsedGameMode.map(WordUtils::capitalizeFully).orElse("Not Known")); + insights.put("most_active_gamemode_perc", playtime != 0 ? percentage.apply(1.0 * longestGMTime / playtime) : "-"); + + return insights; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/Trend.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/Trend.java new file mode 100644 index 000000000..8d02bac40 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/Trend.java @@ -0,0 +1,78 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json; + +import com.djrapitops.plan.delivery.formatting.Formatter; + +/** + * Represents a trend in the data - used for JSON format. + * + * @author Rsl1122 + */ +public class Trend { + + /** + * When a trend is reversed increase is "bad" (red) and decrease is "good" (green) + */ + public static final boolean REVERSED = true; + + private String text; + private String direction; + private boolean reversed; + + public Trend(long before, long after, boolean reversed) { + long difference = Math.abs(before - after); + this.text = Long.toString(difference); + this.direction = getDirection(before, after); + this.reversed = reversed; + } + + public Trend(long before, long after, boolean reversed, Formatter formatter) { + long difference = Math.abs(before - after); + this.text = formatter.apply(difference); + this.direction = getDirection(before, after); + this.reversed = reversed; + } + + public Trend(double before, double after, boolean reversed, Formatter formatter) { + double difference = Math.abs(before - after); + this.text = formatter.apply(difference); + this.direction = getDirection(before, after); + this.reversed = reversed; + } + + private String getDirection(double before, double after) { + if (before < after) { + return "+"; + } else if (before > after) { + return "-"; + } + return null; + } + + public String getText() { + return text; + } + + public String getDirection() { + return direction; + } + + public boolean isReversed() { + return reversed; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONParser.java new file mode 100644 index 000000000..1e1fca0d7 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/GraphJSONParser.java @@ -0,0 +1,292 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json.graphs; + +import com.djrapitops.plan.delivery.domain.DateMap; +import com.djrapitops.plan.delivery.domain.mutators.MutatorFunctions; +import com.djrapitops.plan.delivery.domain.mutators.PingMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.delivery.rendering.json.graphs.bar.BarGraph; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.LineGraphFactory; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.PingGraph; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point; +import com.djrapitops.plan.delivery.rendering.json.graphs.pie.Pie; +import com.djrapitops.plan.delivery.rendering.json.graphs.pie.WorldPie; +import com.djrapitops.plan.delivery.rendering.json.graphs.special.WorldMap; +import com.djrapitops.plan.delivery.rendering.json.graphs.stack.StackGraph; +import com.djrapitops.plan.gathering.domain.Ping; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.WorldTimes; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.analysis.ActivityIndexQueries; +import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries; +import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.storage.database.queries.objects.*; +import com.djrapitops.plugin.api.TimeAmount; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Perses Graph related Data JSON. + * + * @author Rsl1122 + */ +@Singleton +public class GraphJSONParser { + + private final PlanConfig config; + private final Theme theme; + private final DBSystem dbSystem; + private final Graphs graphs; + private final TimeZone timeZone; + + @Inject + public GraphJSONParser( + PlanConfig config, + Theme theme, + DBSystem dbSystem, + Graphs graphs + ) { + this.config = config; + this.theme = theme; + this.dbSystem = dbSystem; + this.graphs = graphs; + this.timeZone = config.getTimeZone(); + } + + public String performanceGraphJSON(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + LineGraphFactory lineGraphs = graphs.line(); + long now = System.currentTimeMillis(); + long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L); + TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(serverUUID))) + .filterDataBetween(halfYearAgo, now); + return '{' + + "\"playersOnline\":" + lineGraphs.playersOnlineGraph(tpsMutator).toHighChartsSeries() + + ",\"tps\":" + lineGraphs.tpsGraph(tpsMutator).toHighChartsSeries() + + ",\"cpu\":" + lineGraphs.cpuGraph(tpsMutator).toHighChartsSeries() + + ",\"ram\":" + lineGraphs.ramGraph(tpsMutator).toHighChartsSeries() + + ",\"entities\":" + lineGraphs.entityGraph(tpsMutator).toHighChartsSeries() + + ",\"chunks\":" + lineGraphs.chunkGraph(tpsMutator).toHighChartsSeries() + + ",\"disk\":" + lineGraphs.diskGraph(tpsMutator).toHighChartsSeries() + + '}'; + } + + public String playersOnlineGraph(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L); + + List points = db.query(TPSQueries.fetchPlayersOnlineOfServer(halfYearAgo, now, serverUUID)).stream() + .map(point -> new Point(point.getDate(), point.getValue())) + .collect(Collectors.toList()); + return "{\"playersOnline\":" + graphs.line().lineGraph(points).toHighChartsSeries() + '}'; + } + + public String uniqueAndNewGraphJSON(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + LineGraphFactory lineGraphs = graphs.line(); + long now = System.currentTimeMillis(); + long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L); + NavigableMap uniquePerDay = db.query( + PlayerCountQueries.uniquePlayerCounts(halfYearAgo, now, timeZone.getOffset(now), serverUUID) + ); + NavigableMap newPerDay = db.query( + PlayerCountQueries.newPlayerCounts(halfYearAgo, now, timeZone.getOffset(now), serverUUID) + ); + + return "{\"uniquePlayers\":" + + lineGraphs.lineGraph(MutatorFunctions.toPoints( + MutatorFunctions.addMissing(uniquePerDay, TimeUnit.DAYS.toMillis(1L), 0) + )).toHighChartsSeries() + + ",\"newPlayers\":" + + lineGraphs.lineGraph(MutatorFunctions.toPoints( + MutatorFunctions.addMissing(newPerDay, TimeUnit.DAYS.toMillis(1L), 0) + )).toHighChartsSeries() + + '}'; + } + + public String uniqueAndNewGraphJSON() { + Database db = dbSystem.getDatabase(); + LineGraphFactory lineGraphs = graphs.line(); + long now = System.currentTimeMillis(); + long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L); + NavigableMap uniquePerDay = db.query( + PlayerCountQueries.uniquePlayerCounts(halfYearAgo, now, timeZone.getOffset(now)) + ); + NavigableMap newPerDay = db.query( + PlayerCountQueries.newPlayerCounts(halfYearAgo, now, timeZone.getOffset(now)) + ); + + return "{\"uniquePlayers\":" + + lineGraphs.lineGraph(MutatorFunctions.toPoints( + MutatorFunctions.addMissing(uniquePerDay, TimeUnit.DAYS.toMillis(1L), 0) + )).toHighChartsSeries() + + ",\"newPlayers\":" + + lineGraphs.lineGraph(MutatorFunctions.toPoints( + MutatorFunctions.addMissing(newPerDay, TimeUnit.DAYS.toMillis(1L), 0) + )).toHighChartsSeries() + + '}'; + } + + public String serverCalendarJSON(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long twoYearsAgo = now - TimeUnit.DAYS.toMillis(730L); + NavigableMap uniquePerDay = db.query( + PlayerCountQueries.uniquePlayerCounts(twoYearsAgo, now, timeZone.getOffset(now), serverUUID) + ); + NavigableMap newPerDay = db.query( + PlayerCountQueries.newPlayerCounts(twoYearsAgo, now, timeZone.getOffset(now), serverUUID) + ); + NavigableMap playtimePerDay = db.query( + SessionQueries.playtimePerDay(twoYearsAgo, now, timeZone.getOffset(now), serverUUID) + ); + NavigableMap sessionsPerDay = db.query( + SessionQueries.sessionCountPerDay(twoYearsAgo, now, timeZone.getOffset(now), serverUUID) + ); + return "{\"data\":" + + graphs.calendar().serverCalendar( + uniquePerDay, + newPerDay, + playtimePerDay, + sessionsPerDay + ).toCalendarSeries() + + ",\"firstDay\":" + 1 + '}'; + } + + public Map serverWorldPieJSONAsMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + WorldTimes worldTimes = db.query(WorldTimesQueries.fetchServerTotalWorldTimes(serverUUID)); + WorldPie worldPie = graphs.pie().worldPie(worldTimes); + + Map dataMap = new HashMap<>(); + dataMap.put("world_series", worldPie.getSlices()); + dataMap.put("gm_series", worldPie.toHighChartsDrillDownMaps()); + return dataMap; + } + + public Map activityGraphsJSONAsMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long date = System.currentTimeMillis(); + Long threshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + DateMap> activityData = new DateMap<>(); + for (long time = date; time >= date - TimeAmount.MONTH.toMillis(2L); time -= TimeAmount.WEEK.toMillis(1L)) { + activityData.put(time, db.query(ActivityIndexQueries.fetchActivityIndexGroupingsOn(time, serverUUID, threshold))); + } + + Map.Entry> lastActivityEntry = activityData.lastEntry(); + Pie activityPie = graphs.pie().activityPie(lastActivityEntry != null ? lastActivityEntry.getValue() : Collections.emptyMap()); + StackGraph activityStackGraph = graphs.stack().activityStackGraph(activityData); + + Map dataMap = new HashMap<>(); + dataMap.put("activity_series", activityStackGraph.getDataSets()); + dataMap.put("activity_labels", activityStackGraph.getLabels()); + dataMap.put("activity_pie_series", activityPie.getSlices()); + return dataMap; + } + + public Map activityGraphsJSONAsMap() { + Database db = dbSystem.getDatabase(); + long date = System.currentTimeMillis(); + Long threshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + DateMap> activityData = new DateMap<>(); + for (long time = date; time >= date - TimeAmount.MONTH.toMillis(2L); time -= TimeAmount.WEEK.toMillis(1L)) { + activityData.put(time, db.query(NetworkActivityIndexQueries.fetchActivityIndexGroupingsOn(time, threshold))); + } + + Map.Entry> lastActivityEntry = activityData.lastEntry(); + Pie activityPie = graphs.pie().activityPie(lastActivityEntry != null ? lastActivityEntry.getValue() : Collections.emptyMap()); + StackGraph activityStackGraph = graphs.stack().activityStackGraph(activityData); + + Map dataMap = new HashMap<>(); + dataMap.put("activity_series", activityStackGraph.getDataSets()); + dataMap.put("activity_labels", activityStackGraph.getLabels()); + dataMap.put("activity_pie_series", activityPie.getSlices()); + return dataMap; + } + + public Map geolocationGraphsJSONAsMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + Map geolocationCounts = db.query(GeoInfoQueries.serverGeolocationCounts(serverUUID)); + + BarGraph geolocationBarGraph = graphs.bar().geolocationBarGraph(geolocationCounts); + WorldMap worldMap = graphs.special().worldMap(geolocationCounts); + + Map dataMap = new HashMap<>(); + dataMap.put("geolocation_series", worldMap.getEntries()); + dataMap.put("geolocation_bar_series", geolocationBarGraph.getBars()); + return dataMap; + } + + public Map geolocationGraphsJSONAsMap() { + Database db = dbSystem.getDatabase(); + Map geolocationCounts = db.query(GeoInfoQueries.networkGeolocationCounts()); + + BarGraph geolocationBarGraph = graphs.bar().geolocationBarGraph(geolocationCounts); + WorldMap worldMap = graphs.special().worldMap(geolocationCounts); + + Map dataMap = new HashMap<>(); + dataMap.put("geolocation_series", worldMap.getEntries()); + dataMap.put("geolocation_bar_series", geolocationBarGraph.getBars()); + return dataMap; + } + + public String pingGraphsJSON(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + List pings = db.query(PingQueries.fetchPingDataOfServer(now - TimeUnit.DAYS.toMillis(180L), now, serverUUID)); + + PingGraph pingGraph = graphs.line().pingGraph(new PingMutator(pings).mutateToByMinutePings().all());// TODO Optimize in query + + return "{\"min_ping_series\":" + pingGraph.getMinGraph().toHighChartsSeries() + + ",\"avg_ping_series\":" + pingGraph.getAvgGraph().toHighChartsSeries() + + ",\"max_ping_series\":" + pingGraph.getMaxGraph().toHighChartsSeries() + '}'; + } + + public Map punchCardJSONAsMap(UUID serverUUID) { + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + List sessions = dbSystem.getDatabase().query( + SessionQueries.fetchServerSessionsWithoutKillOrWorldData(monthAgo, now, serverUUID) + ); + return Collections.singletonMap("punchCard", graphs.special().punchCard(sessions).getDots()); + } + + public Map serverPreferencePieJSONAsMap() { + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + String[] pieColors = theme.getPieColors(ThemeVal.GRAPH_WORLD_PIE); + Map data = new HashMap<>(); + data.put("server_pie_colors", pieColors); + + Map serverPlaytimes = dbSystem.getDatabase().query(SessionQueries.playtimePerServer(now, monthAgo)); + data.put("server_pie_series_30d", graphs.pie().serverPreferencePie(serverPlaytimes).getSlices()); + return data; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/Graphs.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/Graphs.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/Graphs.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/Graphs.java index 928dda542..79f5d1821 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/Graphs.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/Graphs.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs; +package com.djrapitops.plan.delivery.rendering.json.graphs; -import com.djrapitops.plan.utilities.html.graphs.bar.BarGraphFactory; -import com.djrapitops.plan.utilities.html.graphs.calendar.CalendarFactory; -import com.djrapitops.plan.utilities.html.graphs.line.LineGraphFactory; -import com.djrapitops.plan.utilities.html.graphs.pie.PieGraphFactory; -import com.djrapitops.plan.utilities.html.graphs.special.SpecialGraphFactory; -import com.djrapitops.plan.utilities.html.graphs.stack.StackGraphFactory; +import com.djrapitops.plan.delivery.rendering.json.graphs.bar.BarGraphFactory; +import com.djrapitops.plan.delivery.rendering.json.graphs.calendar.CalendarFactory; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.LineGraphFactory; +import com.djrapitops.plan.delivery.rendering.json.graphs.pie.PieGraphFactory; +import com.djrapitops.plan.delivery.rendering.json.graphs.special.SpecialGraphFactory; +import com.djrapitops.plan.delivery.rendering.json.graphs.stack.StackGraphFactory; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/HighChart.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/HighChart.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/HighChart.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/HighChart.java index ee41ba671..36581588b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/HighChart.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/HighChart.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs; +package com.djrapitops.plan.delivery.rendering.json.graphs; /** * Interface for Graphs with HighCharts data support. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/ProgressBar.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/ProgressBar.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/ProgressBar.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/ProgressBar.java index aa4d1dd71..59835ce64 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/ProgressBar.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/ProgressBar.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs; +package com.djrapitops.plan.delivery.rendering.json.graphs; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatter; /** * Utility for creating Progress bars. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/Bar.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/Bar.java similarity index 72% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/Bar.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/Bar.java index 87c766bce..f32798346 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/Bar.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/Bar.java @@ -14,7 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.bar; +package com.djrapitops.plan.delivery.rendering.json.graphs.bar; + +import java.util.Objects; public class Bar implements Comparable { @@ -38,4 +40,18 @@ public class Bar implements Comparable { public int compareTo(Bar bar) { return Long.compare(bar.value, this.value); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Bar)) return false; + Bar bar = (Bar) o; + return value == bar.value && + Objects.equals(label, bar.label); + } + + @Override + public int hashCode() { + return Objects.hash(label, value); + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/BarGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/BarGraph.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/BarGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/BarGraph.java index a3121b53f..012a367f1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/BarGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/BarGraph.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.bar; +package com.djrapitops.plan.delivery.rendering.json.graphs.bar; -import com.djrapitops.plan.utilities.html.graphs.HighChart; +import com.djrapitops.plan.delivery.rendering.json.graphs.HighChart; import org.apache.commons.text.TextStringBuilder; import java.util.List; @@ -29,6 +29,10 @@ public class BarGraph implements HighChart { this.bars = bars; } + public List getBars() { + return bars; + } + public String toHighChartsCategories() { TextStringBuilder categories = new TextStringBuilder("["); categories.appendWithSeparators(bars.stream().map(bar -> "'" + bar.getLabel() + "'").iterator(), ","); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/BarGraphFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/BarGraphFactory.java similarity index 78% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/BarGraphFactory.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/BarGraphFactory.java index 60fb6db65..f1bbafcd7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/BarGraphFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/BarGraphFactory.java @@ -14,12 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.bar; +package com.djrapitops.plan.delivery.rendering.json.graphs.bar; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; +import com.djrapitops.plan.delivery.domain.mutators.PlayersMutator; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.Map; /** * Factory class for Bar Graphs. @@ -36,4 +37,8 @@ public class BarGraphFactory { public BarGraph geolocationBarGraph(PlayersMutator playersMutator) { return new GeolocationBarGraph(playersMutator); } + + public BarGraph geolocationBarGraph(Map geolocationCounts) { + return new GeolocationBarGraph(geolocationCounts); + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/GeolocationBarGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/GeolocationBarGraph.java similarity index 71% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/GeolocationBarGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/GeolocationBarGraph.java index 410c2344b..c32f75353 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/bar/GeolocationBarGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/bar/GeolocationBarGraph.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.bar; +package com.djrapitops.plan.delivery.rendering.json.graphs.bar; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; +import com.djrapitops.plan.delivery.domain.mutators.PlayersMutator; import java.util.HashMap; import java.util.List; @@ -30,20 +30,27 @@ public class GeolocationBarGraph extends BarGraph { } private GeolocationBarGraph(List geolocations) { - super(turnToBars(geolocations)); + super(turnToBars(toGeolocationCounts(geolocations))); } - private static List turnToBars(List geolocations) { - Map counts = new HashMap<>(); + GeolocationBarGraph(Map geolocationCounts) { + super(turnToBars(geolocationCounts)); + } - for (String geolocation : geolocations) { - counts.put(geolocation, counts.getOrDefault(geolocation, 0) + 1); - } - - return counts.entrySet().stream() + private static List turnToBars(Map geolocationCounts) { + return geolocationCounts.entrySet().stream() .map(entry -> new Bar(entry.getKey(), entry.getValue())) .sorted() .limit(20L) .collect(Collectors.toList()); } + + private static Map toGeolocationCounts(List geolocations) { + Map counts = new HashMap<>(); + + for (String geolocation : geolocations) { + counts.put(geolocation, counts.getOrDefault(geolocation, 0) + 1); + } + return counts; + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarEntry.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarEntry.java new file mode 100644 index 000000000..4dba04343 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarEntry.java @@ -0,0 +1,78 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json.graphs.calendar; + +import java.io.Serializable; +import java.util.Optional; + +/** + * Represents an entry for calendar. + * + * @author Rsl1122 + */ +public class CalendarEntry { + + private final String title; + private final Serializable start; + private Serializable end; + private String color; + + private CalendarEntry(String title, Serializable start) { + this.title = title; + this.start = start; + } + + public static CalendarEntry of(String title, Serializable start) { + return new CalendarEntry(title, start); + } + + public CalendarEntry withEnd(Serializable end) { + this.end = end; + return this; + } + + public CalendarEntry withColor(String color) { + this.color = color; + return this; + } + + public String getTitle() { + return title; + } + + public Serializable getStart() { + return start; + } + + public Optional getEnd() { + return Optional.ofNullable(end); + } + + public Optional getColor() { + return Optional.ofNullable(color); + } + + @Override + public String toString() { + return "{" + + "title:'" + title + '\'' + + ", start:'" + start + '\'' + + (end != null ? ", end='" + end + '\'' : "") + + (color != null ? ", color='" + color + '\'' : "") + + '}'; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/CalendarFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarFactory.java similarity index 63% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/CalendarFactory.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarFactory.java index 0f86460c0..2176c5398 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/CalendarFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/CalendarFactory.java @@ -14,19 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.calendar; +package com.djrapitops.plan.delivery.rendering.json.graphs.calendar; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.utilities.formatting.Formatters; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.TimeZone; -import java.util.TreeMap; +import java.util.NavigableMap; +import java.util.SortedMap; /** * Factory class for different objects representing HTML calendars. @@ -37,15 +36,18 @@ import java.util.TreeMap; public class CalendarFactory { private final Theme theme; private final PlanConfig config; + private final Locale locale; private final Formatters formatters; @Inject public CalendarFactory( PlanConfig config, + Locale locale, Formatters formatters, Theme theme ) { this.config = config; + this.locale = locale; this.formatters = formatters; this.theme = theme; } @@ -53,20 +55,20 @@ public class CalendarFactory { public PlayerCalendar playerCalendar(PlayerContainer player) { return new PlayerCalendar( player, - formatters.timeAmount(), formatters.yearLong(), formatters.iso8601NoClockLong(), theme, - config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT") + formatters.timeAmount(), formatters.yearLong(), formatters.iso8601NoClockLong(), theme, locale, + config.getTimeZone() ); } public ServerCalendar serverCalendar( - PlayersMutator mutator, - TreeMap uniquePerDay, - TreeMap newPerDay + SortedMap uniquePerDay, + SortedMap newPerDay, + SortedMap playtimePerDay, + NavigableMap sessionsPerDay ) { return new ServerCalendar( - mutator, uniquePerDay, newPerDay, - formatters.iso8601NoClockLong(), formatters.timeAmount(), theme, - config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT") + uniquePerDay, newPerDay, playtimePerDay, sessionsPerDay, + formatters.iso8601NoClockLong(), formatters.timeAmount(), theme, locale ); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/PlayerCalendar.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/PlayerCalendar.java similarity index 52% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/PlayerCalendar.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/PlayerCalendar.java index 56d3f0ca4..daa56e670 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/calendar/PlayerCalendar.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/PlayerCalendar.java @@ -14,16 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.calendar; +package com.djrapitops.plan.delivery.rendering.json.graphs.calendar; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.settings.theme.ThemeVal; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.HtmlLang; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; import java.util.*; import java.util.concurrent.TimeUnit; @@ -35,10 +37,11 @@ import java.util.concurrent.TimeUnit; */ public class PlayerCalendar { - private final Formatter timeAmountFormatter; - private final Formatter yearLongFormatter; + private final Formatter timeAmount; + private final Formatter year; private final Formatter iso8601Formatter; private final Theme theme; + private final Locale locale; private final TimeZone timeZone; private final List allSessions; @@ -46,33 +49,33 @@ public class PlayerCalendar { PlayerCalendar( PlayerContainer container, - Formatter timeAmountFormatter, - Formatter yearLongFormatter, + Formatter timeAmount, + Formatter year, Formatter iso8601Formatter, Theme theme, + Locale locale, TimeZone timeZone ) { this.allSessions = container.getValue(PlayerKeys.SESSIONS).orElse(new ArrayList<>()); this.registered = container.getValue(PlayerKeys.REGISTERED).orElse(0L); - this.timeAmountFormatter = timeAmountFormatter; - this.yearLongFormatter = yearLongFormatter; + this.timeAmount = timeAmount; + this.year = year; this.iso8601Formatter = iso8601Formatter; this.theme = theme; + this.locale = locale; this.timeZone = timeZone; } - public String toCalendarSeries() { - StringBuilder series = new StringBuilder("["); + public List getEntries() { + List entries = new ArrayList<>(); - appendRegister(series); - appendDailyPlaytime(series); - appendSessionsAndKills(series); + entries.add(CalendarEntry + .of(locale.getString(HtmlLang.LABEL_REGISTERED) + ": " + year.apply(registered), + registered + ).withColor(theme.getValue(ThemeVal.LIGHT_GREEN)) + ); - return series.append("]").toString(); - } - - private void appendDailyPlaytime(StringBuilder series) { Map> sessionsByDay = getSessionsByDay(); for (Map.Entry> entry : sessionsByDay.entrySet()) { @@ -82,15 +85,42 @@ public class PlayerCalendar { int sessionCount = sessions.size(); long playtime = sessions.stream().mapToLong(Session::getLength).sum(); - series.append(",{title: 'Playtime: ").append(timeAmountFormatter.apply(playtime)) - .append("',start:'").append(day) - .append("',color: '").append(theme.getValue(ThemeVal.GREEN)).append("'") - .append("}"); - - series.append(",{title: 'Sessions: ").append(sessionCount) - .append("',start:'").append(day) - .append("'}"); + entries.add(CalendarEntry + .of(locale.getString(HtmlLang.LABEL_PLAYTIME) + ": " + timeAmount.apply(playtime), day) + .withColor(theme.getValue(ThemeVal.GREEN)) + ); + entries.add(CalendarEntry.of(locale.getString(HtmlLang.SIDE_SESSIONS) + ": " + sessionCount, day)); } + + long fiveMinutes = TimeUnit.MINUTES.toMillis(5L); + + for (Session session : allSessions) { + String length = timeAmount.apply(session.getLength()); + Long start = session.getUnsafe(SessionKeys.START); + Long end = session.getValue(SessionKeys.END).orElse(System.currentTimeMillis()); + + entries.add(CalendarEntry + .of(locale.getString(HtmlLang.SESSION) + ": " + length, + start + timeZone.getOffset(start)) + .withEnd(end + timeZone.getOffset(end)) + ); + + for (PlayerKill kill : session.getPlayerKills()) { + long time = kill.getDate(); + String victim = kill.getVictimName().orElse(kill.getVictim().toString()); + entries.add(CalendarEntry + .of(locale.getString(HtmlLang.KILLED) + ": " + victim, time) + .withEnd(time + fiveMinutes) + .withColor(theme.getValue(ThemeVal.RED)) + ); + } + } + + return entries; + } + + public String toCalendarSeries() { + return getEntries().toString(); } private Map> getSessionsByDay() { @@ -104,34 +134,4 @@ public class PlayerCalendar { } return sessionsByDay; } - - private void appendSessionsAndKills(StringBuilder series) { - long fiveMinutes = TimeUnit.MINUTES.toMillis(5L); - - for (Session session : allSessions) { - String length = timeAmountFormatter.apply(session.getLength()); - Long start = session.getUnsafe(SessionKeys.START); - Long end = session.getValue(SessionKeys.END).orElse(System.currentTimeMillis()); - - series.append(",{title: 'Session: ").append(length) - .append("',start:").append(start + timeZone.getOffset(start)) - .append(",end:").append(end + timeZone.getOffset(end)) - .append("}"); - - for (PlayerKill kill : session.getPlayerKills()) { - long time = kill.getDate(); - - series.append(",{title: 'Killed: ").append(kill.getVictimName().orElse(kill.getVictim().toString())) - .append("',start:").append(time) - .append(",end:").append(time + fiveMinutes) - .append(",color: '").append(theme.getValue(ThemeVal.RED)).append("'") - .append("}"); - } - } - } - - private void appendRegister(StringBuilder series) { - series.append("{title: 'Registered: ").append(yearLongFormatter.apply(registered)).append("'," + - "start: ").append(this.registered).append(",color: '").append(theme.getValue(ThemeVal.LIGHT_GREEN)).append("'}"); - } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/ServerCalendar.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/ServerCalendar.java new file mode 100644 index 000000000..43b267cf9 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/calendar/ServerCalendar.java @@ -0,0 +1,147 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json.graphs.calendar; + +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.HtmlLang; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; + +import java.util.Map; +import java.util.NavigableMap; +import java.util.SortedMap; + +/** + * Utility for creating FullCalendar calendar event array on Player page. + * + * @author Rsl1122 + */ +public class ServerCalendar { + + private final SortedMap uniquePerDay; + private final SortedMap newPerDay; + private final SortedMap sessionsPerDay; + private final SortedMap playtimePerDay; + + private final Formatter iso8601Formatter; + private final Formatter timeAmountFormatter; + private final Theme theme; + private final Locale locale; + + ServerCalendar( + SortedMap uniquePerDay, + SortedMap newPerDay, + SortedMap playtimePerDay, + NavigableMap sessionsPerDay, + Formatter iso8601Formatter, + Formatter timeAmountFormatter, + Theme theme, + Locale locale + ) { + this.uniquePerDay = uniquePerDay; + this.newPerDay = newPerDay; + this.iso8601Formatter = iso8601Formatter; + this.timeAmountFormatter = timeAmountFormatter; + this.sessionsPerDay = sessionsPerDay; + this.playtimePerDay = playtimePerDay; + this.theme = theme; + this.locale = locale; + } + + public String toCalendarSeries() { + StringBuilder series = new StringBuilder("["); + + series.append("{\"title\": \"badcode\",\"start\":0}"); + appendTimeZoneOffsetData(series); + + return series.append("]").toString(); + } + + private void appendTimeZoneOffsetData(StringBuilder series) { + appendUniquePlayers(series); + appendNewPlayers(series); + appendSessionCounts(series); + appendPlaytime(series); + } + + private void appendNewPlayers(StringBuilder series) { + for (Map.Entry entry : newPerDay.entrySet()) { + int newPlayers = entry.getValue(); + if (newPlayers <= 0) { + continue; + } + + Long key = entry.getKey(); + String day = iso8601Formatter.apply(key); + + series.append(",{\"title\": \"").append(locale.get(HtmlLang.NEW_CALENDAR)).append(" ").append(newPlayers) + .append("\",\"start\":\"").append(day) + .append("\",\"color\": \"").append(theme.getValue(ThemeVal.LIGHT_GREEN)).append('"') + .append("}"); + } + } + + private void appendUniquePlayers(StringBuilder series) { + for (Map.Entry entry : uniquePerDay.entrySet()) { + long uniquePlayers = entry.getValue(); + if (uniquePlayers <= 0) { + continue; + } + + Long key = entry.getKey(); + String day = iso8601Formatter.apply(key); + + series.append(",{\"title\": \"").append(locale.get(HtmlLang.UNIQUE_CALENDAR)).append(" ").append(uniquePlayers) + .append("\",\"start\":\"").append(day) + .append("\"}"); + + } + } + + private void appendPlaytime(StringBuilder series) { + for (Map.Entry entry : playtimePerDay.entrySet()) { + long playtime = entry.getValue(); + if (playtime <= 0) { + continue; + } + Long key = entry.getKey(); + String day = iso8601Formatter.apply(key); + + series.append(",{\"title\": \"").append(locale.get(HtmlLang.LABEL_PLAYTIME)).append(": ").append(timeAmountFormatter.apply(playtime)) + .append("\",\"start\":\"").append(day) + .append("\",\"color\": \"").append(theme.getValue(ThemeVal.GREEN)).append('"') + .append("}"); + } + } + + private void appendSessionCounts(StringBuilder series) { + for (Map.Entry entry : sessionsPerDay.entrySet()) { + int sessionCount = entry.getValue(); + if (sessionCount <= 0) { + continue; + } + Long key = entry.getKey(); + String day = iso8601Formatter.apply(key); + + series.append(",{\"title\": \"").append(locale.get(HtmlLang.SIDE_SESSIONS)).append(": ").append(sessionCount) + .append("\",\"start\":\"").append(day) + .append("\",\"color\": \"").append(theme.getValue(ThemeVal.TEAL)).append('"') + .append("}"); + } + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/CPUGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/CPUGraph.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/CPUGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/CPUGraph.java index b9b759677..2d1697a6f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/CPUGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/CPUGraph.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.timed.TPSCounter; /** * Graph about CPU Usage gathered by TPSCountTimer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.tasks.TPSCountTimer + * @see TPSCounter */ class CPUGraph extends LineGraph { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/ChunkGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/ChunkGraph.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/ChunkGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/ChunkGraph.java index 1b7e1c82e..5b04f1192 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/ChunkGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/ChunkGraph.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.timed.TPSCounter; /** * Graph about Chunk Counts gathered by TPSCountTimer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.tasks.TPSCountTimer + * @see TPSCounter */ class ChunkGraph extends LineGraph { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/DiskGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/DiskGraph.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/DiskGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/DiskGraph.java index 66347174d..5edb5fdd2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/DiskGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/DiskGraph.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.timed.TPSCounter; /** * Graph about Disk Usage gathered by TPSCountTimer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.tasks.TPSCountTimer + * @see TPSCounter */ class DiskGraph extends LineGraph { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/EntityGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/EntityGraph.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/EntityGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/EntityGraph.java index 5b4baf4a4..978410db6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/EntityGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/EntityGraph.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.timed.TPSCounter; /** * Graph about Entity Counts gathered by TPSCountTimer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.tasks.TPSCountTimer + * @see TPSCounter */ class EntityGraph extends LineGraph { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/Line.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/Line.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/Line.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/Line.java index 767c7112b..832433b06 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/Line.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/Line.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; /** * This math object is used in Ramer–Douglas–Peucker algorithm. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/LineGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/LineGraph.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/LineGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/LineGraph.java index a8bd4b714..37554db3f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/LineGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/LineGraph.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.utilities.html.graphs.HighChart; +import com.djrapitops.plan.delivery.domain.mutators.MutatorFunctions; +import com.djrapitops.plan.delivery.rendering.json.graphs.HighChart; import java.util.List; import java.util.concurrent.TimeUnit; @@ -62,6 +63,13 @@ public class LineGraph implements HighChart { return arrayBuilder.toString(); } + public List getPoints() { + if (displayGaps) { + return MutatorFunctions.addMissing(points, TimeUnit.MINUTES.toMillis(1L), null); + } + return points; + } + private void addMissingPoints(StringBuilder arrayBuilder, Long lastX, long date) { long iterate = lastX + TimeUnit.MINUTES.toMillis(1L); while (iterate < date) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/LineGraphFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/LineGraphFactory.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/LineGraphFactory.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/LineGraphFactory.java index e92a2fc30..63f406a96 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/LineGraphFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/LineGraphFactory.java @@ -14,12 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.store.mutators.TPSMutator; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DisplaySettings; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.domain.Ping; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.utilities.comparators.PointComparator; import javax.inject.Inject; import javax.inject.Singleton; @@ -43,6 +44,7 @@ public class LineGraphFactory { } public LineGraph lineGraph(List points) { + points.sort(new PointComparator()); return new LineGraph(points, shouldDisplayGapsInData()); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/PingGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/PingGraph.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/PingGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/PingGraph.java index fe36144ae..3dadc45e0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/PingGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/PingGraph.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.container.Ping; +import com.djrapitops.plan.gathering.domain.Ping; import java.util.ArrayList; import java.util.List; @@ -63,4 +63,16 @@ public class PingGraph { public String toAvgSeries() { return avgGraph.toHighChartsSeries(); } + + public LineGraph getMaxGraph() { + return maxGraph; + } + + public LineGraph getMinGraph() { + return minGraph; + } + + public LineGraph getAvgGraph() { + return avgGraph; + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/PlayersOnlineGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/PlayersOnlineGraph.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/PlayersOnlineGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/PlayersOnlineGraph.java index 130517c9d..5c894eeef 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/PlayersOnlineGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/PlayersOnlineGraph.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.timed.TPSCounter; /** * Graph about Player Counts gathered by TPSCountTimer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.tasks.TPSCountTimer + * @see TPSCounter */ class PlayersOnlineGraph extends LineGraph { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/Point.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/Point.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/Point.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/Point.java index a6b39a5ef..f439b20cd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/Point.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/Point.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; import java.util.Objects; @@ -62,4 +62,8 @@ public class Point { "x=" + x + ", " + "y=" + y + '}'; } + + public double[] toArray() { + return new double[]{x, y}; + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/RamGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/RamGraph.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/RamGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/RamGraph.java index 37ffc33e7..5a7995dfd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/RamGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/RamGraph.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.timed.TPSCounter; /** * Graph about RAM Usage gathered by TPSCountTimer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.tasks.TPSCountTimer + * @see TPSCounter */ class RamGraph extends LineGraph { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/TPSGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/TPSGraph.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/TPSGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/TPSGraph.java index 11a5168cc..6db40e925 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/TPSGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/TPSGraph.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line; +package com.djrapitops.plan.delivery.rendering.json.graphs.line; -import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.delivery.domain.mutators.TPSMutator; +import com.djrapitops.plan.gathering.timed.TPSCounter; /** * Graph about TPS gathered by TPSCountTimer. * * @author Rsl1122 - * @see com.djrapitops.plan.system.tasks.TPSCountTimer + * @see TPSCounter */ class TPSGraph extends LineGraph { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/alg/DouglasPeuckerAlgorithm.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/alg/DouglasPeuckerAlgorithm.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/alg/DouglasPeuckerAlgorithm.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/alg/DouglasPeuckerAlgorithm.java index 5033cfe17..d6d635b62 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/alg/DouglasPeuckerAlgorithm.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/alg/DouglasPeuckerAlgorithm.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line.alg; +package com.djrapitops.plan.delivery.rendering.json.graphs.line.alg; -import com.djrapitops.plan.utilities.html.graphs.line.Line; -import com.djrapitops.plan.utilities.html.graphs.line.Point; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.Line; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point; import java.util.ArrayList; import java.util.Arrays; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/alg/ReduceGapTriangles.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/alg/ReduceGapTriangles.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/alg/ReduceGapTriangles.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/alg/ReduceGapTriangles.java index 66fd138ab..063f2882d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/line/alg/ReduceGapTriangles.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/line/alg/ReduceGapTriangles.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.line.alg; +package com.djrapitops.plan.delivery.rendering.json.graphs.line.alg; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point; import com.djrapitops.plan.utilities.comparators.PointComparator; -import com.djrapitops.plan.utilities.html.graphs.line.Point; import com.djrapitops.plugin.utilities.Verify; import java.util.HashSet; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/ActivityPie.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/ActivityPie.java similarity index 58% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/ActivityPie.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/ActivityPie.java index 23ffbb5b3..de0000e4e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/ActivityPie.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/ActivityPie.java @@ -14,11 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.pie; +package com.djrapitops.plan.delivery.rendering.json.graphs.pie; -import com.djrapitops.plan.data.store.mutators.ActivityIndex; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; /** * Pie about different Activity Groups defined by ActivityIndex. @@ -28,23 +30,22 @@ import java.util.*; */ public class ActivityPie extends Pie { - ActivityPie(Map> activityData, String[] colors) { - super(turnToSlices(activityData, colors)); + ActivityPie(Map activityData, String[] colors, String[] groups) { + super(turnToSlices(activityData, colors, groups)); } - private static List turnToSlices(Map> activityData, String[] colors) { + private static List turnToSlices(Map activityData, String[] colors, String[] groups) { int maxCol = colors.length; + String[] defaultGroups = ActivityIndex.getDefaultGroups(); List slices = new ArrayList<>(); int i = 0; - for (String group : ActivityIndex.getGroups()) { - Set players = activityData.getOrDefault(group, new HashSet<>()); - int num = players.size(); + for (int j = 0; j < groups.length; j++) { + int num = activityData.getOrDefault(defaultGroups[j], 0); - slices.add(new PieSlice(group, num, colors[i % maxCol], false)); + slices.add(new PieSlice(groups[j], num, colors[i % maxCol], false)); i++; } - return slices; } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/Pie.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/Pie.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/Pie.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/Pie.java index d9f739081..980be5d8c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/Pie.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/Pie.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.pie; +package com.djrapitops.plan.delivery.rendering.json.graphs.pie; -import com.djrapitops.plan.utilities.html.graphs.HighChart; +import com.djrapitops.plan.delivery.rendering.json.graphs.HighChart; import org.apache.commons.text.TextStringBuilder; import java.util.List; @@ -40,4 +40,8 @@ public class Pie implements HighChart { series.appendWithSeparators(slices, ","); return series.append("]").toString(); } + + public List getSlices() { + return slices; + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieGraphFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieGraphFactory.java similarity index 61% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieGraphFactory.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieGraphFactory.java index e9b340c72..8b9aa4049 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieGraphFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieGraphFactory.java @@ -14,20 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.pie; +package com.djrapitops.plan.delivery.rendering.json.graphs.pie; -import com.djrapitops.plan.data.time.GMTimes; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.config.WorldAliasSettings; -import com.djrapitops.plan.system.settings.paths.DisplaySettings; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.settings.theme.ThemeVal; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.gathering.domain.GMTimes; +import com.djrapitops.plan.gathering.domain.WorldTimes; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.WorldAliasSettings; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.GenericLang; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; import javax.inject.Inject; import javax.inject.Singleton; import java.util.Map; -import java.util.Set; import java.util.UUID; /** @@ -39,31 +41,38 @@ import java.util.UUID; public class PieGraphFactory { private final PlanConfig config; + private final Locale locale; private final Theme theme; @Inject public PieGraphFactory( PlanConfig config, + Locale locale, Theme theme ) { this.config = config; + this.locale = locale; this.theme = theme; } - public Pie activityPie(Map> activityData) { - String[] colors = theme.getValue(ThemeVal.GRAPH_ACTIVITY_PIE).split(", "); - return new ActivityPie(activityData, colors); + public Pie activityPie(Map activityData) { + String[] colors = theme.getPieColors(ThemeVal.GRAPH_ACTIVITY_PIE); + return new ActivityPie(activityData, colors, ActivityIndex.getGroups(locale)); } public Pie serverPreferencePie(Map serverNames, Map serverWorldTimes) { - return new ServerPreferencePie(serverNames, serverWorldTimes); + return new ServerPreferencePie(serverNames, serverWorldTimes, locale.get(GenericLang.UNKNOWN).toString()); + } + + public Pie serverPreferencePie(Map serverPlaytimes) { + return new ServerPreferencePie(serverPlaytimes); } public WorldPie worldPie(WorldTimes worldTimes) { WorldAliasSettings worldAliasSettings = config.getWorldAliasSettings(); Map playtimePerAlias = worldAliasSettings.getPlaytimePerAlias(worldTimes); Map gmTimesPerAlias = worldAliasSettings.getGMTimesPerAlias(worldTimes); - String[] colors = theme.getValue(ThemeVal.GRAPH_WORLD_PIE).split(", "); + String[] colors = theme.getPieColors(ThemeVal.GRAPH_WORLD_PIE); boolean orderByPercentage = config.isTrue(DisplaySettings.ORDER_WORLD_PIE_BY_PERC); return new WorldPie(playtimePerAlias, gmTimesPerAlias, colors, orderByPercentage); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieSlice.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieSlice.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieSlice.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieSlice.java index f39ec23c0..a35823799 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieSlice.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieSlice.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.pie; +package com.djrapitops.plan.delivery.rendering.json.graphs.pie; /** * Represents a slice of a pie. @@ -25,7 +25,7 @@ public class PieSlice { private final String name; private final long y; private final String color; - private final boolean drilldown; + private final String drilldown; public PieSlice(String name, long y) { this(name, y, null, false); @@ -43,7 +43,7 @@ public class PieSlice { this.name = name; this.y = y; this.color = color; - this.drilldown = drilldown; + this.drilldown = drilldown ? name : null; } @Override @@ -51,7 +51,7 @@ public class PieSlice { return "{name:'" + name + "'," + "y:" + y + (color != null ? "," + "color:" + color : "") - + (drilldown ? "," + "drilldown: '" + name + "'" : "") + + (drilldown != null ? "," + "drilldown: '" + drilldown + "'" : "") + "}"; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieWithDrilldown.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieWithDrilldown.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieWithDrilldown.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieWithDrilldown.java index c6674c496..27186c5f2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/PieWithDrilldown.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/PieWithDrilldown.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.pie; +package com.djrapitops.plan.delivery.rendering.json.graphs.pie; import java.util.List; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/ServerPreferencePie.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/ServerPreferencePie.java similarity index 62% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/ServerPreferencePie.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/ServerPreferencePie.java index e16a770c0..7e2811dce 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/ServerPreferencePie.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/ServerPreferencePie.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.pie; +package com.djrapitops.plan.delivery.rendering.json.graphs.pie; -import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.gathering.domain.WorldTimes; import java.util.ArrayList; import java.util.List; @@ -25,18 +25,22 @@ import java.util.UUID; public class ServerPreferencePie extends Pie { - ServerPreferencePie(Map serverNames, Map serverWorldTimes) { - super(turnToSlices(serverNames, serverWorldTimes)); + ServerPreferencePie(Map serverNames, Map serverWorldTimes, String unknown) { + super(turnToSlices(serverNames, serverWorldTimes, unknown)); } - private static List turnToSlices(Map serverNames, Map serverWorldTimes) { + ServerPreferencePie(Map serverPlaytimes) { + super(turnToSlices(serverPlaytimes)); + } + + private static List turnToSlices(Map serverNames, Map serverWorldTimes, String unknown) { List slices = new ArrayList<>(); for (Map.Entry server : serverWorldTimes.entrySet()) { UUID serverUUID = server.getKey(); WorldTimes worldTimes = server.getValue(); - String serverName = serverNames.getOrDefault(serverUUID, "Unknown"); + String serverName = serverNames.getOrDefault(serverUUID, unknown); long num = worldTimes.getTotal(); slices.add(new PieSlice(serverName, num)); @@ -44,4 +48,16 @@ public class ServerPreferencePie extends Pie { return slices; } + + private static List turnToSlices(Map serverPlaytimes) { + List slices = new ArrayList<>(); + + for (Map.Entry server : serverPlaytimes.entrySet()) { + String serverName = server.getKey(); + long playtime = server.getValue(); + slices.add(new PieSlice(serverName, playtime)); + } + + return slices; + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/WorldPie.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/WorldPie.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/WorldPie.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/WorldPie.java index 4dd2d75d9..8050bc04a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/pie/WorldPie.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/pie/WorldPie.java @@ -14,15 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.pie; +package com.djrapitops.plan.delivery.rendering.json.graphs.pie; -import com.djrapitops.plan.data.time.GMTimes; +import com.djrapitops.plan.gathering.domain.GMTimes; import com.djrapitops.plan.utilities.comparators.PieSliceComparator; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; public class WorldPie extends PieWithDrilldown { @@ -62,6 +59,27 @@ public class WorldPie extends PieWithDrilldown { return slices; } + public List> toHighChartsDrillDownMaps() { + List> drilldowns = new ArrayList<>(); + for (Map.Entry worldAlias : gmTimesAliasMap.entrySet()) { + Map drilldown = new HashMap<>(); + drilldown.put("name", worldAlias.getKey()); + drilldown.put("id", worldAlias.getKey()); + drilldown.put("data", parseGMTimesForWorld(worldAlias.getValue())); + drilldowns.add(drilldown); + } + return drilldowns; + } + + private List parseGMTimesForWorld(GMTimes gmTimes) { + List data = new ArrayList<>(); + for (Map.Entry gmEntry : gmTimes.getTimes().entrySet()) { + List gmList = Arrays.asList(gmEntry.getKey(), gmEntry.getValue()); + data.add(gmList); + } + return data; + } + @Override public String toHighChartsDrilldown() { StringBuilder drilldownBuilder = new StringBuilder(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/PunchCard.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/PunchCard.java similarity index 56% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/PunchCard.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/PunchCard.java index 3cf9fca55..12e6fb905 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/PunchCard.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/PunchCard.java @@ -14,17 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.special; +package com.djrapitops.plan.delivery.rendering.json.graphs.special; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.utilities.html.graphs.HighChart; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.rendering.json.graphs.HighChart; +import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; /** * Bubble Chart that represents login "punches" of players. @@ -33,14 +31,14 @@ import java.util.stream.Collectors; */ public class PunchCard implements HighChart { - private final Collection sessions; + private final SessionsMutator sessions; /** * Constructor for the graph. * * @param sessions All sessions of All users this PunchCard represents. */ - PunchCard(Collection sessions) { + PunchCard(SessionsMutator sessions) { this.sessions = sessions; } @@ -48,7 +46,7 @@ public class PunchCard implements HighChart { * First number signifies the Day of Week. (0 = Monday, 6 = Sunday) * Second number signifies the Hour of Day. (0 = 0 AM, 23 = 11 PM) */ - private List getDaysAndHours(Collection sessionStarts) { + private int[][] getDaysAndHours(Collection sessionStarts) { return sessionStarts.stream().map((Long start) -> { Calendar day = Calendar.getInstance(); day.setTimeInMillis(start); @@ -65,57 +63,48 @@ public class PunchCard implements HighChart { dayOfWeek = 6; } return new int[]{dayOfWeek, hourOfDay}; - }).collect(Collectors.toList()); + }).toArray(int[][]::new); } - private int[][] turnIntoArray(Collection sessionStarts) { - List daysAndHours = getDaysAndHours(sessionStarts); - int[][] dataArray = createEmptyArray(); - for (int[] dAndH : daysAndHours) { - int d = dAndH[0]; - int h = dAndH[1]; - dataArray[d][h] = dataArray[d][h] + 1; + private int[][] turnIntoMatrix(Collection sessionStarts) { + int[][] daysAndHours = getDaysAndHours(sessionStarts); + int[][] matrix = createZeroMatrix(); + for (int[] dayAndHour : daysAndHours) { + int day = dayAndHour[0]; + int hour = dayAndHour[1]; + matrix[day][hour] = matrix[day][hour] + 1; } - return dataArray; + return matrix; } @Override public String toHighChartsSeries() { - List sessionStarts = getSessionStarts(sessions); - int[][] dataArray = turnIntoArray(sessionStarts); - int big = findBiggestValue(dataArray); - int[][] scaled = scale(dataArray, big); - StringBuilder arrayBuilder = new StringBuilder("["); - for (int i = 0; i < 7; i++) { - for (int j = 0; j < 24; j++) { - int value = scaled[i][j]; - if (j == 0) { - arrayBuilder.append("{x:").append(24 * 3600000); - } else { - arrayBuilder.append("{x:").append(j * 3600000); - } - arrayBuilder.append(", y:").append(i) - .append(", z:").append(value). - append(", marker: { radius:").append(value) - .append("}}"); - if (i != 6 || j != 23) { - arrayBuilder.append(","); - } + return getDots().toString(); + } + + public List getDots() { + List dots = new ArrayList<>(); + + List sessionStarts = sessions.toSessionStarts(); + + int[][] dayHourMatrix = turnIntoMatrix(sessionStarts); + int big = findBiggestValue(dayHourMatrix); + int[][] scaled = scale(dayHourMatrix, big); + + for (int day = 0; day < 7; day++) { + for (int hour = 0; hour < 24; hour++) { + int value = scaled[day][hour]; + + int x = hour == 0 ? 24 * 3600000 : hour * 3600000; + + dots.add(new Dot(x, day, value, value)); } } - arrayBuilder.append("]"); - return arrayBuilder.toString(); + + return dots; } - private List getSessionStarts(Collection data) { - return data.stream() - .filter(Objects::nonNull) - .map(session -> session.getUnsafe(SessionKeys.START)) - .sorted() - .collect(Collectors.toList()); - } - - private int[][] createEmptyArray() { + private int[][] createZeroMatrix() { int[][] dataArray = new int[7][24]; for (int i = 0; i < 7; i++) { for (int j = 0; j < 24; j++) { @@ -151,4 +140,43 @@ public class PunchCard implements HighChart { } return scaled; } + + public static class Dot { + int x; + int y; + int z; + Marker marker; + + public Dot(int x, int y, int z, int radius) { + this.x = x; + this.y = y; + this.z = z; + this.marker = new Marker(radius); + } + + @Override + public String toString() { + return "{" + + "x:" + x + + ", y:" + y + + ", z:" + z + + ", marker:" + marker + + '}'; + } + + public static class Marker { + int radius; + + Marker(int radius) { + this.radius = radius; + } + + @Override + public String toString() { + return "{" + + "radius:" + radius + + '}'; + } + } + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/SpecialGraphFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/SpecialGraphFactory.java similarity index 67% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/SpecialGraphFactory.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/SpecialGraphFactory.java index f84c33eaf..5c12524af 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/SpecialGraphFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/SpecialGraphFactory.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.special; +package com.djrapitops.plan.delivery.rendering.json.graphs.special; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.utilities.html.graphs.HighChart; +import com.djrapitops.plan.delivery.domain.mutators.PlayersMutator; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.delivery.rendering.json.graphs.HighChart; +import com.djrapitops.plan.gathering.domain.Session; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.Collection; +import java.util.List; import java.util.Map; /** @@ -38,11 +39,15 @@ public class SpecialGraphFactory { // Inject Constructor. } - public HighChart punchCard(Collection sessions) { + public PunchCard punchCard(List sessions) { + return punchCard(new SessionsMutator(sessions)); + } + + public PunchCard punchCard(SessionsMutator sessions) { return new PunchCard(sessions); } - public HighChart worldMap(Map geolocationCounts) { + public WorldMap worldMap(Map geolocationCounts) { return new WorldMap(geolocationCounts); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/WorldMap.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/WorldMap.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/WorldMap.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/WorldMap.java index dcfff65e2..4359bb120 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/special/WorldMap.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/special/WorldMap.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.special; +package com.djrapitops.plan.delivery.rendering.json.graphs.special; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.utilities.html.graphs.HighChart; +import com.djrapitops.plan.delivery.domain.mutators.PlayersMutator; +import com.djrapitops.plan.delivery.rendering.json.graphs.HighChart; import org.apache.commons.text.TextStringBuilder; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * World Map that uses iso-a3 specification of Country codes. @@ -99,4 +100,29 @@ public class WorldMap implements HighChart { return dataBuilder.append("]").toString(); } + + public List getEntries() { + return geoCodeCounts.entrySet().stream() + .filter(entry -> entry.getValue() != 0) + .map(e -> new Entry(e.getKey(), e.getValue())) + .collect(Collectors.toList()); + } + + public static class Entry { + private String code; + private int value; + + public Entry(String code, int value) { + this.code = code; + this.value = value; + } + + public String getCode() { + return code; + } + + public int getValue() { + return value; + } + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/ActivityStackGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/ActivityStackGraph.java similarity index 60% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/ActivityStackGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/ActivityStackGraph.java index db1596e00..50af0994a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/ActivityStackGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/ActivityStackGraph.java @@ -14,12 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.stack; +package com.djrapitops.plan.delivery.rendering.json.graphs.stack; -import com.djrapitops.plan.data.store.mutators.ActivityIndex; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.domain.DateMap; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.delivery.formatting.Formatter; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; /** * Stack Graph that represents evolution of the PlayerBase in terms of ActivityIndex Groups. @@ -29,8 +32,8 @@ import java.util.*; */ class ActivityStackGraph extends StackGraph { - ActivityStackGraph(TreeMap>> activityData, String[] colors, Formatter dayFormatter) { - super(getLabels(activityData.navigableKeySet(), dayFormatter), getDataSets(activityData, colors)); + ActivityStackGraph(DateMap> activityData, String[] colors, Formatter dayFormatter, String[] groups) { + super(getLabels(activityData.navigableKeySet(), dayFormatter), getDataSets(activityData, colors, groups)); } private static String[] getLabels(Collection dates, Formatter dayFormatter) { @@ -39,8 +42,7 @@ class ActivityStackGraph extends StackGraph { .toArray(String[]::new); } - private static StackDataSet[] getDataSets(TreeMap>> activityData, String[] colors) { - String[] groups = ActivityIndex.getGroups(); + private static StackDataSet[] initializeDataSet(String[] groups, String[] colors) { int maxCol = colors.length; StackDataSet[] dataSets = new StackDataSet[groups.length]; @@ -48,12 +50,16 @@ class ActivityStackGraph extends StackGraph { dataSets[i] = new StackDataSet(new ArrayList<>(), groups[i], colors[i % maxCol]); } - for (Long date : activityData.navigableKeySet()) { - Map> data = activityData.get(date); + return dataSets; + } + private static StackDataSet[] getDataSets(DateMap> activityData, String[] colors, String[] groups) { + StackDataSet[] dataSets = initializeDataSet(groups, colors); + String[] defaultGroups = ActivityIndex.getDefaultGroups(); + + for (Map data : activityData.values()) { for (int j = 0; j < groups.length; j++) { - Set players = data.get(groups[j]); - dataSets[j].add((double) (players != null ? players.size() : 0)); + dataSets[j].add((double) data.getOrDefault(defaultGroups[j], 0)); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackDataSet.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackDataSet.java similarity index 57% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackDataSet.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackDataSet.java index d78a07408..67b621bb5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackDataSet.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackDataSet.java @@ -14,11 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.stack; +package com.djrapitops.plan.delivery.rendering.json.graphs.stack; -import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * Represents a value set for a Stack graph. @@ -29,13 +27,14 @@ import java.util.Objects; * * @author Rsl1122 */ -public class StackDataSet extends ArrayList { +public class StackDataSet { + private final List data; private final String name; private final String color; - public StackDataSet(List values, String name, String color) { - super(values); + public StackDataSet(List data, String name, String color) { + this.data = data; this.name = name; this.color = color; } @@ -48,18 +47,31 @@ public class StackDataSet extends ArrayList { return color; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof StackDataSet)) return false; - if (!super.equals(o)) return false; - StackDataSet doubles = (StackDataSet) o; - return Objects.equals(name, doubles.name) && - Objects.equals(color, doubles.color); + public List getData() { + return data; } - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), name, color); + public void add(double value) { + data.add(value); + } + + public String toSeriesObjectString() { + StringBuilder dataSetBuilder = new StringBuilder("{name: '"); + + dataSetBuilder.append(getName()).append("',") + .append("color:").append(getColor()) + .append(",data: ["); + + int size = data.size(); + int i = 0; + for (Double value : data) { + dataSetBuilder.append(value); + if (i < size - 1) { + dataSetBuilder.append(","); + } + i++; + } + + return dataSetBuilder.append("]}").toString(); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraph.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackGraph.java similarity index 71% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraph.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackGraph.java index 30954b31a..78382d123 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraph.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackGraph.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.stack; +package com.djrapitops.plan.delivery.rendering.json.graphs.stack; -import com.djrapitops.plan.utilities.html.graphs.HighChart; +import com.djrapitops.plan.delivery.rendering.json.graphs.HighChart; /** * Utility for creating HighCharts Stack graphs. @@ -33,6 +33,10 @@ public class StackGraph implements HighChart { this.labels = labels; } + public String[] getLabels() { + return labels; + } + public String toHighChartsLabels() { StringBuilder labelBuilder = new StringBuilder("["); @@ -50,24 +54,8 @@ public class StackGraph implements HighChart { return labelBuilder.append("]").toString(); } - private String toSeries(StackDataSet dataSet) { - StringBuilder dataSetBuilder = new StringBuilder("{name: '"); - - dataSetBuilder.append(dataSet.getName()).append("',") - .append("color:").append(dataSet.getColor()) - .append(",data: ["); - - int size = dataSet.size(); - int i = 0; - for (Double value : dataSet) { - dataSetBuilder.append(value); - if (i < size - 1) { - dataSetBuilder.append(","); - } - i++; - } - - return dataSetBuilder.append("]}").toString(); + public StackDataSet[] getDataSets() { + return dataSets; } @Override @@ -77,7 +65,7 @@ public class StackGraph implements HighChart { int size = dataSets.length; int i = 0; for (StackDataSet dataSet : dataSets) { - seriesBuilder.append(toSeries(dataSet)); + seriesBuilder.append(dataSet.toSeriesObjectString()); if (i < size - 1) { seriesBuilder.append(","); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraphFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackGraphFactory.java similarity index 63% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraphFactory.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackGraphFactory.java index 83f35cf3b..ecf947c69 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/graphs/stack/StackGraphFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/graphs/stack/StackGraphFactory.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.graphs.stack; +package com.djrapitops.plan.delivery.rendering.json.graphs.stack; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.settings.theme.ThemeVal; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; +import com.djrapitops.plan.delivery.domain.DateMap; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; import javax.inject.Inject; import javax.inject.Singleton; import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.UUID; /** * Factory class for different objects representing HTML stack graphs. @@ -36,20 +36,23 @@ import java.util.UUID; @Singleton public class StackGraphFactory { + private final Locale locale; private final Theme theme; private final Formatter dayFormatter; @Inject public StackGraphFactory( + Locale locale, Formatters formatters, Theme theme ) { + this.locale = locale; this.theme = theme; this.dayFormatter = formatters.dayLong(); } - public StackGraph activityStackGraph(TreeMap>> activityData) { - String[] colors = theme.getValue(ThemeVal.GRAPH_ACTIVITY_PIE).split(", "); - return new ActivityStackGraph(activityData, colors, dayFormatter); + public StackGraph activityStackGraph(DateMap> activityData) { + String[] colors = theme.getPieColors(ThemeVal.GRAPH_ACTIVITY_PIE); + return new ActivityStackGraph(activityData, colors, dayFormatter, ActivityIndex.getGroups(locale)); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkOverviewJSONParser.java new file mode 100644 index 000000000..332b039b0 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkOverviewJSONParser.java @@ -0,0 +1,192 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json.network; + +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.json.Trend; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries; +import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; +import com.djrapitops.plan.storage.database.queries.objects.TPSQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /network-page Network Overview tab. + * + * @author Rsl1122 + */ +@Singleton +public class NetworkOverviewJSONParser implements NetworkTabJSONParser> { + + private final Formatter day; + private PlanConfig config; + private DBSystem dbSystem; + private ServerInfo serverInfo; + private Formatter timeAmount; + private Formatter year; + + @Inject + public NetworkOverviewJSONParser( + PlanConfig config, + DBSystem dbSystem, + ServerInfo serverInfo, + Formatters formatters + ) { + this.config = config; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + + year = formatters.year(); + day = formatters.dayLong(); + timeAmount = formatters.timeAmount(); + } + + public Map createJSONAsMap() { + Map serverOverview = new HashMap<>(); + serverOverview.put("players", createPlayersMap()); + serverOverview.put("numbers", createNumbersMap()); + serverOverview.put("weeks", createWeeksMap()); + return serverOverview; + } + + private Map createPlayersMap() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long dayAgo = now - TimeUnit.DAYS.toMillis(1L); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + Map sevenDays = new HashMap<>(); + + sevenDays.put("unique_players_1d", db.query(PlayerCountQueries.uniquePlayerCount(dayAgo, now))); + sevenDays.put("unique_players_7d", db.query(PlayerCountQueries.uniquePlayerCount(weekAgo, now))); + sevenDays.put("unique_players_30d", db.query(PlayerCountQueries.uniquePlayerCount(monthAgo, now))); + + sevenDays.put("new_players_1d", db.query(PlayerCountQueries.newPlayerCount(dayAgo, now))); + sevenDays.put("new_players_7d", db.query(PlayerCountQueries.newPlayerCount(weekAgo, now))); + sevenDays.put("new_players_30d", db.query(PlayerCountQueries.newPlayerCount(monthAgo, now))); + + return sevenDays; + } + + private Map createNumbersMap() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long twoDaysAgo = now - TimeUnit.DAYS.toMillis(2L); + Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map numbers = new HashMap<>(); + + Integer userCount = db.query(PlayerCountQueries.newPlayerCount(0L, now)); + numbers.put("total_players", userCount); + numbers.put("regular_players", db.query(NetworkActivityIndexQueries.fetchRegularPlayerCount(now, playtimeThreshold))); + numbers.put("online_players", getOnlinePlayers()); + UUID serverUUID = serverInfo.getServerUUID(); + Optional> lastPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo)); + Optional> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID)); + numbers.put("last_peak_date", lastPeak.map(year).orElse("-")); + numbers.put("last_peak_players", lastPeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); + numbers.put("best_peak_date", allTimePeak.map(year).orElse("-")); + numbers.put("best_peak_players", allTimePeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); + Long totalPlaytime = db.query(SessionQueries.playtime(0L, now)); + numbers.put("playtime", timeAmount.apply(totalPlaytime)); + numbers.put("player_playtime", userCount != 0 ? timeAmount.apply(totalPlaytime / userCount) : "-"); + Long sessionCount = db.query(SessionQueries.sessionCount(0L, now)); + numbers.put("sessions", sessionCount); + numbers.put("session_length_avg", sessionCount != 0 ? timeAmount.apply(totalPlaytime / sessionCount) : "-"); + + return numbers; + } + + private Object getOnlinePlayers() { + return serverInfo.getServerProperties().getOnlinePlayers(); + } + + private Map createWeeksMap() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long oneWeekAgo = now - TimeUnit.DAYS.toMillis(7L); + long twoWeeksAgo = now - TimeUnit.DAYS.toMillis(14L); + Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map weeks = new HashMap<>(); + + weeks.put("start", day.apply(twoWeeksAgo)); + weeks.put("midpoint", day.apply(oneWeekAgo)); + weeks.put("end", day.apply(now)); + + Integer uniqueBefore = db.query(PlayerCountQueries.uniquePlayerCount(twoWeeksAgo, oneWeekAgo)); + Integer uniqueAfter = db.query(PlayerCountQueries.uniquePlayerCount(oneWeekAgo, now)); + Trend uniqueTrend = new Trend(uniqueBefore, uniqueAfter, false); + weeks.put("unique_before", uniqueBefore); + weeks.put("unique_after", uniqueAfter); + weeks.put("unique_trend", uniqueTrend); + + Integer newBefore = db.query(PlayerCountQueries.newPlayerCount(twoWeeksAgo, oneWeekAgo)); + Integer newAfter = db.query(PlayerCountQueries.newPlayerCount(oneWeekAgo, now)); + Trend newTrend = new Trend(newBefore, newAfter, false); + weeks.put("new_before", newBefore); + weeks.put("new_after", newAfter); + weeks.put("new_trend", newTrend); + + int regularBefore = db.query(NetworkActivityIndexQueries.fetchRegularPlayerCount(oneWeekAgo, playtimeThreshold)); + int regularAfter = db.query(NetworkActivityIndexQueries.fetchRegularPlayerCount(now, playtimeThreshold)); + weeks.put("regular_before", regularBefore); + weeks.put("regular_after", regularAfter); + weeks.put("regular_trend", new Trend(regularBefore, regularAfter, false)); + + Long playtimeBefore = db.query(SessionQueries.playtime(twoWeeksAgo, oneWeekAgo)); + Long playtimeAfter = db.query(SessionQueries.playtime(oneWeekAgo, now)); + long avgPlaytimeBefore = uniqueBefore != 0 ? playtimeBefore / uniqueBefore : 0L; + long avgPlaytimeAfter = uniqueAfter != 0 ? playtimeAfter / uniqueAfter : 0L; + Trend avgPlaytimeTrend = new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false, timeAmount); + weeks.put("average_playtime_before", timeAmount.apply(avgPlaytimeBefore)); + weeks.put("average_playtime_after", timeAmount.apply(avgPlaytimeAfter)); + weeks.put("average_playtime_trend", avgPlaytimeTrend); + + Long sessionsBefore = db.query(SessionQueries.sessionCount(twoWeeksAgo, oneWeekAgo)); + Long sessionsAfter = db.query(SessionQueries.sessionCount(oneWeekAgo, now)); + Trend sessionsTrend = new Trend(sessionsBefore, sessionsAfter, false); + weeks.put("sessions_before", sessionsBefore); + weeks.put("sessions_after", sessionsAfter); + weeks.put("sessions_trend", sessionsTrend); + + long avgSessionLengthBefore = sessionsBefore != 0 ? playtimeBefore / sessionsBefore : 0; + long avgSessionLengthAfter = sessionsAfter != 0 ? playtimeAfter / sessionsAfter : 0; + Trend avgSessionLengthTrend = new Trend(avgSessionLengthBefore, avgSessionLengthAfter, false, timeAmount); + weeks.put("session_length_average_before", timeAmount.apply(avgSessionLengthBefore)); + weeks.put("session_length_average_after", timeAmount.apply(avgSessionLengthAfter)); + weeks.put("session_length_average_trend", avgSessionLengthTrend); + + return weeks; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkPlayerBaseOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkPlayerBaseOverviewJSONParser.java new file mode 100644 index 000000000..c40e7864d --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkPlayerBaseOverviewJSONParser.java @@ -0,0 +1,151 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json.network; + +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.json.Trend; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries; +import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /network-page Playerbase Overview tab. + * + * @author Rsl1122 + */ +@Singleton +public class NetworkPlayerBaseOverviewJSONParser implements NetworkTabJSONParser> { + + private PlanConfig config; + private DBSystem dbSystem; + + private Formatter timeAmount; + private Formatter percentage; + + @Inject + public NetworkPlayerBaseOverviewJSONParser( + PlanConfig config, + DBSystem dbSystem, + Formatters formatters + ) { + this.config = config; + this.dbSystem = dbSystem; + + timeAmount = formatters.timeAmount(); + percentage = formatters.percentage(); + } + + public Map createJSONAsMap() { + Map serverOverview = new HashMap<>(); + serverOverview.put("trends", createTrendsMap()); + serverOverview.put("insights", createInsightsMap()); + return serverOverview; + } + + private Map createTrendsMap() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + long twoMonthsAgo = now - TimeUnit.DAYS.toMillis(60L); + Long playThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map trends = new HashMap<>(); + + Integer playersBefore = db.query(PlayerCountQueries.newPlayerCount(0L, monthAgo)); + Integer playersAfter = db.query(PlayerCountQueries.newPlayerCount(0L, now)); + trends.put("total_players_then", playersBefore); + trends.put("total_players_now", playersAfter); + trends.put("total_players_trend", new Trend(playersBefore, playersAfter, false)); + + Integer regularBefore = db.query(NetworkActivityIndexQueries.fetchRegularPlayerCount(monthAgo, playThreshold)); + Integer regularAfter = db.query(NetworkActivityIndexQueries.fetchRegularPlayerCount(now, playThreshold)); + trends.put("regular_players_then", regularBefore); + trends.put("regular_players_now", regularAfter); + trends.put("regular_players_trend", new Trend(regularBefore, regularAfter, false)); + + Long avgPlaytimeBefore = db.query(SessionQueries.averagePlaytimePerPlayer(twoMonthsAgo, monthAgo)); + Long avgPlaytimeAfter = db.query(SessionQueries.averagePlaytimePerPlayer(monthAgo, now)); + trends.put("playtime_avg_then", timeAmount.apply(avgPlaytimeBefore)); + trends.put("playtime_avg_now", timeAmount.apply(avgPlaytimeAfter)); + trends.put("playtime_avg_trend", new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false, timeAmount)); + + Long avgAfkBefore = db.query(SessionQueries.averageAfkPerPlayer(twoMonthsAgo, monthAgo)); + Long avgAfkAfter = db.query(SessionQueries.averageAfkPerPlayer(monthAgo, now)); + double afkPercBefore = avgPlaytimeBefore != 0 ? (double) avgAfkBefore / avgPlaytimeBefore : 0; + double afkPercAfter = avgPlaytimeAfter != 0 ? (double) avgAfkAfter / avgPlaytimeAfter : 0; + trends.put("afk_then", percentage.apply(afkPercBefore)); + trends.put("afk_now", percentage.apply(afkPercAfter)); + trends.put("afk_trend", new Trend(afkPercBefore, afkPercAfter, Trend.REVERSED, percentage)); + + Long avgRegularPlaytimeBefore = db.query(NetworkActivityIndexQueries.averagePlaytimePerRegularPlayer(twoMonthsAgo, monthAgo, playThreshold)); + Long avgRegularPlaytimeAfter = db.query(NetworkActivityIndexQueries.averagePlaytimePerRegularPlayer(monthAgo, now, playThreshold)); + trends.put("regular_playtime_avg_then", timeAmount.apply(avgRegularPlaytimeBefore)); + trends.put("regular_playtime_avg_now", timeAmount.apply(avgRegularPlaytimeAfter)); + trends.put("regular_playtime_avg_trend", new Trend(avgRegularPlaytimeBefore, avgRegularPlaytimeAfter, false, timeAmount)); + + Long avgRegularSessionLengthBefore = db.query(NetworkActivityIndexQueries.averageSessionLengthPerRegularPlayer(twoMonthsAgo, monthAgo, playThreshold)); + Long avgRegularSessionLengthAfter = db.query(NetworkActivityIndexQueries.averageSessionLengthPerRegularPlayer(monthAgo, now, playThreshold)); + trends.put("regular_session_avg_then", timeAmount.apply(avgRegularSessionLengthBefore)); + trends.put("regular_session_avg_now", timeAmount.apply(avgRegularSessionLengthAfter)); + trends.put("regular_session_avg_trend", new Trend(avgRegularSessionLengthBefore, avgRegularSessionLengthAfter, false, timeAmount)); + + Long avgRegularAfkBefore = db.query(NetworkActivityIndexQueries.averageAFKPerRegularPlayer(twoMonthsAgo, monthAgo, playThreshold)); + Long avgRegularAfkAfter = db.query(NetworkActivityIndexQueries.averageAFKPerRegularPlayer(monthAgo, now, playThreshold)); + double afkRegularPercBefore = avgRegularPlaytimeBefore != 0 ? (double) avgRegularAfkBefore / avgRegularPlaytimeBefore : 0; + double afkRegularPercAfter = avgRegularPlaytimeAfter != 0 ? (double) avgRegularAfkAfter / avgRegularPlaytimeAfter : 0; + trends.put("regular_afk_avg_then", percentage.apply(afkRegularPercBefore)); + trends.put("regular_afk_avg_now", percentage.apply(afkRegularPercAfter)); + trends.put("regular_afk_avg_trend", new Trend(afkRegularPercBefore, afkRegularPercAfter, Trend.REVERSED, percentage)); + + return trends; + } + + private Map createInsightsMap() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long halfMonthAgo = now - TimeUnit.DAYS.toMillis(15L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + Long playThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map insights = new HashMap<>(); + + int newToRegular = db.query(NetworkActivityIndexQueries.countNewPlayersTurnedRegular(monthAgo, now, playThreshold)); + Integer newToRegularBefore = db.query(NetworkActivityIndexQueries.countNewPlayersTurnedRegular(monthAgo, halfMonthAgo, playThreshold)); + Integer newToRegularAfter = db.query(NetworkActivityIndexQueries.countNewPlayersTurnedRegular(halfMonthAgo, now, playThreshold)); + insights.put("new_to_regular", newToRegular); + insights.put("new_to_regular_trend", new Trend(newToRegularBefore, newToRegularAfter, false)); + + Integer regularToInactive = db.query(NetworkActivityIndexQueries.countRegularPlayersTurnedInactive(monthAgo, now, playThreshold)); + Integer regularToInactiveBefore = db.query(NetworkActivityIndexQueries.countRegularPlayersTurnedInactive(monthAgo, halfMonthAgo, playThreshold)); + Integer regularToInactiveAfter = db.query(NetworkActivityIndexQueries.countRegularPlayersTurnedInactive(halfMonthAgo, now, playThreshold)); + insights.put("regular_to_inactive", regularToInactive); + insights.put("regular_to_inactive_trend", new Trend(regularToInactiveBefore, regularToInactiveAfter, Trend.REVERSED)); + + return insights; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkSessionsOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkSessionsOverviewJSONParser.java new file mode 100644 index 000000000..b7873016f --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkSessionsOverviewJSONParser.java @@ -0,0 +1,75 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.json.network; + +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /network-page Sessions tab. + * + * @author Rsl1122 + */ +@Singleton +public class NetworkSessionsOverviewJSONParser implements NetworkTabJSONParser> { + + private DBSystem dbSystem; + + private Formatter timeAmount; + private Formatter percentage; + + @Inject + public NetworkSessionsOverviewJSONParser( + DBSystem dbSystem, + Formatters formatters + ) { + this.dbSystem = dbSystem; + + timeAmount = formatters.timeAmount(); + percentage = formatters.percentage(); + } + + public Map createJSONAsMap() { + return Collections.singletonMap("insights", createInsightsMap()); + } + + private Map createInsightsMap() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + Map insights = new HashMap<>(); + + Long playtime = db.query(SessionQueries.playtime(monthAgo, now)); + Long afkTime = db.query(SessionQueries.afkTime(monthAgo, now)); + insights.put("total_playtime", timeAmount.apply(playtime)); + insights.put("afk_time", timeAmount.apply(afkTime)); + insights.put("afk_time_perc", playtime != 0 ? percentage.apply(1.0 * afkTime / playtime) : "-"); + + return insights; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/WideRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkTabJSONParser.java similarity index 70% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/request/WideRequest.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkTabJSONParser.java index f4d5781c4..0e5da2d87 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/WideRequest.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/network/NetworkTabJSONParser.java @@ -14,12 +14,21 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.request; +package com.djrapitops.plan.delivery.rendering.json.network; + +import java.util.function.Supplier; /** - * InfoRequest that should be relayed to all Bukkit servers. + * Interface for different tab JSON parsers. * * @author Rsl1122 */ -public interface WideRequest extends InfoRequest { +public interface NetworkTabJSONParser extends Supplier { + + T createJSONAsMap(); + + @Override + default T get() { + return createJSONAsMap(); + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/DebugPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/DebugPage.java similarity index 73% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/DebugPage.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/DebugPage.java index f17a95543..efb7463f0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/DebugPage.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/DebugPage.java @@ -14,26 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.pages; +package com.djrapitops.plan.delivery.rendering.pages; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.file.FileResource; -import com.djrapitops.plan.system.info.connection.ConnectionLog; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.HtmlStructure; -import com.djrapitops.plan.utilities.html.icon.Icon; -import com.djrapitops.plan.utilities.html.structure.TabsElement; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.html.Html; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plan.delivery.rendering.html.structure.TabsElement; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.identification.properties.ServerProperties; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.file.FileResource; +import com.djrapitops.plan.storage.file.ResourceCache; +import com.djrapitops.plan.version.VersionCheckSystem; import com.djrapitops.plugin.benchmarking.Benchmark; import com.djrapitops.plugin.benchmarking.Timings; import com.djrapitops.plugin.logging.FolderTimeStampFileLogger; @@ -57,31 +54,29 @@ public class DebugPage implements Page { private final Database database; private final ServerInfo serverInfo; - private final ConnectionSystem connectionSystem; + private final VersionCheckSystem versionCheckSystem; private final CombineDebugLogger debugLogger; private final Timings timings; private final ErrorHandler errorHandler; - private final Formatter secondFormatter; private final Formatter yearFormatter; DebugPage( Database database, ServerInfo serverInfo, - ConnectionSystem connectionSystem, Formatters formatters, + VersionCheckSystem versionCheckSystem, DebugLogger debugLogger, Timings timings, ErrorHandler errorHandler ) { this.database = database; this.serverInfo = serverInfo; - this.connectionSystem = connectionSystem; + this.versionCheckSystem = versionCheckSystem; this.debugLogger = (CombineDebugLogger) debugLogger; this.timings = timings; this.errorHandler = errorHandler; - this.secondFormatter = formatters.second(); this.yearFormatter = formatters.yearLong(); } @@ -93,7 +88,7 @@ public class DebugPage implements Page { String hastebinLink = Html.LINK_EXTERNAL.parse("https://hastebin.com/", "Create a new hastebin paste"); preContent.append("

    ") - .append(HtmlStructure.separateWithDots(issueLink, hastebinLink)).append("

    ") + .append(Html.separateWithDots(issueLink, hastebinLink)).append("

    ") .append("This page contains debug information for an issue ticket. You can copy it directly into the issue, the info is pre-formatted.") .append("

    "); @@ -109,19 +104,35 @@ public class DebugPage implements Page { private String createCacheContent() { StringBuilder content = new StringBuilder(); - appendResponseCache(content); + appendResourceCache(content); + appendJSONCache(content); appendSessionCache(content); return content.toString(); } - private void appendResponseCache(StringBuilder content) { + private void appendResourceCache(StringBuilder content) { try { - content.append("
    ### Cached Responses:

    "); - List cacheKeys = new ArrayList<>(ResponseCache.getCacheKeys()); + content.append("
    ### Cached Resources (from File or Jar):

    "); + List cacheKeys = ResourceCache.getCachedResourceNames(); + if (cacheKeys.isEmpty()) { + content.append("Empty"); + } + for (String cacheKey : cacheKeys) { + content.append("- ").append(cacheKey).append("
    "); + } + content.append("
    "); + } catch (Exception e) { + errorHandler.log(L.WARN, this.getClass(), e); + } + } + + private void appendJSONCache(StringBuilder content) { + try { + content.append("
    ### Cached JSON:

    "); + List cacheKeys = JSONCache.getCachedIDs(); if (cacheKeys.isEmpty()) { content.append("Empty"); } - Collections.sort(cacheKeys); for (String cacheKey : cacheKeys) { content.append("- ").append(cacheKey).append("
    "); } @@ -134,16 +145,17 @@ public class DebugPage implements Page { private void appendSessionCache(StringBuilder content) { try { content.append("
    ### Session Cache:

    "); - content.append("UUID | Session Started
    ") + content.append("Name | Session Started
    ") .append("-- | --
    "); Set> sessions = SessionCache.getActiveSessions().entrySet(); if (sessions.isEmpty()) { content.append("Empty"); } for (Map.Entry entry : sessions) { - UUID uuid = entry.getKey(); - String start = entry.getValue().getValue(SessionKeys.START).map(yearFormatter).orElse("Unknown"); - content.append(uuid.toString()).append(" | ").append(start).append("
    "); + Session session = entry.getValue(); + String name = session.getValue(SessionKeys.NAME).orElse(entry.getKey().toString()); + String start = session.getValue(SessionKeys.START).map(yearFormatter).orElse("Unknown"); + content.append(name).append(" | ").append(start).append("
    "); } content.append("
    "); } catch (Exception e) { @@ -167,59 +179,18 @@ public class DebugPage implements Page { StringBuilder content = new StringBuilder(); appendServerInformation(content); - appendConnectionLog(content); appendBenchmarks(content); return content.toString(); } - private void appendConnectionLog(StringBuilder content) { - try { - Map> logEntries = connectionSystem.getConnectionLog().getLogEntries(); - - content.append("
    ### Connection Log:

    "); - content.append("Server Address | Request Type | Response | Sent
    ") - .append("-- | -- | -- | --
    "); - - if (logEntries.isEmpty()) { - content.append("**No Connections Logged**
    "); - } - for (Map.Entry> entry : logEntries.entrySet()) { - String address = entry.getKey(); - Map requests = entry.getValue(); - for (Map.Entry requestEntry : requests.entrySet()) { - String infoRequest = requestEntry.getKey(); - ConnectionLog.Entry logEntry = requestEntry.getValue(); - - content.append(address).append(" | ") - .append(infoRequest).append(" | ") - .append(logEntry.getResponseCode()).append(" | ") - .append(secondFormatter.apply(logEntry)).append("
    "); - } - - } - content.append("
    "); - - content.append("
    ### Servers:

    "); - List servers = connectionSystem.getDataServers(); - content.append("Server Name | Address
    ") - .append("-- | --
    "); - for (Server server : servers) { - content.append(server.getName()).append(" | ") - .append(server.getWebAddress()).append("
    "); - } - content.append("
    "); - - } catch (Exception e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - private void appendServerInformation(StringBuilder content) { ServerProperties serverProperties = serverInfo.getServerProperties(); content.append("
    ### Server Information
    ") - .append("**Plan Version:** ${version}
    "); + .append("**Plan Version:** ") + .append(versionCheckSystem.getCurrentVersion()) + .append("
    "); content.append("**Server:** "); content.append(serverProperties.getName()) diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/NetworkPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/NetworkPage.java new file mode 100644 index 000000000..9bec5c014 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/NetworkPage.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.pages; + +import com.djrapitops.plan.delivery.domain.container.CachingSupplier; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; +import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerDataQuery; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.ProxySettings; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.UUID; + +/** + * Html String parser for /network page. + * + * @author Rsl1122 + */ +public class NetworkPage implements Page { + + private final DBSystem dbSystem; + + private final VersionCheckSystem versionCheckSystem; + private final PlanFiles files; + private final PlanConfig config; + private final Theme theme; + private final ServerInfo serverInfo; + private final Formatters formatters; + + NetworkPage( + DBSystem dbSystem, + VersionCheckSystem versionCheckSystem, + PlanFiles files, + PlanConfig config, + Theme theme, + ServerInfo serverInfo, + Formatters formatters + ) { + this.dbSystem = dbSystem; + this.versionCheckSystem = versionCheckSystem; + this.files = files; + this.config = config; + this.theme = theme; + this.serverInfo = serverInfo; + this.formatters = formatters; + } + + @Override + public String toHtml() throws ParseException { + try { + PlaceholderReplacer placeholders = new PlaceholderReplacer(); + + UUID serverUUID = serverInfo.getServerUUID(); + placeholders.put("networkDisplayName", config.get(ProxySettings.NETWORK_NAME)); + placeholders.put("serverUUID", serverUUID.toString()); + + placeholders.put("gmPieColors", theme.getValue(ThemeVal.GRAPH_GM_PIE)); + placeholders.put("playersGraphColor", theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE)); + placeholders.put("worldMapColLow", theme.getValue(ThemeVal.WORLD_MAP_LOW)); + placeholders.put("worldMapColHigh", theme.getValue(ThemeVal.WORLD_MAP_HIGH)); + placeholders.put("maxPingColor", theme.getValue(ThemeVal.GRAPH_MAX_PING)); + placeholders.put("minPingColor", theme.getValue(ThemeVal.GRAPH_MIN_PING)); + placeholders.put("avgPingColor", theme.getValue(ThemeVal.GRAPH_AVG_PING)); + placeholders.put("timeZone", config.getTimeZoneOffsetHours()); + + placeholders.put("version", versionCheckSystem.getUpdateButton().orElse(versionCheckSystem.getCurrentVersionButton())); + placeholders.put("updateModal", versionCheckSystem.getUpdateModal()); + + CachingSupplier pluginTabs = new CachingSupplier<>(() -> { + List extensionData = dbSystem.getDatabase().query(new ExtensionServerDataQuery(serverUUID)); + return new ServerPluginTabs(extensionData, formatters); + }); + + String nav = JSONCache.getOrCacheString(DataID.EXTENSION_NAV, serverUUID, () -> pluginTabs.get().getNav()); + String tabs = JSONCache.getOrCacheString(DataID.EXTENSION_TABS, serverUUID, () -> pluginTabs.get().getTabs()); + + placeholders.put("navPluginsTabs", nav); + placeholders.put("tabsPlugins", StringUtils.remove(tabs, "${backButton}")); + + return placeholders.apply(files.getCustomizableResourceOrDefault("web/network.html").asString()); + } catch (Exception e) { + throw new ParseException(e); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/Page.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/Page.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/Page.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/Page.java index 2a162f406..72cb6334d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/Page.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/Page.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.pages; +package com.djrapitops.plan.delivery.rendering.pages; -import com.djrapitops.plan.api.exceptions.ParseException; +import com.djrapitops.plan.exceptions.ParseException; /** * Interface for parsing page HTML. 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 new file mode 100644 index 000000000..90b8e12a5 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PageFactory.java @@ -0,0 +1,160 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.pages; + +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.exceptions.connection.NotFoundException; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; +import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionPlayerDataQuery; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; +import com.djrapitops.plugin.benchmarking.Timings; +import com.djrapitops.plugin.logging.debug.DebugLogger; +import com.djrapitops.plugin.logging.error.ErrorHandler; +import dagger.Lazy; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; + +/** + * Factory for creating different {@link Page} objects. + * + * @author Rsl1122 + */ +@Singleton +public class PageFactory { + + private final Lazy versionCheckSystem; + private final Lazy fileSystem; + private final Lazy config; + private final Lazy theme; + private final Lazy dbSystem; + private final Lazy serverInfo; + private final Lazy formatters; + private final Lazy debugLogger; + private final Lazy timings; + private final Lazy errorHandler; + + @Inject + public PageFactory( + Lazy versionCheckSystem, + Lazy fileSystem, + Lazy config, + Lazy theme, + Lazy dbSystem, + Lazy serverInfo, + Lazy formatters, + Lazy debugLogger, + Lazy timings, + Lazy errorHandler + ) { + this.versionCheckSystem = versionCheckSystem; + this.fileSystem = fileSystem; + this.config = config; + this.theme = theme; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + this.formatters = formatters; + this.debugLogger = debugLogger; + this.timings = timings; + this.errorHandler = errorHandler; + } + + public DebugPage debugPage() { + return new DebugPage( + dbSystem.get().getDatabase(), serverInfo.get(), formatters.get(), versionCheckSystem.get(), + debugLogger.get(), timings.get(), errorHandler.get() + ); + } + + public PlayersPage playersPage() { + return new PlayersPage(versionCheckSystem.get(), fileSystem.get(), config.get(), serverInfo.get()); + } + + public ServerPage serverPage(UUID serverUUID) throws NotFoundException { + return dbSystem.get().getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)) + .map(server -> new ServerPage( + server, + config.get(), + theme.get(), + versionCheckSystem.get(), + fileSystem.get(), + dbSystem.get(), + serverInfo.get(), + formatters.get() + )).orElseThrow(() -> new NotFoundException("Server not found in the database")); + } + + public PlayerPage playerPage(UUID playerUUID) { + Database db = dbSystem.get().getDatabase(); + PlayerContainer player = db.query(ContainerFetchQueries.fetchPlayerContainer(playerUUID)); + return new PlayerPage( + player, + versionCheckSystem.get(), + fileSystem.get(), config.get(), this, theme.get(), + formatters.get(), serverInfo.get() + ); + } + + public PlayerPluginTab inspectPluginTabs(UUID playerUUID) { + Database database = dbSystem.get().getDatabase(); + + Map> extensionPlayerData = database.query(new ExtensionPlayerDataQuery(playerUUID)); + + if (extensionPlayerData.isEmpty()) { + return new PlayerPluginTab("", Collections.emptyList(), formatters.get()); + } + + List playerPluginTabs = new ArrayList<>(); + for (Map.Entry entry : database.query(ServerQueries.fetchPlanServerInformation()).entrySet()) { + UUID serverUUID = entry.getKey(); + String serverName = entry.getValue().getIdentifiableName(); + + List ofServer = extensionPlayerData.get(serverUUID); + if (ofServer == null) { + continue; + } + + playerPluginTabs.add(new PlayerPluginTab(serverName, ofServer, formatters.get())); + } + + StringBuilder navs = new StringBuilder(); + StringBuilder tabs = new StringBuilder(); + + playerPluginTabs.stream().sorted().forEach(tab -> { + navs.append(tab.getNav()); + tabs.append(tab.getTab()); + }); + + return new PlayerPluginTab(navs.toString(), tabs.toString()); + } + + public NetworkPage networkPage() { + return new NetworkPage(dbSystem.get(), + versionCheckSystem.get(), fileSystem.get(), config.get(), theme.get(), serverInfo.get(), formatters.get()); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPage.java new file mode 100644 index 000000000..2db8db330 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPage.java @@ -0,0 +1,119 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.pages; + +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer; +import com.djrapitops.plan.delivery.rendering.html.Html; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; + +import java.io.IOException; +import java.util.UUID; + +/** + * Used for parsing Inspect page out of database data and the html. + * + * @author Rsl1122 + */ +public class PlayerPage implements Page { + + private final PlayerContainer player; + + private final VersionCheckSystem versionCheckSystem; + + private final PlanFiles files; + private final PlanConfig config; + private final PageFactory pageFactory; + private final Theme theme; + private final ServerInfo serverInfo; + + private final Formatter clockLongFormatter; + private final Formatter secondLongFormatter; + + PlayerPage( + PlayerContainer player, + VersionCheckSystem versionCheckSystem, + PlanFiles files, + PlanConfig config, + PageFactory pageFactory, + Theme theme, + Formatters formatters, + ServerInfo serverInfo + ) { + this.player = player; + this.versionCheckSystem = versionCheckSystem; + this.files = files; + this.config = config; + this.pageFactory = pageFactory; + this.theme = theme; + this.serverInfo = serverInfo; + + clockLongFormatter = formatters.clockLong(); + secondLongFormatter = formatters.secondLong(); + } + + @Override + public String toHtml() throws ParseException { + if (!player.getValue(PlayerKeys.REGISTERED).isPresent()) { + throw new IllegalStateException("Player is not registered"); + } + try { + return parse(player); + } catch (Exception e) { + throw new ParseException(e); + } + } + + public String parse(PlayerContainer player) throws IOException { + long now = System.currentTimeMillis(); + UUID playerUUID = player.getUnsafe(PlayerKeys.UUID); + + PlaceholderReplacer placeholders = new PlaceholderReplacer(); + + placeholders.put("refresh", clockLongFormatter.apply(now)); + placeholders.put("refreshFull", secondLongFormatter.apply(now)); + placeholders.put("version", versionCheckSystem.getUpdateButton().orElse(versionCheckSystem.getCurrentVersionButton())); + placeholders.put("updateModal", versionCheckSystem.getUpdateModal()); + placeholders.put("timeZone", config.getTimeZoneOffsetHours()); + + String playerName = player.getValue(PlayerKeys.NAME).orElse(playerUUID.toString()); + placeholders.put("playerName", playerName); + + placeholders.put("worldPieColors", theme.getValue(ThemeVal.GRAPH_WORLD_PIE)); + placeholders.put("gmPieColors", theme.getValue(ThemeVal.GRAPH_GM_PIE)); + placeholders.put("serverPieColors", theme.getValue(ThemeVal.GRAPH_SERVER_PREF_PIE)); + placeholders.put("firstDay", 1); + + placeholders.put("backButton", (serverInfo.getServer().isProxy() ? Html.BACK_BUTTON_NETWORK : Html.BACK_BUTTON_SERVER).parse()); + + PlayerPluginTab pluginTabs = pageFactory.inspectPluginTabs(playerUUID); + + placeholders.put("navPluginsTabs", pluginTabs.getNav()); + placeholders.put("pluginsTabs", pluginTabs.getTab()); + + return placeholders.apply(files.getCustomizableResourceOrDefault("web/player.html").asString()); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPluginTab.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPluginTab.java similarity index 72% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPluginTab.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPluginTab.java index f25e9b219..67506c2cf 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/InspectPluginTab.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayerPluginTab.java @@ -14,21 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.pages; +package com.djrapitops.plan.delivery.rendering.pages; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plan.delivery.rendering.html.structure.NavLink; +import com.djrapitops.plan.delivery.rendering.html.structure.TabsElement; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.implementation.TabInformation; -import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive; -import com.djrapitops.plan.extension.implementation.results.ExtensionInformation; -import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import com.djrapitops.plan.extension.implementation.results.ExtensionTableData; -import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.icon.Icon; -import com.djrapitops.plan.utilities.html.structure.TabsElement; +import com.djrapitops.plan.extension.implementation.results.*; +import com.djrapitops.plugin.utilities.Format; import java.util.*; @@ -37,10 +34,10 @@ import java.util.*; * * @author Rsl1122 */ -public class InspectPluginTab implements Comparable { +public class PlayerPluginTab implements Comparable { private String serverName; - private List playerData; + private List playerData; private Map> numberFormatters; @@ -52,14 +49,14 @@ public class InspectPluginTab implements Comparable { private boolean hasWideTable; - public InspectPluginTab(String nav, String tab) { + public PlayerPluginTab(String nav, String tab) { this.nav = nav; this.tab = tab; } - public InspectPluginTab( + public PlayerPluginTab( String serverName, - List playerData, + List playerData, Formatters formatters ) { this.serverName = serverName; @@ -89,12 +86,13 @@ public class InspectPluginTab implements Comparable { private void generate() { if (playerData.isEmpty()) { - nav = "
  • " + serverName + " (No Data)
  • "; - tab = "
    " + - "
    " + Html.CARD.parse("

    No Data (" + serverName + ")

    ") + - "
    "; + nav = NavLink.collapsed(Icon.called("cubes").build(), serverName + " (No Data)").toHtml(); + tab = wrapInTab( + serverName + " (No Data)", + "

    No Extension Data

    " + ); } else { - nav = "
  • " + serverName + "
  • "; + nav = NavLink.collapsed(Icon.called("cubes").build(), serverName).toHtml(); tab = generatePageTab(); } } @@ -104,7 +102,7 @@ public class InspectPluginTab implements Comparable { StringBuilder tabBuilder = new StringBuilder(); - for (ExtensionPlayerData datum : playerData) { + for (ExtensionData datum : playerData) { ExtensionInformation extensionInformation = datum.getExtensionInformation(); boolean onlyGeneric = datum.hasOnlyGenericTab(); @@ -122,11 +120,17 @@ public class InspectPluginTab implements Comparable { tabBuilder.append(wrapInContainer(extensionInformation, tabsElement)); } - return wrapInTab(tabBuilder.toString()); + return wrapInTab(serverName, tabBuilder.toString()); } - private String wrapInTab(String content) { - return "
    " + content + "
    "; + private String wrapInTab(String tabName, String content) { + return "
    " + + // Page heading + "
    " + + "

    " + serverName + " · Plugins Overview

    ${backButton}" + + "
    " + + // End Page heading + "
    " + content + "
    "; } private TabsElement.Tab wrapToTabElementTab(ExtensionTabData tabData) { @@ -145,7 +149,7 @@ public class InspectPluginTab implements Comparable { ElementOrder[] order = tabInformation.getTabElementOrder().orElse(ElementOrder.values()); String values = parseValuesHtml(tabData); - String valuesHtml = values.isEmpty() ? "" : Html.BODY.parse(values); + String valuesHtml = values.isEmpty() ? "" : "
    " + values + "
    "; String tablesHtml = parseTablesHtml(tabData); StringBuilder builder = new StringBuilder(); @@ -197,24 +201,25 @@ public class InspectPluginTab implements Comparable { builder.append("

    "); } builder.append(Icon.fromExtensionIcon(descriptive.getIcon())) - .append(' ').append(descriptive.getText()).append(": ").append(formattedValue).append("

    "); + .append(' ').append(descriptive.getText()).append("").append(formattedValue).append("

    "); } private String wrapInContainer(ExtensionInformation information, String tabsElement) { String colWidth = hasWideTable ? "col-md-8 col-lg-8" : "col-md-4 col-lg-4"; - return "
    " + - "
    " + - "

    " + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "

    " + + // TODO move large tables to their own tabs + return "
    " + + "
    " + + "
    " + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "
    " + "
    " + tabsElement + - "
    "; + "
    "; } @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof InspectPluginTab)) return false; - InspectPluginTab that = (InspectPluginTab) o; + if (!(o instanceof PlayerPluginTab)) return false; + PlayerPluginTab that = (PlayerPluginTab) o; return Objects.equals(serverName, that.serverName) && Objects.equals(nav, that.nav); } @@ -225,7 +230,7 @@ public class InspectPluginTab implements Comparable { } @Override - public int compareTo(InspectPluginTab other) { + public int compareTo(PlayerPluginTab other) { return String.CASE_INSENSITIVE_ORDER.compare(this.serverName, other.serverName); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayersPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayersPage.java new file mode 100644 index 000000000..08a35fc40 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/PlayersPage.java @@ -0,0 +1,70 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.pages; + +import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.config.paths.ProxySettings; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; + +/** + * Html String parser for /players page. + * + * @author Rsl1122 + */ +public class PlayersPage implements Page { + + private final VersionCheckSystem versionCheckSystem; + private final PlanFiles files; + private final PlanConfig config; + private final ServerInfo serverInfo; + + PlayersPage( + VersionCheckSystem versionCheckSystem, + PlanFiles files, + PlanConfig config, + ServerInfo serverInfo + ) { + this.versionCheckSystem = versionCheckSystem; + this.files = files; + this.config = config; + this.serverInfo = serverInfo; + } + + @Override + public String toHtml() throws ParseException { + try { + PlaceholderReplacer placeholders = new PlaceholderReplacer(); + + placeholders.put("version", versionCheckSystem.getUpdateButton().orElse(versionCheckSystem.getCurrentVersionButton())); + placeholders.put("updateModal", versionCheckSystem.getUpdateModal()); + if (serverInfo.getServer().isProxy()) { + placeholders.put("networkName", config.get(ProxySettings.NETWORK_NAME)); + } else { + placeholders.put("networkName", config.get(PluginSettings.SERVER_NAME)); + } + + return placeholders.apply(files.getCustomizableResourceOrDefault("web/players.html").asString()); + } catch (Exception e) { + throw new ParseException(e); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPage.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPage.java new file mode 100644 index 000000000..617407d91 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPage.java @@ -0,0 +1,148 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.rendering.pages; + +import com.djrapitops.plan.delivery.domain.container.CachingSupplier; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.container.RawDataContainer; +import com.djrapitops.plan.delivery.domain.keys.AnalysisKeys; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.formatting.PlaceholderReplacer; +import com.djrapitops.plan.delivery.rendering.html.Html; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; +import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerDataQuery; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.settings.theme.ThemeVal; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +import static com.djrapitops.plan.delivery.domain.keys.AnalysisKeys.*; + +/** + * Used for parsing a Html String out of server.html. + * + * @author Rsl1122 + */ +public class ServerPage implements Page { + + private final Server server; + private PlanConfig config; + private Theme theme; + private final VersionCheckSystem versionCheckSystem; + private final PlanFiles files; + private final DBSystem dbSystem; + private final ServerInfo serverInfo; + private Formatters formatters; + + ServerPage( + Server server, + PlanConfig config, + Theme theme, + VersionCheckSystem versionCheckSystem, + PlanFiles files, + DBSystem dbSystem, + ServerInfo serverInfo, + Formatters formatters + ) { + this.server = server; + this.config = config; + this.theme = theme; + this.versionCheckSystem = versionCheckSystem; + this.files = files; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + this.formatters = formatters; + } + + @Override + public String toHtml() throws ParseException { + PlaceholderReplacer placeholders = new PlaceholderReplacer(); + + UUID serverUUID = server.getUuid(); + placeholders.put("serverUUID", serverUUID.toString()); + placeholders.put("serverName", server.getIdentifiableName()); + placeholders.put("serverDisplayName", server.getName()); + + DataContainer constants = new RawDataContainer(); + constants.putRawData(AnalysisKeys.VERSION, versionCheckSystem.getCurrentVersion()); + constants.putRawData(AnalysisKeys.TIME_ZONE, config.getTimeZoneOffsetHours()); + + // TODO Move these graph settings to the graph requests instead of placeholders + constants.putRawData(AnalysisKeys.FIRST_DAY, 1); + constants.putRawData(AnalysisKeys.TPS_MEDIUM, config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED)); + constants.putRawData(AnalysisKeys.TPS_HIGH, config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_HIGH)); + constants.putRawData(AnalysisKeys.DISK_MEDIUM, config.get(DisplaySettings.GRAPH_DISK_THRESHOLD_MED)); + constants.putRawData(AnalysisKeys.DISK_HIGH, config.get(DisplaySettings.GRAPH_DISK_THRESHOLD_HIGH)); + constants.putRawData(AnalysisKeys.ACTIVITY_PIE_COLORS, theme.getValue(ThemeVal.GRAPH_ACTIVITY_PIE)); + constants.putRawData(AnalysisKeys.GM_PIE_COLORS, theme.getValue(ThemeVal.GRAPH_GM_PIE)); + constants.putRawData(AnalysisKeys.PLAYERS_GRAPH_COLOR, theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE)); + constants.putRawData(AnalysisKeys.TPS_LOW_COLOR, theme.getValue(ThemeVal.GRAPH_TPS_LOW)); + constants.putRawData(AnalysisKeys.TPS_MEDIUM_COLOR, theme.getValue(ThemeVal.GRAPH_TPS_MED)); + constants.putRawData(AnalysisKeys.TPS_HIGH_COLOR, theme.getValue(ThemeVal.GRAPH_TPS_HIGH)); + constants.putRawData(AnalysisKeys.WORLD_MAP_LOW_COLOR, theme.getValue(ThemeVal.WORLD_MAP_LOW)); + constants.putRawData(AnalysisKeys.WORLD_MAP_HIGH_COLOR, theme.getValue(ThemeVal.WORLD_MAP_HIGH)); + constants.putRawData(AnalysisKeys.WORLD_PIE_COLORS, theme.getValue(ThemeVal.GRAPH_WORLD_PIE)); + constants.putRawData(AnalysisKeys.AVG_PING_COLOR, theme.getValue(ThemeVal.GRAPH_AVG_PING)); + constants.putRawData(AnalysisKeys.MAX_PING_COLOR, theme.getValue(ThemeVal.GRAPH_MAX_PING)); + constants.putRawData(AnalysisKeys.MIN_PING_COLOR, theme.getValue(ThemeVal.GRAPH_MIN_PING)); + + placeholders.addAllPlaceholdersFrom(constants, + VERSION, TIME_ZONE, + FIRST_DAY, TPS_MEDIUM, TPS_HIGH, + DISK_MEDIUM, DISK_HIGH, + PLAYERS_MAX, PLAYERS_ONLINE, PLAYERS_TOTAL, + + WORLD_PIE_COLORS, GM_PIE_COLORS, ACTIVITY_PIE_COLORS, + PLAYERS_GRAPH_COLOR, TPS_HIGH_COLOR, TPS_MEDIUM_COLOR, + TPS_LOW_COLOR, WORLD_MAP_HIGH_COLOR, WORLD_MAP_LOW_COLOR, + AVG_PING_COLOR, MAX_PING_COLOR, MIN_PING_COLOR + ); + + placeholders.put("backButton", serverInfo.getServer().isProxy() ? Html.BACK_BUTTON_NETWORK.parse() : ""); + placeholders.put("version", versionCheckSystem.getUpdateButton().orElse(versionCheckSystem.getCurrentVersionButton())); + placeholders.put("updateModal", versionCheckSystem.getUpdateModal()); + + CachingSupplier pluginTabs = new CachingSupplier<>(() -> { + List extensionData = dbSystem.getDatabase().query(new ExtensionServerDataQuery(serverUUID)); + return new ServerPluginTabs(extensionData, formatters); + }); + + String nav = JSONCache.getOrCacheString(DataID.EXTENSION_NAV, serverUUID, () -> pluginTabs.get().getNav()); + String tabs = JSONCache.getOrCacheString(DataID.EXTENSION_TABS, serverUUID, () -> pluginTabs.get().getTabs()); + + placeholders.put("navPluginsTabs", nav); + placeholders.put("tabsPlugins", tabs); + + try { + return placeholders.apply(files.getCustomizableResourceOrDefault("web/server.html").asString()); + } catch (IOException e) { + throw new ParseException(e); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/AnalysisPluginTabs.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPluginTabs.java similarity index 59% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/AnalysisPluginTabs.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPluginTabs.java index c4a62549d..84ad7356c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/AnalysisPluginTabs.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/pages/ServerPluginTabs.java @@ -14,56 +14,57 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.pages; +package com.djrapitops.plan.delivery.rendering.pages; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plan.delivery.rendering.html.structure.NavLink; +import com.djrapitops.plan.delivery.rendering.html.structure.TabsElement; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.implementation.TabInformation; -import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive; -import com.djrapitops.plan.extension.implementation.results.ExtensionInformation; -import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import com.djrapitops.plan.extension.implementation.results.ExtensionTableData; -import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.icon.Icon; -import com.djrapitops.plan.utilities.html.structure.TabsElement; +import com.djrapitops.plan.extension.implementation.results.*; +import com.djrapitops.plugin.utilities.Format; import java.util.*; +import java.util.stream.Collectors; /** * Responsible for generating /server page plugin tabs based on DataExtension API data. *

    - * Currently very similar to {@link InspectPluginTab}. + * Currently very similar to {@link PlayerPluginTab}. * This will become more complex once tables are added, since some big tables will be moved to their own tabs. * * @author Rsl1122 */ -public class AnalysisPluginTabs { +public class ServerPluginTabs { - private List serverData; + private List serverData; + private List extraTabServerData; private Map> numberFormatters; private Formatter decimalFormatter; private Formatter percentageFormatter; - private String nav; + private StringBuilder nav; private String tab; - private boolean hasWideTable; - - public AnalysisPluginTabs(String nav, String tab) { - this.nav = nav; + public ServerPluginTabs(String nav, String tab) { + this.nav = new StringBuilder(nav); this.tab = tab; } - public AnalysisPluginTabs( - List serverData, + public ServerPluginTabs( + List serverData, Formatters formatters ) { this.serverData = serverData; + this.extraTabServerData = serverData.stream() + .filter(ExtensionData::doesNeedWiderSpace) + .collect(Collectors.toList()); + this.serverData.removeAll(extraTabServerData); numberFormatters = new EnumMap<>(FormatType.class); numberFormatters.put(FormatType.DATE_SECOND, formatters.secondLong()); @@ -74,13 +75,11 @@ public class AnalysisPluginTabs { this.decimalFormatter = formatters.decimals(); this.percentageFormatter = formatters.percentage(); - hasWideTable = false; - generate(); } public String getNav() { - return nav; + return nav.toString(); } public String getTabs() { @@ -89,22 +88,55 @@ public class AnalysisPluginTabs { private void generate() { if (serverData.isEmpty()) { - nav = "

  • Extensions (No Data)
  • "; - tab = "
    " + - "
    " + Html.CARD.parse("

    No Extension Data

    ") + - "
    "; + String tabName = "Overview (No Data)"; + nav = new StringBuilder(new NavLink(Icon.called("cubes").build(), tabName).toHtml()); + tab = wrapInTab( + tabName, + "

    No Extension Data

    " + ); } else { - nav = "
  • General
  • "; - tab = generatePageTab(); + nav = new StringBuilder(new NavLink(Icon.called("cubes").build(), "Overview").toHtml()); + tab = generatePageTabs(); } } - private String generatePageTab() { + private String generatePageTabs() { Collections.sort(serverData); + String overviewTab = generateOverviewTab(); + String extraTabs = generateExtraTabs(); + + return overviewTab + extraTabs; + } + + private String generateExtraTabs() { StringBuilder tabBuilder = new StringBuilder(); - for (ExtensionServerData datum : serverData) { + for (ExtensionData datum : extraTabServerData) { + ExtensionInformation extensionInformation = datum.getExtensionInformation(); + + boolean onlyGeneric = datum.hasOnlyGenericTab(); + + String tabsElement; + if (onlyGeneric) { + ExtensionTabData genericTabData = datum.getTabs().get(0); + tabsElement = parseContentHtml(genericTabData); + } else { + tabsElement = new TabsElement( + datum.getTabs().stream().map(this::wrapToTabElementTab).toArray(TabsElement.Tab[]::new) + ).toHtmlFull(); + } + + tabBuilder.append(wrapInTab(extensionInformation.getPluginName(), wrapInContainer(extensionInformation, tabsElement))); + nav.append(new NavLink(Icon.fromExtensionIcon(extensionInformation.getIcon()), extensionInformation.getPluginName()).toHtml()); + } + return tabBuilder.toString(); + } + + private String generateOverviewTab() { + StringBuilder tabBuilder = new StringBuilder(); + + for (ExtensionData datum : serverData) { ExtensionInformation extensionInformation = datum.getExtensionInformation(); boolean onlyGeneric = datum.hasOnlyGenericTab(); @@ -122,11 +154,17 @@ public class AnalysisPluginTabs { tabBuilder.append(wrapInContainer(extensionInformation, tabsElement)); } - return wrapInTab(tabBuilder.toString()); + return wrapInTab("Overview", tabBuilder.toString()); } - private String wrapInTab(String content) { - return "
    " + content + "
    "; + private String wrapInTab(String tabName, String content) { + return "
    " + + // Page heading + "
    " + + "

    · Plugins Overview

    ${backButton}" + + "
    " + + // End Page heading + "
    " + content + "
    "; } private TabsElement.Tab wrapToTabElementTab(ExtensionTabData tabData) { @@ -145,7 +183,7 @@ public class AnalysisPluginTabs { ElementOrder[] order = tabInformation.getTabElementOrder().orElse(ElementOrder.values()); String values = parseValuesHtml(tabData); - String valuesHtml = values.isEmpty() ? "" : Html.BODY.parse(values); + String valuesHtml = values.isEmpty() ? "" : "
    " + values + "
    "; String tablesHtml = parseTablesHtml(tabData); StringBuilder builder = new StringBuilder(); @@ -169,9 +207,6 @@ public class AnalysisPluginTabs { private String parseTablesHtml(ExtensionTabData tabData) { StringBuilder builder = new StringBuilder(); for (ExtensionTableData tableData : tabData.getTableData()) { - if (tableData.isWideTable()) { - hasWideTable = true; - } builder.append(tableData.getHtmlTable().parseHtml()); } return builder.toString(); @@ -197,16 +232,15 @@ public class AnalysisPluginTabs { builder.append("

    "); } builder.append(Icon.fromExtensionIcon(descriptive.getIcon())) - .append(' ').append(descriptive.getText()).append(": ").append(formattedValue).append("

    "); + .append(' ').append(descriptive.getText()).append("").append(formattedValue).append("

    "); } private String wrapInContainer(ExtensionInformation information, String tabsElement) { - String colWidth = hasWideTable ? "col-md-8 col-lg-8" : "col-md-4 col-lg-4"; - return "
    " + - "
    " + - "

    " + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "

    " + + return "
    " + + "
    " + + "
    " + Icon.fromExtensionIcon(information.getIcon()) + ' ' + information.getPluginName() + "
    " + "
    " + tabsElement + - "
    "; + "
    "; } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/NonProxyWebserverDisableChecker.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/NonProxyWebserverDisableChecker.java new file mode 100644 index 000000000..4b2443766 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/NonProxyWebserverDisableChecker.java @@ -0,0 +1,83 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver; + +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.config.paths.WebserverSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plugin.logging.L; +import com.djrapitops.plugin.logging.console.PluginLogger; +import com.djrapitops.plugin.logging.error.ErrorHandler; + +import java.io.IOException; + +/** + * In charge of disabling Webserver if a Proxy server is detected in the database. + * + * @author Rsl1122 + */ +public class NonProxyWebserverDisableChecker implements Runnable { + + private final PlanConfig config; + private final DBSystem dbSystem; + private final WebServerSystem webServerSystem; + private final PluginLogger logger; + private final ErrorHandler errorHandler; + + public NonProxyWebserverDisableChecker( + PlanConfig config, + DBSystem dbSystem, + WebServerSystem webServerSystem, + PluginLogger logger, + ErrorHandler errorHandler + ) { + this.config = config; + this.dbSystem = dbSystem; + this.webServerSystem = webServerSystem; + this.logger = logger; + this.errorHandler = errorHandler; + } + + @Override + public void run() { + if (config.isFalse(PluginSettings.PROXY_COPY_CONFIG)) return; + + dbSystem.getDatabase().query(ServerQueries.fetchProxyServerInformation()).ifPresent(proxy -> { + logger.info("Proxy server detected in the database - Proxy Webserver address is '" + proxy.getWebAddress() + "'."); + WebServer webServer = webServerSystem.getWebServer(); + + if (webServer.isEnabled()) { + disableWebserver(webServer); + } + }); + } + + private void disableWebserver(WebServer webServer) { + logger.warn("Disabling Webserver on this server - You can override this behavior by setting '" + PluginSettings.PROXY_COPY_CONFIG.getPath() + "' to false."); + webServer.disable(); + try { + config.set(WebserverSettings.DISABLED, true); + config.save(); + logger.warn("Note: Set '" + WebserverSettings.DISABLED.getPath() + "' to true"); + + } catch (IOException e) { + errorHandler.log(L.WARN, this.getClass(), e); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/Request.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/Request.java index e0d7a2bd2..036c3303f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/Request.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/Request.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver; +package com.djrapitops.plan.delivery.webserver; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.webserver.auth.Authentication; +import com.djrapitops.plan.delivery.webserver.auth.Authentication; +import com.djrapitops.plan.settings.locale.Locale; import com.sun.net.httpserver.HttpExchange; import java.io.InputStream; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/RequestHandler.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/RequestHandler.java index 8d7e99cbc..952016ea1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/RequestHandler.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver; +package com.djrapitops.plan.delivery.webserver; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.auth.BasicAuthentication; -import com.djrapitops.plan.system.webserver.response.PromptAuthorizationResponse; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import com.djrapitops.plan.system.webserver.response.errors.ForbiddenResponse; +import com.djrapitops.plan.delivery.webserver.auth.Authentication; +import com.djrapitops.plan.delivery.webserver.auth.BasicAuthentication; +import com.djrapitops.plan.delivery.webserver.response.PromptAuthorizationResponse; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +import com.djrapitops.plan.delivery.webserver.response.errors.ForbiddenResponse; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -36,6 +36,7 @@ import com.github.benmanes.caffeine.cache.Caffeine; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; +import org.apache.commons.lang3.StringUtils; import javax.inject.Inject; import javax.inject.Singleton; @@ -173,8 +174,8 @@ public class RequestHandler implements HttpHandler { } String authLine = authorization.get(0); - if (authLine.contains("Basic ")) { - return new BasicAuthentication(authLine.split(" ")[1], dbSystem.getDatabase()); + if (StringUtils.contains(authLine, "Basic ")) { + return new BasicAuthentication(StringUtils.split(authLine, ' ')[1], dbSystem.getDatabase()); } return null; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/RequestTarget.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/RequestTarget.java index e4a5f359a..94ef6cd9a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/RequestTarget.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/RequestTarget.java @@ -14,7 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver; +package com.djrapitops.plan.delivery.webserver; + +import org.apache.commons.lang3.StringUtils; import java.net.URI; import java.util.*; @@ -33,7 +35,8 @@ public class RequestTarget { public RequestTarget(URI targetURI) { resourceString = targetURI.getPath(); - resource = Arrays.stream(resourceString.split("/")).filter(part -> !part.isEmpty()).collect(Collectors.toList()); + resource = Arrays.stream(StringUtils.split(resourceString, '/')) + .filter(part -> !part.isEmpty()).collect(Collectors.toList()); parameters = new TreeMap<>(); parseParameters(targetURI.getQuery()); @@ -44,12 +47,12 @@ public class RequestTarget { return; } - String[] keysAndValues = parameterString.split("&"); + String[] keysAndValues = StringUtils.split(parameterString, '&'); for (String kv : keysAndValues) { if (kv.isEmpty()) { continue; } - String[] keyAndValue = kv.split("=", 2); + String[] keyAndValue = StringUtils.split(kv, "=", 2); if (keyAndValue.length >= 2) { parameters.put(keyAndValue[0], keyAndValue[1]); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseHandler.java similarity index 70% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseHandler.java index f36d6ba45..8486e3a53 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/ResponseHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/ResponseHandler.java @@ -14,19 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver; +package com.djrapitops.plan.delivery.webserver; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.api.exceptions.connection.*; -import com.djrapitops.plan.system.info.connection.InfoRequestPageHandler; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.pages.*; -import com.djrapitops.plan.system.webserver.pages.json.RootJSONHandler; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse; +import com.djrapitops.plan.delivery.webserver.auth.Authentication; +import com.djrapitops.plan.delivery.webserver.pages.*; +import com.djrapitops.plan.delivery.webserver.pages.json.RootJSONHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +import com.djrapitops.plan.delivery.webserver.response.errors.BadRequestResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.*; +import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import dagger.Lazy; @@ -47,33 +45,33 @@ public class ResponseHandler extends TreePageHandler { private final PlayersPageHandler playersPageHandler; private final PlayerPageHandler playerPageHandler; private final ServerPageHandler serverPageHandler; - private final InfoRequestPageHandler infoRequestPageHandler; private final RootJSONHandler rootJSONHandler; private final ErrorHandler errorHandler; + private final ServerInfo serverInfo; private Lazy webServer; @Inject public ResponseHandler( ResponseFactory responseFactory, Lazy webServer, + ServerInfo serverInfo, DebugPageHandler debugPageHandler, PlayersPageHandler playersPageHandler, PlayerPageHandler playerPageHandler, ServerPageHandler serverPageHandler, - InfoRequestPageHandler infoRequestPageHandler, RootJSONHandler rootJSONHandler, ErrorHandler errorHandler ) { super(responseFactory); this.webServer = webServer; + this.serverInfo = serverInfo; this.debugPageHandler = debugPageHandler; this.playersPageHandler = playersPageHandler; this.playerPageHandler = playerPageHandler; this.serverPageHandler = serverPageHandler; - this.infoRequestPageHandler = infoRequestPageHandler; this.rootJSONHandler = rootJSONHandler; this.errorHandler = errorHandler; } @@ -86,20 +84,15 @@ public class ResponseHandler extends TreePageHandler { registerPage("network", serverPageHandler); registerPage("server", serverPageHandler); - if (webServer.get().isAuthRequired()) { - registerPage("", new RootPageHandler(responseFactory)); - } else { - registerPage("", responseFactory.redirectResponse("/server"), 5); - } + registerPage("", new RootPageHandler(responseFactory, webServer.get(), serverInfo)); - registerPage("info", infoRequestPageHandler); - registerPage("json", rootJSONHandler); + registerPage("v1", rootJSONHandler); } public Response getResponse(Request request) { try { return tryToGetResponse(request); - } catch (NoServersException | NotFoundException e) { + } catch (NotFoundException e) { return responseFactory.notFound404(e.getMessage()); } catch (WebUserAuthException e) { return responseFactory.basicAuthFail(e); @@ -107,10 +100,6 @@ public class ResponseHandler extends TreePageHandler { return responseFactory.forbidden403(e.getMessage()); } catch (BadRequestException e) { return new BadRequestResponse(e.getMessage() + " (when requesting '" + request.getTargetString() + "')"); - } catch (UnauthorizedServerException e) { - return responseFactory.unauthorizedServer(e.getMessage()); - } catch (GatewayException e) { - return responseFactory.gatewayError504(e.getMessage()); } catch (InternalErrorException e) { if (e.getCause() != null) { return responseFactory.internalErrorResponse(e.getCause(), request.getTargetString()); @@ -129,16 +118,19 @@ public class ResponseHandler extends TreePageHandler { String resource = target.getResourceString(); if (target.endsWith(".css")) { - return ResponseCache.loadResponse(PageId.CSS.of(resource), () -> responseFactory.cssResponse(resource)); + return responseFactory.cssResponse(resource); } if (target.endsWith(".js")) { - return ResponseCache.loadResponse(PageId.JS.of(resource), () -> responseFactory.javaScriptResponse(resource)); + return responseFactory.javaScriptResponse(resource); + } + if (target.endsWith(".png")) { + return responseFactory.imageResponse(resource); } if (target.endsWith("favicon.ico")) { - return ResponseCache.loadResponse(PageId.FAVICON.id(), responseFactory::faviconResponse); + return responseFactory.faviconResponse(); } - boolean isNotInfoRequest = target.isEmpty() || !target.get(0).equals("info"); - boolean isAuthRequired = webServer.get().isAuthRequired() && isNotInfoRequest; + + boolean isAuthRequired = webServer.get().isAuthRequired(); if (isAuthRequired && !authentication.isPresent()) { if (webServer.get().isUsingHTTPS()) { return responseFactory.basicAuth(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServer.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServer.java index d38cab217..9a58821f1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServer.java @@ -14,26 +14,27 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver; +package com.djrapitops.plan.delivery.webserver; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.WebserverSettings; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.identification.properties.ServerProperties; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.config.paths.WebserverSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpsConfigurator; import com.sun.net.httpserver.HttpsParameters; import com.sun.net.httpserver.HttpsServer; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; import javax.inject.Inject; import javax.inject.Singleton; @@ -143,7 +144,13 @@ public class WebServer implements SubSystem { ExecutorService executor = new ThreadPoolExecutor( 4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100), - new ThreadFactoryBuilder().setNameFormat("Plan WebServer Thread-%d").build() + new BasicThreadFactory.Builder() + .namingPattern("Plan WebServer Thread-%d") + .uncaughtExceptionHandler((thread, throwable) -> { + if (config.get(PluginSettings.DEV_MODE)) { + errorHandler.log(L.WARN, WebServer.class, throwable); + } + }).build() ); server.setExecutor(executor); server.start(); @@ -151,6 +158,10 @@ public class WebServer implements SubSystem { enabled = true; logger.info(locale.getString(PluginLang.ENABLED_WEB_SERVER, server.getAddress().getPort(), getAccessAddress())); + boolean usingAlternativeIP = config.isTrue(WebserverSettings.SHOW_ALTERNATIVE_IP); + if (!usingAlternativeIP && serverProperties.getIp().isEmpty()) { + logger.log(L.INFO_COLOR, "§e" + locale.getString(PluginLang.ENABLE_NOTIFY_EMPTY_IP)); + } } catch (IllegalArgumentException | IllegalStateException | IOException e) { errorHandler.log(L.ERROR, this.getClass(), e); enabled = false; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServerSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServerSystem.java similarity index 70% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServerSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServerSystem.java index f703865dc..94e68b46d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/WebServerSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/WebServerSystem.java @@ -14,12 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver; +package com.djrapitops.plan.delivery.webserver; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plugin.benchmarking.Timings; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.exceptions.EnableException; import javax.inject.Inject; import javax.inject.Singleton; @@ -33,28 +32,28 @@ import javax.inject.Singleton; public class WebServerSystem implements SubSystem { private final WebServer webServer; - private Timings timings; @Inject - public WebServerSystem(WebServer webServer, Timings timings) { + public WebServerSystem( + WebServer webServer + ) { this.webServer = webServer; - this.timings = timings; } @Override public void enable() throws EnableException { - timings.start("WebServer Initialization"); webServer.enable(); - timings.end("WebServer Initialization"); } @Override public void disable() { - ResponseCache.clearCache(); webServer.disable(); + JSONCache.invalidateAll(); + JSONCache.cleanUp(); } public WebServer getWebServer() { return webServer; } + } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/Authentication.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/Authentication.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/Authentication.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/Authentication.java index ba1905741..3912fcddf 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/Authentication.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/Authentication.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.auth; +package com.djrapitops.plan.delivery.webserver.auth; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.data.WebUser; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.exceptions.WebUserAuthException; /** * Interface for different WebUser authentication methods used by Requests. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/BasicAuthentication.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/BasicAuthentication.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/BasicAuthentication.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/BasicAuthentication.java index 4d0fa2019..0a1797868 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/BasicAuthentication.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/BasicAuthentication.java @@ -14,16 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.auth; +package com.djrapitops.plan.delivery.webserver.auth; -import com.djrapitops.plan.api.exceptions.PassEncryptException; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.WebUserQueries; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.exceptions.PassEncryptException; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.WebUserQueries; import com.djrapitops.plan.utilities.Base64Util; import com.djrapitops.plan.utilities.PassEncryptUtil; +import org.apache.commons.lang3.StringUtils; /** * Authentication handling for Basic Auth. @@ -47,7 +48,7 @@ public class BasicAuthentication implements Authentication { public WebUser getWebUser() throws WebUserAuthException { String decoded = Base64Util.decode(authenticationString); - String[] userInfo = decoded.split(":"); + String[] userInfo = StringUtils.split(decoded, ':'); if (userInfo.length != 2) { throw new WebUserAuthException(FailReason.USER_AND_PASS_NOT_SPECIFIED); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/FailReason.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/FailReason.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/FailReason.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/FailReason.java index 0a7cfeddf..01f5be8f5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/auth/FailReason.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/auth/FailReason.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.auth; +package com.djrapitops.plan.delivery.webserver.auth; -import com.djrapitops.plan.system.locale.lang.Lang; +import com.djrapitops.plan.settings.locale.lang.Lang; /** * Reason for WebUserAuthException. * * @author Rsl1122 - * @see com.djrapitops.plan.api.exceptions.WebUserAuthException + * @see com.djrapitops.plan.exceptions.WebUserAuthException */ public enum FailReason implements Lang { USER_AND_PASS_NOT_SPECIFIED("User and Password not specified"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/NoServersException.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/DataID.java similarity index 51% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/NoServersException.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/DataID.java index 5e0b826a6..1efd85f7d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/NoServersException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/DataID.java @@ -14,24 +14,43 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.connection; +package com.djrapitops.plan.delivery.webserver.cache; + +import java.util.UUID; /** - * Thrown when ConnectionSystem can not find any servers to send request to. + * Enum for different JSON data entries that can be stored in {@link JSONCache}. * * @author Rsl1122 */ -public class NoServersException extends WebException { +public enum DataID { + PLAYERS, + SESSIONS, + SERVERS, + KILLS, + PING_TABLE, + GRAPH_PERFORMANCE, + GRAPH_ONLINE, + GRAPH_UNIQUE_NEW, + GRAPH_CALENDAR, + GRAPH_WORLD_PIE, + GRAPH_WORLD_MAP, + GRAPH_ACTIVITY, + GRAPH_PING, + GRAPH_SERVER_PIE, + GRAPH_PUNCHCARD, + SERVER_OVERVIEW, + ONLINE_OVERVIEW, + SESSIONS_OVERVIEW, + PVP_PVE, + PLAYERBASE_OVERVIEW, + PERFORMANCE_OVERVIEW, + EXTENSION_NAV, + EXTENSION_TABS + ; - public NoServersException(String message) { - super(message); + public String of(UUID serverUUID) { + return name() + '-' + serverUUID; } - public NoServersException(String message, Throwable cause) { - super(message, cause); - } - - public NoServersException(Throwable cause) { - super(cause); - } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/JSONCache.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/JSONCache.java new file mode 100644 index 000000000..68133e5d6 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/JSONCache.java @@ -0,0 +1,146 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.cache; + +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; +import com.djrapitops.plan.storage.file.ResourceCache; +import com.djrapitops.plugin.task.AbsRunnable; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import org.apache.commons.lang3.StringUtils; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * Cache for any JSON data sent via {@link com.djrapitops.plan.delivery.webserver.pages.json.RootJSONHandler}. + * + * @author Rsl1122 + */ +public class JSONCache { + + private static final Cache cache = Caffeine.newBuilder() + .expireAfterAccess(2, TimeUnit.MINUTES) + .build(); + + private JSONCache() { + // Static class + } + + public static Response getOrCache(String identifier, Supplier jsonResponseSupplier) { + String found = cache.getIfPresent(identifier); + if (found == null) { + JSONResponse response = jsonResponseSupplier.get(); + cache.put(identifier, response.getContent()); + return response; + } + return new JSONResponse(found); + } + + public static String getOrCacheString(DataID dataID, UUID serverUUID, Supplier stringSupplier) { + String identifier = dataID.of(serverUUID); + String found = cache.getIfPresent(identifier); + if (found == null) { + String result = stringSupplier.get(); + cache.put(identifier, result); + return result; + } + return found; + } + + public static Response getOrCache(DataID dataID, Supplier jsonResponseSupplier) { + return getOrCache(dataID.name(), jsonResponseSupplier); + } + + public static Response getOrCache(DataID dataID, UUID serverUUID, Supplier jsonResponseSupplier) { + return getOrCache(dataID.of(serverUUID), jsonResponseSupplier); + } + + public static void invalidate(String identifier) { + cache.invalidate(identifier); + } + + public static void invalidate(DataID dataID) { + invalidate(dataID.name()); + } + + public static void invalidate(UUID serverUUID, DataID... dataIDs) { + for (DataID dataID : dataIDs) { + invalidate(dataID.of(serverUUID)); + } + } + + public static void invalidate(DataID dataID, UUID serverUUID) { + invalidate(dataID.of(serverUUID)); + } + + public static void invalidateMatching(DataID... dataIDs) { + Set toInvalidate = Arrays.stream(dataIDs) + .map(DataID::name) + .collect(Collectors.toSet()); + for (String identifier : cache.asMap().keySet()) { + for (String identifierToInvalidate : toInvalidate) { + if (StringUtils.startsWith(identifier, identifierToInvalidate)) { + invalidate(identifier); + } + } + } + } + + public static void invalidateMatching(DataID dataID) { + String toInvalidate = dataID.name(); + for (String identifier : cache.asMap().keySet()) { + if (StringUtils.startsWith(identifier, toInvalidate)) { + invalidate(identifier); + } + } + } + + public static void invalidateAll() { + cache.invalidateAll(); + } + + public static void cleanUp() { + cache.cleanUp(); + } + + public static List getCachedIDs() { + List identifiers = new ArrayList<>(cache.asMap().keySet()); + Collections.sort(identifiers); + return identifiers; + } + + @Singleton + public static class CleanTask extends AbsRunnable { + + @Inject + public CleanTask() { + // Dagger requires inject constructor + } + + @Override + public void run() { + cleanUp(); + ResourceCache.cleanUp(); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/PageId.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/PageId.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/PageId.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/PageId.java index 935135d17..d111396cb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/PageId.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/cache/PageId.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.cache; +package com.djrapitops.plan.delivery.webserver.cache; import java.util.UUID; @@ -26,7 +26,6 @@ import java.util.UUID; public enum PageId { SERVER("serverPage:"), - RAW_SERVER("rawServer:"), PLAYER("playerPage:"), RAW_PLAYER("rawPlayer:"), PLAYERS("playersPage"), @@ -38,11 +37,7 @@ public enum PageId { JS("js:"), CSS("css:"), - FAVICON("Favicon"), - - @Deprecated - PLAYER_PLUGINS_TAB("playerPluginsTab:"), - NETWORK_CONTENT("networkContent"); + FAVICON("Favicon"); private final String id; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/DebugPageHandler.java similarity index 74% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/DebugPageHandler.java index 181a38a20..22503cd40 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/DebugPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/DebugPageHandler.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.pages; +package com.djrapitops.plan.delivery.webserver.pages; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; +import com.djrapitops.plan.delivery.domain.WebUser; +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.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +import com.djrapitops.plan.exceptions.WebUserAuthException; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PageHandler.java similarity index 76% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PageHandler.java index f76cf881a..7a2f8f42a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PageHandler.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.pages; +package com.djrapitops.plan.delivery.webserver.pages; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.response.Response; +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.webserver.response.Response; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.WebException; /** * PageHandlers are used for easier Response management and authorization checking. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayerPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayerPageHandler.java new file mode 100644 index 000000000..fd1010057 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayerPageHandler.java @@ -0,0 +1,88 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages; + +import com.djrapitops.plan.delivery.domain.WebUser; +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.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +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.UUID; + +/** + * PageHandler for /player/PlayerName pages. + * + * @author Rsl1122 + */ +@Singleton +public class PlayerPageHandler implements PageHandler { + + private final ResponseFactory responseFactory; + private final DBSystem dbSystem; + private final UUIDUtility uuidUtility; + + @Inject + public PlayerPageHandler( + ResponseFactory responseFactory, + DBSystem dbSystem, + UUIDUtility uuidUtility + ) { + this.responseFactory = responseFactory; + this.dbSystem = dbSystem; + this.uuidUtility = uuidUtility; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + if (target.isEmpty()) { + return responseFactory.pageNotFound404(); + } + + 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(); + } + 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(playerUUID); + } + return responseFactory.playerPageResponse(playerUUID); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + WebUser webUser = auth.getWebUser(); + return webUser.getPermLevel() <= 1 || webUser.getName().equalsIgnoreCase(target.get(target.size() - 1)); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayersPageHandler.java similarity index 67% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayersPageHandler.java index d1be11287..b8197b391 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayersPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/PlayersPageHandler.java @@ -14,20 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.pages; +package com.djrapitops.plan.delivery.webserver.pages; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.api.exceptions.connection.ForbiddenException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; +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.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.ForbiddenException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; import javax.inject.Inject; import javax.inject.Singleton; @@ -58,7 +56,7 @@ public class PlayersPageHandler implements PageHandler { if (dbState != Database.State.OPEN) { throw new ForbiddenException("Database is " + dbState.name() + " - Please try again later. You can check database status with /plan info"); } - return ResponseCache.loadResponse(PageId.PLAYERS.id(), responseFactory::playersPageResponse); + return responseFactory.playersPageResponse(); } @Override diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/RootPageHandler.java similarity index 60% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/RootPageHandler.java index 47052fa27..5f01d2ab3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/RootPageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/RootPageHandler.java @@ -14,16 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.pages; +package com.djrapitops.plan.delivery.webserver.pages; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.response.RedirectResponse; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.delivery.webserver.Request; +import com.djrapitops.plan.delivery.webserver.RequestTarget; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.delivery.webserver.auth.Authentication; +import com.djrapitops.plan.delivery.webserver.response.RedirectResponse; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.ServerInfo; import java.util.Optional; @@ -37,13 +39,21 @@ import java.util.Optional; public class RootPageHandler implements PageHandler { private final ResponseFactory responseFactory; + private final WebServer webServer; + private final ServerInfo serverInfo; - public RootPageHandler(ResponseFactory responseFactory) { + public RootPageHandler(ResponseFactory responseFactory, WebServer webServer, ServerInfo serverInfo) { this.responseFactory = responseFactory; + this.webServer = webServer; + this.serverInfo = serverInfo; } @Override public Response getResponse(Request request, RequestTarget target) throws WebException { + if (!webServer.isAuthRequired()) { + return responseFactory.redirectResponse(serverInfo.getServer().isProxy() ? "/network" : "/server"); + } + Optional auth = request.getAuth(); if (!auth.isPresent()) { return responseFactory.basicAuth(); @@ -54,7 +64,7 @@ public class RootPageHandler implements PageHandler { int permLevel = webUser.getPermLevel(); switch (permLevel) { case 0: - return new RedirectResponse("/server"); + return new RedirectResponse(serverInfo.getServer().isProxy() ? "/network" : "/server"); case 1: return new RedirectResponse("/players"); case 2: diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/ServerPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/ServerPageHandler.java new file mode 100644 index 000000000..0b0240c14 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/ServerPageHandler.java @@ -0,0 +1,103 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages; + +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.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +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.Server; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Optional; +import java.util.UUID; + +/** + * PageHandler for /server and /network pages. + * + * @author Rsl1122 + */ +@Singleton +public class ServerPageHandler implements PageHandler { + + private final ResponseFactory responseFactory; + private final DBSystem dbSystem; + private final ServerInfo serverInfo; + + @Inject + public ServerPageHandler( + ResponseFactory responseFactory, + DBSystem dbSystem, + ServerInfo serverInfo + ) { + this.responseFactory = responseFactory; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + UUID serverUUID = getServerUUID(target); + + checkDBState(); + if (serverInfo.getServer().isProxy() && serverInfo.getServerUUID().equals(serverUUID)) { + return responseFactory.networkPageResponse(); + } + return responseFactory.serverPageResponse(serverUUID); + } + + private void checkDBState() throws ForbiddenException { + 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"); + } + } + + private UUID getServerUUID(RequestTarget target) { + // Default to current server's page + UUID serverUUID = serverInfo.getServerUUID(); + + if (!target.isEmpty()) { + try { + String serverName = target.get(0); + Optional serverUUIDOptional = dbSystem.getDatabase() + .query(ServerQueries.fetchServerMatchingIdentifier(serverName)) + .map(Server::getUuid); + if (serverUUIDOptional.isPresent()) { + serverUUID = serverUUIDOptional.get(); + } + } catch (IllegalArgumentException ignore) { + /*ignored*/ + } + } + return serverUUID; + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/TreePageHandler.java similarity index 70% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/TreePageHandler.java index f082456ba..36f1730cc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/TreePageHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/TreePageHandler.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.pages; +package com.djrapitops.plan.delivery.webserver.pages; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; +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.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.WebException; import java.util.HashMap; import java.util.Map; @@ -47,6 +47,19 @@ public abstract class TreePageHandler implements PageHandler { pages.put(targetPage, handler); } + public void registerPage(String targetPage, PageHandler handler, int requiredPerm) { + pages.put(targetPage, new PageHandler() { + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + return handler.getResponse(request, target); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= requiredPerm; + } + }); + } public void registerPage(String targetPage, Response response, int requiredPerm) { pages.put(targetPage, new PageHandler() { @Override diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/GraphsJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/GraphsJSONHandler.java new file mode 100644 index 000000000..802e162e2 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/GraphsJSONHandler.java @@ -0,0 +1,133 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.rendering.json.graphs.GraphJSONParser; +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.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.delivery.webserver.pages.PageHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.BadRequestException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.Identifiers; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Collections; +import java.util.UUID; + +/** + * JSON handler for different graph data JSON requests. + * + * @author Rsl1122 + */ +@Singleton +public class GraphsJSONHandler implements PageHandler { + + private final Identifiers identifiers; + private final GraphJSONParser graphJSON; + + @Inject + public GraphsJSONHandler( + Identifiers identifiers, + GraphJSONParser graphJSON + ) { + this.identifiers = identifiers; + this.graphJSON = graphJSON; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + String type = target.getParameter("type") + .orElseThrow(() -> new BadRequestException("'type' parameter was not defined.")); + + DataID dataID = getDataID(type); + + if (target.getParameter("server").isPresent()) { + UUID serverUUID = identifiers.getServerUUID(target); // Can throw BadRequestException + return JSONCache.getOrCache(dataID, serverUUID, () -> generateGraphDataJSONOfType(dataID, serverUUID)); + } + // Assume network + return JSONCache.getOrCache(dataID, () -> generateGraphDataJSONOfType(dataID)); + } + + private DataID getDataID(String type) throws BadRequestException { + switch (type) { + case "performance": return DataID.GRAPH_PERFORMANCE; + case "playersOnline": return DataID.GRAPH_ONLINE; + case "uniqueAndNew": return DataID.GRAPH_UNIQUE_NEW; + case "serverCalendar": return DataID.GRAPH_CALENDAR; + case "worldPie": return DataID.GRAPH_WORLD_PIE; + case "activity": return DataID.GRAPH_ACTIVITY; + case "geolocation": return DataID.GRAPH_WORLD_MAP; + case "aggregatedPing": return DataID.GRAPH_PING; + case "punchCard": return DataID.GRAPH_PUNCHCARD; + case "serverPie": return DataID.GRAPH_SERVER_PIE; + default: throw new BadRequestException("unknown 'type' parameter: " + type); + } + } + + private JSONResponse generateGraphDataJSONOfType(DataID id, UUID serverUUID) { + switch (id) { + case GRAPH_PERFORMANCE: + return new JSONResponse(graphJSON.performanceGraphJSON(serverUUID)); + case GRAPH_ONLINE: + return new JSONResponse(graphJSON.playersOnlineGraph(serverUUID)); + case GRAPH_UNIQUE_NEW: + return new JSONResponse(graphJSON.uniqueAndNewGraphJSON(serverUUID)); + case GRAPH_CALENDAR: + return new JSONResponse(graphJSON.serverCalendarJSON(serverUUID)); + case GRAPH_WORLD_PIE: + return new JSONResponse(graphJSON.serverWorldPieJSONAsMap(serverUUID)); + case GRAPH_ACTIVITY: + return new JSONResponse(graphJSON.activityGraphsJSONAsMap(serverUUID)); + case GRAPH_WORLD_MAP: + return new JSONResponse(graphJSON.geolocationGraphsJSONAsMap(serverUUID)); + case GRAPH_PING: + return new JSONResponse(graphJSON.pingGraphsJSON(serverUUID)); + case GRAPH_PUNCHCARD: + return new JSONResponse(graphJSON.punchCardJSONAsMap(serverUUID)); + default: + return new JSONResponse(Collections.singletonMap("error", "Undefined ID: " + id.name())); + } + } + + private JSONResponse generateGraphDataJSONOfType(DataID id) { + switch (id) { + case GRAPH_ACTIVITY: + return new JSONResponse(graphJSON.activityGraphsJSONAsMap()); + case GRAPH_UNIQUE_NEW: + return new JSONResponse(graphJSON.uniqueAndNewGraphJSON()); + case GRAPH_SERVER_PIE: + return new JSONResponse(graphJSON.serverPreferencePieJSONAsMap()); + case GRAPH_WORLD_MAP: + return new JSONResponse(graphJSON.geolocationGraphsJSONAsMap()); + default: + return new JSONResponse(Collections.singletonMap("error", "Undefined ID: " + id.name())); + } + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/NetworkJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/NetworkJSONHandler.java new file mode 100644 index 000000000..1b0c313f0 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/NetworkJSONHandler.java @@ -0,0 +1,67 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.rendering.json.JSONFactory; +import com.djrapitops.plan.delivery.rendering.json.network.NetworkOverviewJSONParser; +import com.djrapitops.plan.delivery.rendering.json.network.NetworkPlayerBaseOverviewJSONParser; +import com.djrapitops.plan.delivery.rendering.json.network.NetworkSessionsOverviewJSONParser; +import com.djrapitops.plan.delivery.rendering.json.network.NetworkTabJSONParser; +import com.djrapitops.plan.delivery.webserver.RequestTarget; +import com.djrapitops.plan.delivery.webserver.auth.Authentication; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.pages.TreePageHandler; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +import com.djrapitops.plan.exceptions.WebUserAuthException; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Root handler for different JSON end points. + * + * @author Rsl1122 + */ +@Singleton +public class NetworkJSONHandler extends TreePageHandler { + + @Inject + public NetworkJSONHandler( + ResponseFactory responseFactory, + JSONFactory jsonFactory, + NetworkOverviewJSONParser networkOverviewJSONParser, + NetworkPlayerBaseOverviewJSONParser playerBaseOverviewJSONParser, + NetworkSessionsOverviewJSONParser sessionsOverviewJSONParser + ) { + super(responseFactory); + + registerPage("overview", DataID.SERVER_OVERVIEW, networkOverviewJSONParser); + registerPage("playerbaseOverview", DataID.PLAYERBASE_OVERVIEW, playerBaseOverviewJSONParser); + registerPage("sessionsOverview", DataID.SESSIONS_OVERVIEW, sessionsOverviewJSONParser); + registerPage("servers", DataID.SERVERS, jsonFactory::serversAsJSONMaps); + registerPage("pingTable", DataID.PING_TABLE, jsonFactory::pingPerGeolocation); + } + + private void registerPage(String identifier, DataID dataID, NetworkTabJSONParser tabJSONParser) { + registerPage(identifier, new NetworkTabJSONHandler<>(dataID, tabJSONParser)); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return true; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/NetworkTabJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/NetworkTabJSONHandler.java new file mode 100644 index 000000000..84b54c77d --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/NetworkTabJSONHandler.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.rendering.json.network.NetworkTabJSONParser; +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.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.delivery.webserver.pages.PageHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; + +import java.util.function.Supplier; + +/** + * Generic Tab JSON handler for any tab's data. + * + * @author Rsl1122 + */ +public class NetworkTabJSONHandler implements PageHandler { + + private final DataID dataID; + private final Supplier jsonParser; + + public NetworkTabJSONHandler(DataID dataID, NetworkTabJSONParser jsonParser) { + this.dataID = dataID; + this.jsonParser = jsonParser; + } + + @Override + public Response getResponse(Request request, RequestTarget target) { + return JSONCache.getOrCache(dataID, () -> new JSONResponse(jsonParser.get())); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayerJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayerJSONHandler.java new file mode 100644 index 000000000..7f81127bd --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayerJSONHandler.java @@ -0,0 +1,59 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.delivery.rendering.json.PlayerJSONParser; +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.webserver.pages.PageHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.Identifiers; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +@Singleton +public class PlayerJSONHandler implements PageHandler { + + private final Identifiers identifiers; + private final PlayerJSONParser jsonParser; + + @Inject + public PlayerJSONHandler(Identifiers identifiers, PlayerJSONParser jsonParser) { + this.identifiers = identifiers; + this.jsonParser = jsonParser; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + UUID playerUUID = identifiers.getPlayerUUID(target); // Can throw BadRequestException + return new JSONResponse(jsonParser.createJSONAsMap(playerUUID)); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + WebUser webUser = auth.getWebUser(); + return webUser.getPermLevel() <= 1 || webUser.getName().equalsIgnoreCase(target.get(target.size() - 1)); + + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayerKillsJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayerKillsJSONHandler.java new file mode 100644 index 000000000..44a0cc8e9 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayerKillsJSONHandler.java @@ -0,0 +1,69 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.rendering.json.JSONFactory; +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.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.delivery.webserver.pages.PageHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.Identifiers; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Collections; +import java.util.UUID; + +/** + * Performs parameter parsing for PvP kills JSON requests. + * + * @author Rsl1122 + */ +@Singleton +public class PlayerKillsJSONHandler implements PageHandler { + + private final Identifiers identifiers; + private final JSONFactory jsonFactory; + + @Inject + public PlayerKillsJSONHandler( + Identifiers identifiers, + JSONFactory jsonFactory + ) { + this.identifiers = identifiers; + this.jsonFactory = jsonFactory; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + UUID serverUUID = identifiers.getServerUUID(target); + return JSONCache.getOrCache(DataID.KILLS, serverUUID, () -> + new JSONResponse(Collections.singletonMap("player_kills", jsonFactory.serverPlayerKillsAsJSONMap(serverUUID))) + ); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayersTableJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayersTableJSONHandler.java new file mode 100644 index 000000000..ce888f972 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/PlayersTableJSONHandler.java @@ -0,0 +1,72 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.rendering.json.JSONFactory; +import com.djrapitops.plan.delivery.rendering.json.PlayersTableJSONParser; +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.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.delivery.webserver.pages.PageHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.Identifiers; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +/** + * JSON handler for different Player table JSON requests. + * + * @author Rsl1122 + * @see PlayersTableJSONParser For JSON parsing of /server players table. + */ +@Singleton +public class PlayersTableJSONHandler implements PageHandler { + + private final Identifiers identifiers; + private final JSONFactory jsonFactory; + + @Inject + public PlayersTableJSONHandler( + Identifiers identifiers, + JSONFactory jsonFactory + ) { + this.identifiers = identifiers; + this.jsonFactory = jsonFactory; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + if (target.getParameter("server").isPresent()) { + UUID serverUUID = identifiers.getServerUUID(target); // Can throw BadRequestException + return JSONCache.getOrCache(DataID.PLAYERS, serverUUID, () -> new JSONResponse(jsonFactory.serverPlayersTableJSON(serverUUID))); + } + // Assume network + return JSONCache.getOrCache(DataID.PLAYERS, () -> new JSONResponse(jsonFactory.networkPlayersTableJSON())); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/RootJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/RootJSONHandler.java new file mode 100644 index 000000000..e3b750711 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/RootJSONHandler.java @@ -0,0 +1,89 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.rendering.json.*; +import com.djrapitops.plan.delivery.webserver.RequestTarget; +import com.djrapitops.plan.delivery.webserver.auth.Authentication; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.pages.TreePageHandler; +import com.djrapitops.plan.delivery.webserver.response.ResponseFactory; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.identification.Identifiers; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Root handler for different JSON end points. + * + * @author Rsl1122 + */ +@Singleton +public class RootJSONHandler extends TreePageHandler { + + private Identifiers identifiers; + + @Inject + public RootJSONHandler( + ResponseFactory responseFactory, + Identifiers identifiers, + JSONFactory jsonFactory, + + GraphsJSONHandler graphsJSONHandler, + SessionsJSONHandler sessionsJSONHandler, + PlayersTableJSONHandler playersTableJSONHandler, + ServerOverviewJSONParser serverOverviewJSONParser, + OnlineActivityOverviewJSONParser onlineActivityOverviewJSONParser, + SessionsOverviewJSONParser sessionsOverviewJSONParser, + PlayerKillsJSONHandler playerKillsJSONHandler, + PvPPvEJSONParser pvPPvEJSONParser, + PlayerBaseOverviewJSONParser playerBaseOverviewJSONParser, + PerformanceJSONParser performanceJSONParser, + + PlayerJSONHandler playerJSONHandler, + NetworkJSONHandler networkJSONHandler + ) { + super(responseFactory); + this.identifiers = identifiers; + + registerPage("players", playersTableJSONHandler, 1); + registerPage("sessions", sessionsJSONHandler, 0); + registerPage("kills", playerKillsJSONHandler, 0); + registerPage("pingTable", DataID.PING_TABLE, jsonFactory::pingPerGeolocation); + registerPage("graph", graphsJSONHandler, 0); + + registerPage("serverOverview", DataID.SERVER_OVERVIEW, serverOverviewJSONParser); + registerPage("onlineOverview", DataID.ONLINE_OVERVIEW, onlineActivityOverviewJSONParser); + registerPage("sessionsOverview", DataID.SESSIONS_OVERVIEW, sessionsOverviewJSONParser); + registerPage("playerVersus", DataID.PVP_PVE, pvPPvEJSONParser); + registerPage("playerbaseOverview", DataID.PLAYERBASE_OVERVIEW, playerBaseOverviewJSONParser); + registerPage("performanceOverview", DataID.PERFORMANCE_OVERVIEW, performanceJSONParser); + + registerPage("player", playerJSONHandler, 2); + registerPage("network", networkJSONHandler, 0); + } + + private void registerPage(String identifier, DataID dataID, ServerTabJSONParser tabJSONParser) { + registerPage(identifier, new ServerTabJSONHandler<>(dataID, identifiers, tabJSONParser), 0); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return true; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/ServerTabJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/ServerTabJSONHandler.java new file mode 100644 index 000000000..ba58fc41b --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/ServerTabJSONHandler.java @@ -0,0 +1,66 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.rendering.json.ServerTabJSONParser; +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.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.delivery.webserver.pages.PageHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.Identifiers; + +import java.util.UUID; +import java.util.function.Function; + +/** + * Generic Tab JSON handler for any tab's data. + * + * @author Rsl1122 + */ +public class ServerTabJSONHandler implements PageHandler { + + private final DataID dataID; + private final Identifiers identifiers; + private final Function jsonParser; + + public ServerTabJSONHandler( + DataID dataID, + Identifiers identifiers, + ServerTabJSONParser jsonParser + ) { + this.dataID = dataID; + this.identifiers = identifiers; + this.jsonParser = jsonParser; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + UUID serverUUID = identifiers.getServerUUID(target); // Can throw BadRequestException + return JSONCache.getOrCache(dataID, serverUUID, () -> new JSONResponse(jsonParser.apply(serverUUID))); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/SessionsJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/SessionsJSONHandler.java new file mode 100644 index 000000000..dd5117938 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/pages/json/SessionsJSONHandler.java @@ -0,0 +1,75 @@ +/* + * 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 . + */ +package com.djrapitops.plan.delivery.webserver.pages.json; + +import com.djrapitops.plan.delivery.rendering.json.JSONFactory; +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.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.delivery.webserver.pages.PageHandler; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.WebException; +import com.djrapitops.plan.identification.Identifiers; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Collections; +import java.util.UUID; + +/** + * Performs parameter parsing for Sessions JSON requests. + * + * @author Rsl1122 + */ +@Singleton +public class SessionsJSONHandler implements PageHandler { + + private final Identifiers identifiers; + private final JSONFactory jsonFactory; + + @Inject + public SessionsJSONHandler( + Identifiers identifiers, + JSONFactory jsonFactory + ) { + this.identifiers = identifiers; + this.jsonFactory = jsonFactory; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + if (target.getParameter("server").isPresent()) { + UUID serverUUID = identifiers.getServerUUID(target); + return JSONCache.getOrCache(DataID.SESSIONS, serverUUID, () -> + new JSONResponse(Collections.singletonMap("sessions", jsonFactory.serverSessionsAsJSONMap(serverUUID))) + ); + } + // Assume network + return JSONCache.getOrCache(DataID.SESSIONS, () -> + new JSONResponse(Collections.singletonMap("sessions", jsonFactory.networkSessionsAsJSONMap())) + ); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ByteResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ByteResponse.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ByteResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ByteResponse.java index 547fda430..3ca48c558 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ByteResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ByteResponse.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.theme.Theme; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; import com.sun.net.httpserver.HttpExchange; import java.io.IOException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/CSSResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/CSSResponse.java similarity index 69% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/CSSResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/CSSResponse.java index 92cfb28f6..4430aae22 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/CSSResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/CSSResponse.java @@ -14,9 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; -import com.djrapitops.plan.system.file.PlanFiles; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.sun.net.httpserver.HttpExchange; import java.io.IOException; @@ -30,4 +33,10 @@ public class CSSResponse extends FileResponse { super.setType(ResponseType.CSS); setContent(getContent()); } + + @Override + public void send(HttpExchange exchange, Locale locale, Theme theme) throws IOException { + fixThemeColors(theme); + super.send(exchange, locale, theme); + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/DefaultResponses.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/DefaultResponses.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/DefaultResponses.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/DefaultResponses.java index 012e952cf..7e4d7a0fd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/DefaultResponses.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/DefaultResponses.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; /** * Enum containing default responses that don't need to be cached because they're always the same. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/FileResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/FileResponse.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/FileResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/FileResponse.java index 8362a11ca..94a9202b3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/FileResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/FileResponse.java @@ -14,10 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; -import com.djrapitops.plan.system.file.PlanFiles; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.utilities.Verify; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; @@ -36,18 +37,18 @@ public class FileResponse extends Response { } public static String format(String fileName) { - String[] split = fileName.split("/"); + String[] split = StringUtils.split(fileName, '/'); int i; for (i = 0; i < split.length; i++) { String s = split[i]; - if (Verify.equalsOne(s, "css", "js", "plugins", "scss")) { + if (Verify.equalsOne(s, "css", "js", "vendor", "img")) { break; } } StringBuilder b = new StringBuilder("web"); for (int j = i; j < split.length; j++) { String s = split[j]; - b.append("/").append(s); + b.append('/').append(s); } return b.toString(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/JavaScriptResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/JavaScriptResponse.java similarity index 74% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/JavaScriptResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/JavaScriptResponse.java index 88eca07c5..d49119695 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/JavaScriptResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/JavaScriptResponse.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; -import com.djrapitops.plan.system.file.PlanFiles; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.storage.file.PlanFiles; import java.io.IOException; @@ -25,8 +26,9 @@ import java.io.IOException; */ public class JavaScriptResponse extends FileResponse { - JavaScriptResponse(String fileName, PlanFiles files) throws IOException { + JavaScriptResponse(String fileName, PlanFiles files, Locale locale) throws IOException { super(format(fileName), files); + super.translate(locale::replaceLanguageInJavascript); super.setType(ResponseType.JAVASCRIPT); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/PromptAuthorizationResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/PromptAuthorizationResponse.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/PromptAuthorizationResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/PromptAuthorizationResponse.java index 69ea8a8c6..7098214a7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/PromptAuthorizationResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/PromptAuthorizationResponse.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; -import com.djrapitops.plan.system.webserver.auth.FailReason; -import com.djrapitops.plan.system.webserver.response.errors.ErrorResponse; -import com.djrapitops.plan.utilities.html.icon.Icon; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plan.delivery.webserver.auth.FailReason; +import com.djrapitops.plan.delivery.webserver.response.errors.ErrorResponse; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; import java.io.IOException; import java.util.ArrayList; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/RedirectResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/RedirectResponse.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/RedirectResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/RedirectResponse.java index 24c0afef7..3a7791ff4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/RedirectResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/RedirectResponse.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.theme.Theme; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; import com.sun.net.httpserver.HttpExchange; import java.io.IOException; @@ -27,14 +27,16 @@ import java.io.IOException; */ public class RedirectResponse extends Response { + private String direct; + public RedirectResponse(String direct) { super.setHeader("HTTP/1.1 302 Found"); - super.setContent(direct); + this.direct = direct; } @Override public void send(HttpExchange exchange, Locale locale, Theme theme) throws IOException { - responseHeaders.set("Location", getContent()); + responseHeaders.set("Location", direct); super.send(exchange, locale, theme); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/Response.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/Response.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/Response.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/Response.java index 177a94dc6..1394ca43c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/Response.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/Response.java @@ -14,18 +14,20 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.theme.Theme; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; +import org.apache.commons.lang3.StringUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import java.util.zip.GZIPOutputStream; /** @@ -55,9 +57,14 @@ public abstract class Response { } public Optional getHeader(String called) { - for (String header : header.split("\r\n")) { - if (header.startsWith(called)) { - return Optional.of(header.split(": ")[1]); + if (header != null) { + for (String header : StringUtils.split(header, "\r\n")) { + if (called == null) { + return Optional.of(header); + } + if (StringUtils.startsWith(header, called)) { + return Optional.of(StringUtils.split(header, ':')[1].trim()); + } } } return Optional.empty(); @@ -84,7 +91,7 @@ public abstract class Response { } public int getCode() { - return header == null ? 500 : Integer.parseInt(header.split(" ")[1]); + return getHeader(null).map(h -> Integer.parseInt(StringUtils.split(h, ' ')[1])).orElse(500); } @Override @@ -109,21 +116,22 @@ public abstract class Response { this.responseHeaders = responseHeaders; } + protected void translate(Function translator) { + content = translator.apply(content); + } + + protected void fixThemeColors(Theme theme) { + content = theme.replaceThemeColors(content); + } + public void send(HttpExchange exchange, Locale locale, Theme theme) throws IOException { responseHeaders.set("Content-Type", type); responseHeaders.set("Content-Encoding", "gzip"); exchange.sendResponseHeaders(getCode(), 0); - String sentContent = getContent(); - // TODO Smell - if (!(this instanceof JavaScriptResponse)) { - sentContent = locale.replaceMatchingLanguage(sentContent); - } - sentContent = theme.replaceThemeColors(sentContent); - try ( GZIPOutputStream out = new GZIPOutputStream(exchange.getResponseBody()); - ByteArrayInputStream bis = new ByteArrayInputStream(sentContent.getBytes(StandardCharsets.UTF_8)) + ByteArrayInputStream bis = new ByteArrayInputStream((content != null ? content : "").getBytes(StandardCharsets.UTF_8)) ) { byte[] buffer = new byte[2048]; int count; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseCode.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseCode.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseCode.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseCode.java index c2214263b..11b7cad9e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseCode.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseCode.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; /** * Enum for HTTP response codes. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseFactory.java similarity index 72% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseFactory.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseFactory.java index 7a2ac764b..b3de9a22b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseFactory.java @@ -14,21 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; -import com.djrapitops.plan.api.exceptions.ParseException; -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.db.access.queries.containers.ContainerFetchQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.ErrorPageLang; -import com.djrapitops.plan.system.update.VersionCheckSystem; -import com.djrapitops.plan.system.webserver.response.errors.*; -import com.djrapitops.plan.system.webserver.response.pages.*; -import com.djrapitops.plan.utilities.html.pages.PageFactory; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.error.ErrorHandler; +import com.djrapitops.plan.delivery.rendering.pages.PageFactory; +import com.djrapitops.plan.delivery.webserver.response.errors.ErrorResponse; +import com.djrapitops.plan.delivery.webserver.response.errors.ForbiddenResponse; +import com.djrapitops.plan.delivery.webserver.response.errors.InternalErrorResponse; +import com.djrapitops.plan.delivery.webserver.response.errors.NotFoundResponse; +import com.djrapitops.plan.delivery.webserver.response.pages.*; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.exceptions.WebUserAuthException; +import com.djrapitops.plan.exceptions.connection.NotFoundException; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.ErrorPageLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; import javax.inject.Inject; import javax.inject.Singleton; @@ -48,7 +50,6 @@ public class ResponseFactory { private final PageFactory pageFactory; private final Locale locale; private final DBSystem dbSystem; - private final ErrorHandler errorHandler; @Inject public ResponseFactory( @@ -56,15 +57,13 @@ public class ResponseFactory { PlanFiles files, PageFactory pageFactory, Locale locale, - DBSystem dbSystem, - ErrorHandler errorHandler + DBSystem dbSystem ) { this.versionCheckSystem = versionCheckSystem; this.files = files; this.pageFactory = pageFactory; this.locale = locale; this.dbSystem = dbSystem; - this.errorHandler = errorHandler; } public Response debugPageResponse() { @@ -85,7 +84,6 @@ public class ResponseFactory { public ErrorResponse internalErrorResponse(Throwable e, String s) { try { - errorHandler.log(L.WARN, this.getClass(), e); return new InternalErrorResponse(s, e, versionCheckSystem, files); } catch (IOException improperRestartException) { return new ErrorResponse( @@ -98,23 +96,27 @@ public class ResponseFactory { public Response networkPageResponse() { try { - return new NetworkPageResponse(pageFactory.networkPage()); + return new PageResponse(pageFactory.networkPage()); } catch (ParseException e) { return internalErrorResponse(e, "Failed to parse network page"); } } + public Response serverPageResponse(UUID serverUUID) throws NotFoundException { + try { + return new PageResponse(pageFactory.serverPage(serverUUID)); + } catch (ParseException e) { + return internalErrorResponse(e, "Failed to parse server page"); + } + } + public RawDataResponse rawPlayerPageResponse(UUID uuid) { return new RawPlayerDataResponse(dbSystem.getDatabase().query(ContainerFetchQueries.fetchPlayerContainer(uuid))); } - public RawDataResponse rawServerPageResponse(UUID serverUUID) { - return new RawServerDataResponse(dbSystem.getDatabase().query(ContainerFetchQueries.fetchServerContainer(serverUUID))); - } - public Response javaScriptResponse(String fileName) { try { - return new JavaScriptResponse(fileName, files); + return new JavaScriptResponse(fileName, files, locale); } catch (IOException e) { return notFound404("JS File not found from jar: " + fileName + ", " + e.toString()); } @@ -128,6 +130,10 @@ public class ResponseFactory { } } + public Response imageResponse(String fileName) { + return new ByteResponse(ResponseType.IMAGE, FileResponse.format(fileName), files); + } + public Response redirectResponse(String location) { return new RedirectResponse(location); } @@ -181,22 +187,6 @@ public class ResponseFactory { } } - public ErrorResponse unauthorizedServer(String message) { - try { - return new UnauthorizedServerResponse(message, versionCheckSystem, files); - } catch (IOException e) { - return internalErrorResponse(e, "Failed to parse UnauthorizedServerResponse"); - } - } - - public ErrorResponse gatewayError504(String message) { - try { - return new GatewayErrorResponse(message, versionCheckSystem, files); - } catch (IOException e) { - return internalErrorResponse(e, "Failed to parse GatewayErrorResponse"); - } - } - public ErrorResponse basicAuth() { try { return PromptAuthorizationResponse.getBasicAuthResponse(versionCheckSystem, files); @@ -205,11 +195,13 @@ public class ResponseFactory { } } - public ErrorResponse refreshingAnalysisResponse() { + public Response playerPageResponse(UUID playerUUID) { try { - return new RefreshingAnalysisResponse(versionCheckSystem, files); - } catch (IOException e) { - return internalErrorResponse(e, "Failed to parse RefreshingAnalysisResponse"); + return new PageResponse(pageFactory.playerPage(playerUUID)); + } catch (IllegalStateException e) { + return playerNotFound404(); + } catch (ParseException e) { + return internalErrorResponse(e, "Failed to parse player page"); } } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseType.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseType.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseType.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseType.java index 175cb0423..8c86f5c1c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/ResponseType.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/ResponseType.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; /** * Enum for HTTP content-type response header Strings. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/TextResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/TextResponse.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/TextResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/TextResponse.java index c549aeb17..f56558e49 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/TextResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/TextResponse.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response; +package com.djrapitops.plan.delivery.webserver.response; /** * Response for raw text. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/data/JSONResponse.java similarity index 55% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/data/JSONResponse.java index b114caa83..df754d95b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/data/JSONResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/data/JSONResponse.java @@ -14,10 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.data; +package com.djrapitops.plan.delivery.webserver.response.data; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseType; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseType; +import com.google.gson.Gson; +import com.google.gson.JsonElement; /** * Generic JSON response implemented using Gson. @@ -26,21 +28,14 @@ import com.djrapitops.plan.system.webserver.response.ResponseType; * * @author Rsl1122 */ -public class JSONResponse extends Response { +public class JSONResponse extends Response { - public JSONResponse(T object) { - super(ResponseType.JSON); - super.setHeader("HTTP/1.1 200 OK"); + public JSONResponse(Object object) { + this(new Gson().toJson(object)); + } - try { - Class gsonClass = Class.forName("com.google.gson.Gson"); - Object gson = gsonClass.getConstructor().newInstance(); - Object json = gsonClass.getMethod("toJson", Object.class).invoke(gson, object); - - super.setContent(json.toString()); - } catch (ReflectiveOperationException e) { - super.setContent("{\"error\":\"Gson for json responses not available on this server: " + e.toString() + "\"}"); - } + public JSONResponse(JsonElement json) { + this(json.getAsString()); } public JSONResponse(String jsonString) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/BadRequestResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/BadRequestResponse.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/BadRequestResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/BadRequestResponse.java index b82b30c35..2fbdfcb83 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/BadRequestResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/BadRequestResponse.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.errors; +package com.djrapitops.plan.delivery.webserver.response.errors; -import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.Response; /** * @author Fuzzlemann diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ErrorResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/ErrorResponse.java similarity index 65% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ErrorResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/ErrorResponse.java index 72d12ad5b..a2916a537 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ErrorResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/ErrorResponse.java @@ -14,11 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.errors; +package com.djrapitops.plan.delivery.webserver.response.errors; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; -import com.djrapitops.plan.system.webserver.response.pages.PageResponse; +import com.djrapitops.plan.delivery.webserver.response.pages.PageResponse; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; +import com.sun.net.httpserver.HttpExchange; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringSubstitutor; import java.io.IOException; @@ -48,15 +52,15 @@ public class ErrorResponse extends PageResponse { } public void replacePlaceholders() { - Map placeHolders = new HashMap<>(); - placeHolders.put("title", title); - String[] split = title.split(">", 3); - placeHolders.put("titleText", split.length == 3 ? split[2] : title); - placeHolders.put("paragraph", paragraph); - placeHolders.put("version", versionCheckSystem.getCurrentVersion()); - placeHolders.put("update", versionCheckSystem.getUpdateHtml().orElse("")); + Map placeholders = new HashMap<>(); + placeholders.put("title", title); + String[] split = StringUtils.split(title, ">", 3); + placeholders.put("titleText", split.length == 3 ? split[2] : title); + placeholders.put("paragraph", paragraph); + placeholders.put("version", versionCheckSystem.getUpdateButton().orElse(versionCheckSystem.getCurrentVersionButton())); + placeholders.put("updateModal", versionCheckSystem.getUpdateModal()); - setContent(StringSubstitutor.replace(getContent(), placeHolders)); + setContent(StringSubstitutor.replace(getContent(), placeholders)); } public void setTitle(String title) { @@ -67,6 +71,13 @@ public class ErrorResponse extends PageResponse { this.paragraph = paragraph; } + @Override + public void send(HttpExchange exchange, Locale locale, Theme theme) throws IOException { + translate(locale::replaceLanguageInHtml); + fixThemeColors(theme); + super.send(exchange, locale, theme); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ForbiddenResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/ForbiddenResponse.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ForbiddenResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/ForbiddenResponse.java index 9ee22d827..8d9b11670 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/ForbiddenResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/ForbiddenResponse.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.errors; +package com.djrapitops.plan.delivery.webserver.response.errors; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; -import com.djrapitops.plan.utilities.html.icon.Family; -import com.djrapitops.plan.utilities.html.icon.Icon; +import com.djrapitops.plan.delivery.rendering.html.icon.Family; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; import java.io.IOException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/InternalErrorResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/InternalErrorResponse.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/InternalErrorResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/InternalErrorResponse.java index 5789f6818..f8ee2dc40 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/InternalErrorResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/InternalErrorResponse.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.errors; +package com.djrapitops.plan.delivery.webserver.response.errors; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.icon.Icon; +import com.djrapitops.plan.delivery.rendering.html.Html; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; import java.io.IOException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/NotFoundResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/NotFoundResponse.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/NotFoundResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/NotFoundResponse.java index d019270dc..c5bd50781 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/NotFoundResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/errors/NotFoundResponse.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.errors; +package com.djrapitops.plan.delivery.webserver.response.errors; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; -import com.djrapitops.plan.utilities.html.icon.Icon; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; import java.io.IOException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/DebugPageResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/DebugPageResponse.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/DebugPageResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/DebugPageResponse.java index e95be1ca4..0895829d2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/DebugPageResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/DebugPageResponse.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.pages; +package com.djrapitops.plan.delivery.webserver.response.pages; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; -import com.djrapitops.plan.system.webserver.response.errors.ErrorResponse; -import com.djrapitops.plan.utilities.html.icon.Icon; -import com.djrapitops.plan.utilities.html.pages.DebugPage; +import com.djrapitops.plan.delivery.rendering.html.icon.Icon; +import com.djrapitops.plan.delivery.rendering.pages.DebugPage; +import com.djrapitops.plan.delivery.webserver.response.errors.ErrorResponse; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.version.VersionCheckSystem; import java.io.IOException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PageResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/PageResponse.java similarity index 58% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PageResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/PageResponse.java index fe6deb79d..7b216f0d5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PageResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/PageResponse.java @@ -14,11 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.pages; +package com.djrapitops.plan.delivery.webserver.response.pages; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseType; +import com.djrapitops.plan.delivery.rendering.pages.Page; +import com.djrapitops.plan.delivery.webserver.response.Response; +import com.djrapitops.plan.delivery.webserver.response.ResponseType; +import com.djrapitops.plan.exceptions.ParseException; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.theme.Theme; import com.googlecode.htmlcompressor.compressor.HtmlCompressor; +import com.sun.net.httpserver.HttpExchange; + +import java.io.IOException; /** * Response for all HTML Page responses. @@ -37,9 +44,22 @@ public class PageResponse extends Response { super(type); } + public PageResponse(Page page) throws ParseException { + this(ResponseType.HTML); + super.setHeader("HTTP/1.1 200 OK"); + setContent(page.toHtml()); + } + public PageResponse() { } + @Override + public void send(HttpExchange exchange, Locale locale, Theme theme) throws IOException { + translate(locale::replaceLanguageInHtml); + fixThemeColors(theme); + super.send(exchange, locale, theme); + } + @Override public void setContent(String content) { super.setContent(HTML_COMPRESSOR.compress(content)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PlayersPageResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/PlayersPageResponse.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PlayersPageResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/PlayersPageResponse.java index 194155f37..ac41e179d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/PlayersPageResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/PlayersPageResponse.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.pages; +package com.djrapitops.plan.delivery.webserver.response.pages; -import com.djrapitops.plan.api.exceptions.ParseException; -import com.djrapitops.plan.utilities.html.pages.PlayersPage; +import com.djrapitops.plan.delivery.rendering.pages.PlayersPage; +import com.djrapitops.plan.exceptions.ParseException; /** * @author Rsl1122 diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawDataResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawDataResponse.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawDataResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawDataResponse.java index a1dd8a097..b91c99dfd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawDataResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawDataResponse.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.pages; +package com.djrapitops.plan.delivery.webserver.response.pages; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.system.webserver.response.data.JSONResponse; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.webserver.response.data.JSONResponse; import java.util.HashMap; import java.util.List; @@ -32,7 +32,7 @@ import java.util.stream.Collectors; * * @author Rsl1122 */ -public class RawDataResponse extends JSONResponse> { +public class RawDataResponse extends JSONResponse { public RawDataResponse(DataContainer dataContainer) { super(mapToNormalMap(dataContainer)); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawPlayerDataResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawPlayerDataResponse.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawPlayerDataResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawPlayerDataResponse.java index 0ae64aaea..a4eb8deac 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawPlayerDataResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/response/pages/RawPlayerDataResponse.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.pages; +package com.djrapitops.plan.delivery.webserver.response.pages; -import com.djrapitops.plan.data.store.containers.PlayerContainer; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; /** * Raw Data JSON response for a Player. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/DataExtensionMethodCallException.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/DataExtensionMethodCallException.java index 5ed5c5c91..a504f386c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/DataExtensionMethodCallException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/DataExtensionMethodCallException.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions; +package com.djrapitops.plan.exceptions; import com.djrapitops.plan.extension.implementation.providers.MethodWrapper; @@ -29,7 +29,7 @@ public class DataExtensionMethodCallException extends IllegalStateException { private final String pluginName; // Non serializable field due to Method not being serializable. - private transient final MethodWrapper method; + private final transient MethodWrapper method; public DataExtensionMethodCallException(Throwable cause, String pluginName, MethodWrapper method) { super(cause); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/EnableException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/EnableException.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/EnableException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/EnableException.java index 209fe9201..13ae83fd6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/EnableException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/EnableException.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions; +package com.djrapitops.plan.exceptions; /** * Thrown when something goes wrong with Plan initialization. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/AnalysisPageResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/ExportException.java similarity index 71% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/AnalysisPageResponse.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/ExportException.java index 1b213fb03..f5fd9ffcb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/AnalysisPageResponse.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/ExportException.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.webserver.response.pages; +package com.djrapitops.plan.exceptions; /** + * Exception thrown by {@link com.djrapitops.plan.delivery.export.Exporter} if something goes wrong with export. + * * @author Rsl1122 */ -public class AnalysisPageResponse extends PageResponse { +public class ExportException extends Exception { - public AnalysisPageResponse(String html) { - super.setHeader("HTTP/1.1 200 OK"); - super.setContent(html); + public ExportException(String message, Throwable cause) { + super(message, cause); } -} +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/ParseException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/ParseException.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/ParseException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/ParseException.java index 5b765d85a..162dbfc03 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/ParseException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/ParseException.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions; +package com.djrapitops.plan.exceptions; /** * Exception thrown when Page encounters an Exception. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/PassEncryptException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/PassEncryptException.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/PassEncryptException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/PassEncryptException.java index ac618d786..2c08b4816 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/PassEncryptException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/PassEncryptException.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions; +package com.djrapitops.plan.exceptions; public class PassEncryptException extends Exception { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/WebUserAuthException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/WebUserAuthException.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/WebUserAuthException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/WebUserAuthException.java index 592100be4..8ba8ae959 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/WebUserAuthException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/WebUserAuthException.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions; +package com.djrapitops.plan.exceptions; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.webserver.auth.FailReason; +import com.djrapitops.plan.delivery.webserver.auth.FailReason; +import com.djrapitops.plan.exceptions.connection.WebException; /** * Thrown when WebUser can not be authorized (WebServer). diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/BadRequestException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/BadRequestException.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/BadRequestException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/BadRequestException.java index 24d46856b..9597994b3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/BadRequestException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/BadRequestException.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.connection; +package com.djrapitops.plan.exceptions.connection; -import com.djrapitops.plan.system.webserver.response.ResponseCode; +import com.djrapitops.plan.delivery.webserver.response.ResponseCode; /** * Thrown when connection is returned 401 Bad Request. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/ForbiddenException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/ForbiddenException.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/ForbiddenException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/ForbiddenException.java index de9e1473d..bbb0f864d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/ForbiddenException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/ForbiddenException.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.connection; +package com.djrapitops.plan.exceptions.connection; -import com.djrapitops.plan.system.webserver.response.ResponseCode; +import com.djrapitops.plan.delivery.webserver.response.ResponseCode; /** * Thrown when Connection gets a 403 response. * * @author Rsl1122 */ -public class ForbiddenException extends WebFailException { +public class ForbiddenException extends WebException { public ForbiddenException(String url) { super("Forbidden: " + url, ResponseCode.FORBIDDEN); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/InternalErrorException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/InternalErrorException.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/InternalErrorException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/InternalErrorException.java index 1646f4713..02cc3e270 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/InternalErrorException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/InternalErrorException.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.connection; +package com.djrapitops.plan.exceptions.connection; -import com.djrapitops.plan.system.webserver.response.ResponseCode; +import com.djrapitops.plan.delivery.webserver.response.ResponseCode; /** * Thrown when Connection returns 500. * * @author Rsl1122 */ -public class InternalErrorException extends WebFailException { +public class InternalErrorException extends WebException { public InternalErrorException() { super("Internal Error occurred on receiving server", ResponseCode.INTERNAL_ERROR); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/NotFoundException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/NotFoundException.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/NotFoundException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/NotFoundException.java index 67ee7e57a..1f0837df5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/NotFoundException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/NotFoundException.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.connection; +package com.djrapitops.plan.exceptions.connection; -import com.djrapitops.plan.system.webserver.response.ResponseCode; +import com.djrapitops.plan.delivery.webserver.response.ResponseCode; /** * Thrown when Connection returns 404, when page is not found. * * @author Rsl1122 */ -public class NotFoundException extends WebFailException { +public class NotFoundException extends WebException { public NotFoundException(String message) { super(message, ResponseCode.NOT_FOUND); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/WebException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/WebException.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/WebException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/WebException.java index 2bea242a0..4103ebd96 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/WebException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/connection/WebException.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.connection; +package com.djrapitops.plan.exceptions.connection; -import com.djrapitops.plan.system.webserver.response.ResponseCode; +import com.djrapitops.plan.delivery.webserver.response.ResponseCode; /** * Thrown when Connection POST-request fails, general Exception. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/DBInitException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBInitException.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/DBInitException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBInitException.java index 81734c977..d6bc9b64b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/DBInitException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBInitException.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.database; +package com.djrapitops.plan.exceptions.database; /** * Thrown when something goes wrong with {@code Database#init}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/DBOpException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBOpException.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/DBOpException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBOpException.java index 5e8fc3448..05159056d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/DBOpException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/DBOpException.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.database; +package com.djrapitops.plan.exceptions.database; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/FatalDBException.java b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/FatalDBException.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/FatalDBException.java rename to Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/FatalDBException.java index f192b8e7e..01c7214ac 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/database/FatalDBException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/exceptions/database/FatalDBException.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.database; +package com.djrapitops.plan.exceptions.database; public class FatalDBException extends DBOpException { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java index a37c8ae77..0b70088e7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/ExtensionServiceImplementation.java @@ -16,17 +16,19 @@ */ package com.djrapitops.plan.extension; -import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; -import com.djrapitops.plan.data.plugin.PluginsConfigSection; +import com.djrapitops.plan.DebugChannels; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.extension.implementation.CallerImplementation; import com.djrapitops.plan.extension.implementation.DataProviderExtractor; import com.djrapitops.plan.extension.implementation.ExtensionRegister; import com.djrapitops.plan.extension.implementation.providers.gathering.ProviderValueGatherer; -import com.djrapitops.plan.system.DebugChannels; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.config.ExtensionSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -82,7 +84,7 @@ public class ExtensionServiceImplementation implements ExtensionService { public void register() { try { - extensionRegister.registerBuiltInExtensions(config.getPluginsConfigSection().getDisabled()); + extensionRegister.registerBuiltInExtensions(config.getExtensionSettings().getDisabled()); } catch (IllegalStateException failedToRegisterOne) { logger.warn("One or more extensions failed to register, see suppressed exceptions."); errorHandler.log(L.WARN, this.getClass(), failedToRegisterOne); @@ -120,7 +122,7 @@ public class ExtensionServiceImplementation implements ExtensionService { } private boolean shouldNotAllowRegistration(String pluginName) { - PluginsConfigSection pluginsConfig = config.getPluginsConfigSection(); + ExtensionSettings pluginsConfig = config.getExtensionSettings(); if (!pluginsConfig.hasSection(pluginName)) { try { @@ -161,9 +163,9 @@ public class ExtensionServiceImplementation implements ExtensionService { // Try again updatePlayerValues(gatherer, playerUUID, playerName, event); } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) { - logger.warn("Encountered unexpected error with " + gatherer.getPluginName() + " Extension (please report this): " + unexpectedError + + logger.warn("Encountered unexpected error with " + gatherer.getPluginName() + " Extension: " + unexpectedError + " (but failed safely) when updating value for '" + playerName + - "', stack trace to follow:"); + "', stack trace to follow (please report this):"); errorHandler.log(L.WARN, gatherer.getClass(), unexpectedError); } } @@ -171,10 +173,10 @@ public class ExtensionServiceImplementation implements ExtensionService { private void logFailure(String playerName, DataExtensionMethodCallException methodCallFailed) { Throwable cause = methodCallFailed.getCause(); String causeName = cause.getClass().getSimpleName(); - logger.warn("Encountered " + causeName + " with " + methodCallFailed.getPluginName() + " Extension (please report this)" + + logger.warn("Encountered " + causeName + " with " + methodCallFailed.getPluginName() + " Extension" + " (failed safely) when updating value for '" + playerName + "', the method was disabled temporarily (won't be called until next Plan reload)" + - ", stack trace to follow:"); + ", stack trace to follow (please report this):"); errorHandler.log(L.WARN, getClass(), cause); } @@ -182,6 +184,9 @@ public class ExtensionServiceImplementation implements ExtensionService { for (ProviderValueGatherer gatherer : extensionGatherers.values()) { updateServerValues(gatherer, event); } + UUID serverUUID = serverInfo.getServerUUID(); + JSONCache.invalidate(DataID.EXTENSION_NAV, serverUUID); + JSONCache.invalidate(DataID.EXTENSION_TABS, serverUUID); } public void updateServerValues(ProviderValueGatherer gatherer, CallEvents event) { @@ -200,8 +205,8 @@ public class ExtensionServiceImplementation implements ExtensionService { // Try again updateServerValues(gatherer, event); } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError unexpectedError) { - logger.warn("Encountered unexpected error with " + gatherer.getPluginName() + " Extension (please report this): " + unexpectedError + - " (failed safely) when updating value for server, stack trace to follow:"); + logger.warn("Encountered unexpected error with " + gatherer.getPluginName() + " Extension: " + unexpectedError + + " (failed safely) when updating value for server, stack trace to follow (please report this):"); errorHandler.log(L.WARN, gatherer.getClass(), unexpectedError); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java index fa05d4516..0f85db8ef 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/CallerImplementation.java @@ -20,7 +20,7 @@ import com.djrapitops.plan.extension.CallEvents; import com.djrapitops.plan.extension.Caller; import com.djrapitops.plan.extension.ExtensionServiceImplementation; import com.djrapitops.plan.extension.implementation.providers.gathering.ProviderValueGatherer; -import com.djrapitops.plan.system.processing.Processing; +import com.djrapitops.plan.processing.Processing; import com.djrapitops.plugin.utilities.Verify; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java index d3aa47fa2..dd3adca44 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/DataProviderExtractor.java @@ -125,6 +125,7 @@ public class DataProviderExtractor { extractDataProviders(pluginInfo, tabs, conditions, NumberProvider.class, NumberDataProvider::placeToDataProviders); extractDataProviders(pluginInfo, tabs, conditions, StringProvider.class, StringDataProvider::placeToDataProviders); extractDataProviders(pluginInfo, tabs, conditions, TableProvider.class, TableDataProvider::placeToDataProviders); + extractDataProviders(pluginInfo, tabs, conditions, GroupProvider.class, GroupDataProvider::placeToDataProviders); } private void extractDataProviders(PluginInfo pluginInfo, Map tabs, Map conditions, Class ofKind, DataProviderFactory factory) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/GroupDataProvider.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/GroupDataProvider.java new file mode 100644 index 000000000..962e9d6d8 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/GroupDataProvider.java @@ -0,0 +1,53 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.implementation.providers; + +import com.djrapitops.plan.extension.annotation.Conditional; +import com.djrapitops.plan.extension.annotation.GroupProvider; +import com.djrapitops.plan.extension.annotation.StringProvider; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.ProviderInformation; + +import java.lang.reflect.Method; + +/** + * Represents a DataExtension API method annotated with {@link StringProvider} annotation. + *

    + * Used to obtain data to place in the database. + * + * @author Rsl1122 + */ +public class GroupDataProvider extends DataProvider { + + private GroupDataProvider(ProviderInformation providerInformation, MethodWrapper methodWrapper) { + super(providerInformation, methodWrapper); + } + + public static void placeToDataProviders( + DataProviders dataProviders, Method method, GroupProvider annotation, + Conditional condition, String tab, String pluginName + ) { + MethodWrapper methodWrapper = new MethodWrapper<>(method, String[].class); + Icon providerIcon = new Icon(annotation.iconFamily(), annotation.iconName(), annotation.groupColor()); + + ProviderInformation providerInformation = new ProviderInformation( + pluginName, method.getName(), annotation.text(), null, providerIcon, 0, true, tab, condition + ); + + dataProviders.put(new GroupDataProvider(providerInformation, methodWrapper)); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java index be1112abf..b0f447266 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/BooleanProviderValueGatherer.java @@ -16,9 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; -import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.implementation.ProviderInformation; import com.djrapitops.plan.extension.implementation.providers.BooleanDataProvider; @@ -29,6 +27,8 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreBooleanProviderTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerBooleanResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerBooleanResultTransaction; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.*; import java.util.concurrent.Callable; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java index b1d9a73d8..e02ceb256 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/DoubleAndPercentageProviderValueGatherer.java @@ -16,9 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; -import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.implementation.ProviderInformation; import com.djrapitops.plan.extension.implementation.providers.DataProvider; @@ -31,6 +29,8 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.results import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerPercentageResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerDoubleResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerPercentageResultTransaction; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.Optional; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/GroupProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/GroupProviderValueGatherer.java new file mode 100644 index 000000000..1aee20075 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/GroupProviderValueGatherer.java @@ -0,0 +1,90 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.implementation.providers.gathering; + +import com.djrapitops.plan.exceptions.DataExtensionMethodCallException; +import com.djrapitops.plan.extension.DataExtension; +import com.djrapitops.plan.extension.implementation.ProviderInformation; +import com.djrapitops.plan.extension.implementation.providers.DataProvider; +import com.djrapitops.plan.extension.implementation.providers.DataProviders; +import com.djrapitops.plan.extension.implementation.providers.MethodWrapper; +import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIconTransaction; +import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreGroupProviderTransaction; +import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerGroupsResultTransaction; +import com.djrapitops.plan.storage.database.Database; + +import java.util.Optional; +import java.util.UUID; + +/** + * Gathers GroupProvider method data. + * + * @author Rsl1122 + */ +class GroupProviderValueGatherer { + + private final String pluginName; + private final DataExtension extension; + private final UUID serverUUID; + + private final Database database; + private final DataProviders dataProviders; + + GroupProviderValueGatherer( + String pluginName, DataExtension extension, + UUID serverUUID, Database database, + DataProviders dataProviders + ) { + this.pluginName = pluginName; + this.extension = extension; + this.serverUUID = serverUUID; + this.database = database; + this.dataProviders = dataProviders; + } + + void gatherGroupDataOfPlayer(UUID playerUUID, String playerName, Conditions conditions) { + for (DataProvider groupProvider : dataProviders.getPlayerMethodsByType(String[].class)) { + gatherGroupDataOfProvider(playerUUID, playerName, conditions, groupProvider); + } + } + + private void gatherGroupDataOfProvider( + UUID playerUUID, String playerName, + Conditions conditions, + DataProvider groupProvider + ) { + ProviderInformation providerInformation = groupProvider.getProviderInformation(); + Optional condition = providerInformation.getCondition(); + if (condition.isPresent() && conditions.isNotFulfilled(condition.get())) { + return; + } + + MethodWrapper method = groupProvider.getMethod(); + try { + String[] result = method.callMethod(extension, playerUUID, playerName); + if (result == null) { + return; // Error during call + } + + database.executeTransaction(new StoreIconTransaction(providerInformation.getIcon())); + database.executeTransaction(new StoreGroupProviderTransaction(groupProvider, serverUUID)); + database.executeTransaction(new StorePlayerGroupsResultTransaction(pluginName, serverUUID, method.getMethodName(), playerUUID, result)); + } catch (Exception | NoClassDefFoundError | NoSuchFieldError | NoSuchMethodError e) { + throw new DataExtensionMethodCallException(e, pluginName, method); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java index c768146cb..61e676346 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/NumberProviderValueGatherer.java @@ -16,9 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; -import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.implementation.ProviderInformation; @@ -30,6 +28,8 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreNumberProviderTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerNumberResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerNumberResultTransaction; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.Optional; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java index 45ecfc273..693617db0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/ProviderValueGatherer.java @@ -16,7 +16,6 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; -import com.djrapitops.plan.db.Database; import com.djrapitops.plan.extension.CallEvents; import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.icon.Icon; @@ -28,8 +27,9 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc import com.djrapitops.plan.extension.implementation.storage.transactions.StorePluginTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.StoreTabInformationTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveInvalidResultsTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; import java.util.UUID; @@ -51,6 +51,7 @@ public class ProviderValueGatherer { private DoubleAndPercentageProviderValueGatherer doubleAndPercentageGatherer; private StringProviderValueGatherer stringGatherer; private TableProviderValueGatherer tableGatherer; + private GroupProviderValueGatherer groupGatherer; public ProviderValueGatherer( @@ -83,6 +84,9 @@ public class ProviderValueGatherer { tableGatherer = new TableProviderValueGatherer( pluginName, extension, serverUUID, database, dataProviders ); + groupGatherer = new GroupProviderValueGatherer( + pluginName, extension, serverUUID, database, dataProviders + ); } public void disableMethodFromUse(MethodWrapper method) { @@ -129,6 +133,7 @@ public class ProviderValueGatherer { doubleAndPercentageGatherer.gatherDoubleDataOfPlayer(playerUUID, playerName, conditions); stringGatherer.gatherStringDataOfPlayer(playerUUID, playerName, conditions); tableGatherer.gatherTableDataOfPlayer(playerUUID, playerName, conditions); + groupGatherer.gatherGroupDataOfPlayer(playerUUID, playerName, conditions); } public void updateValues() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java index 74ddc6405..96f11ef7a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/StringProviderValueGatherer.java @@ -16,9 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; -import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.implementation.ProviderInformation; import com.djrapitops.plan.extension.implementation.providers.DataProvider; @@ -29,6 +27,8 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.StoreIc import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreStringProviderTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerStringResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerStringResultTransaction; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.Transaction; import org.apache.commons.lang3.StringUtils; import java.util.Optional; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java index f5c4349af..32add67a5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/providers/gathering/TableProviderValueGatherer.java @@ -16,9 +16,7 @@ */ package com.djrapitops.plan.extension.implementation.providers.gathering; -import com.djrapitops.plan.api.exceptions.DataExtensionMethodCallException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.exceptions.DataExtensionMethodCallException; import com.djrapitops.plan.extension.DataExtension; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.ProviderInformation; @@ -31,6 +29,8 @@ import com.djrapitops.plan.extension.implementation.storage.transactions.provide import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerTableResultTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerTableResultTransaction; import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.Optional; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/GatewayException.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/DescribedExtensionData.java similarity index 68% rename from Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/GatewayException.java rename to Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/DescribedExtensionData.java index a5c064323..5dfdbc806 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/api/exceptions/connection/GatewayException.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/DescribedExtensionData.java @@ -14,18 +14,20 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.api.exceptions.connection; - -import com.djrapitops.plan.system.webserver.response.ResponseCode; +package com.djrapitops.plan.extension.implementation.results; /** - * Thrown when InfoRequest can not connect to the other server. + * Represents a data-point given by a Provider method of a DataExtension. * * @author Rsl1122 */ -public class GatewayException extends WebException { +public interface DescribedExtensionData { + + /** + * Get Descriptive information about the data point. + * + * @return a {@link ExtensionDescriptive}. + */ + ExtensionDescriptive getDescriptive(); - public GatewayException(String message) { - super(message, ResponseCode.GATEWAY_ERROR); - } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionBooleanData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionBooleanData.java index ba0820410..4bcd50e83 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionBooleanData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionBooleanData.java @@ -21,7 +21,7 @@ package com.djrapitops.plan.extension.implementation.results; * * @author Rsl1122 */ -public class ExtensionBooleanData implements ExtensionData { +public class ExtensionBooleanData implements DescribedExtensionData { private ExtensionDescriptive descriptive; private boolean value; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionData.java index 4b7634d1b..50468b3ec 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionData.java @@ -16,18 +16,116 @@ */ package com.djrapitops.plan.extension.implementation.results; +import java.util.*; + /** - * Represents a data-point given by a Provider method of a DataExtension. + * Represents data of a single extension about a server. * * @author Rsl1122 */ -public interface ExtensionData { +public class ExtensionData implements Comparable { - /** - * Get Descriptive information about the data point. - * - * @return a {@link ExtensionDescriptive}. - */ - ExtensionDescriptive getDescriptive(); + private final int pluginID; -} + private ExtensionInformation extensionInformation; + + private Map tabs; + + private ExtensionData(int pluginID) { + this.pluginID = pluginID; + + tabs = new HashMap<>(); + } + + public int getPluginID() { + return pluginID; + } + + public ExtensionInformation getExtensionInformation() { + return extensionInformation; + } + + public boolean hasOnlyGenericTab() { + return tabs.size() == 1 && tabs.containsKey(""); + } + + public boolean doesNeedWiderSpace() { + for (ExtensionTabData tab : tabs.values()) { + for (ExtensionTableData table : tab.getTableData()) { + if (table.isWideTable()) return true; + } + } + return false; + } + + public List getTabs() { + List tabList = new ArrayList<>(tabs.values()); + Collections.sort(tabList); + return tabList; + } + + @Override + public int compareTo(ExtensionData o) { + return String.CASE_INSENSITIVE_ORDER.compare(this.extensionInformation.getPluginName(), o.extensionInformation.getPluginName()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ExtensionData)) return false; + ExtensionData that = (ExtensionData) o; + return pluginID == that.pluginID && + Objects.equals(extensionInformation, that.extensionInformation) && + Objects.equals(tabs, that.tabs); + } + + @Override + public int hashCode() { + return Objects.hash(pluginID, extensionInformation, tabs); + } + + public static class Factory { + + private final ExtensionData data; + + public Factory(int pluginId) { + data = new ExtensionData(pluginId); + } + + public Factory combine(Factory with) { + if (with != null) { + for (ExtensionTabData tab : with.build().getTabs()) { + Optional found = getTab(tab.getTabInformation().getTabName()); + if (found.isPresent()) { + found.get().combine(tab); + } else { + addTab(tab); + } + } + } + return this; + } + + public Factory setInformation(ExtensionInformation information) { + if (information.getId() != data.pluginID) { + throw new IllegalArgumentException("ID mismatch, wanted id: " + data.pluginID + " but got " + information); + } + data.extensionInformation = information; + return this; + } + + public Factory addTab(ExtensionTabData tab) { + data.tabs.put(tab.getTabInformation().getTabName(), tab); + return this; + } + + public Optional getTab(String tabName) { + return Optional.ofNullable(data.tabs.get(tabName)); + } + + public ExtensionData build() { + return data; + } + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java index 81e266b9b..7a8ded63c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionDoubleData.java @@ -16,14 +16,14 @@ */ package com.djrapitops.plan.extension.implementation.results; -import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatter; /** * Represents double data returned by a DoubleProvider or PercentageProvider method. * * @author Rsl1122 */ -public class ExtensionDoubleData implements ExtensionData { +public class ExtensionDoubleData implements DescribedExtensionData { private ExtensionDescriptive descriptive; private double value; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java index a8e784325..c987886f5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionNumberData.java @@ -16,15 +16,15 @@ */ package com.djrapitops.plan.extension.implementation.results; +import com.djrapitops.plan.delivery.formatting.Formatter; import com.djrapitops.plan.extension.FormatType; -import com.djrapitops.plan.utilities.formatting.Formatter; /** * Represents double data returned by a DoubleProvider or PercentageProvider method. * * @author Rsl1122 */ -public class ExtensionNumberData implements ExtensionData { +public class ExtensionNumberData implements DescribedExtensionData { private final ExtensionDescriptive descriptive; private final FormatType formatType; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionStringData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionStringData.java index c2e3274f0..065b6742b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionStringData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionStringData.java @@ -16,19 +16,18 @@ */ package com.djrapitops.plan.extension.implementation.results; -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.utilities.html.Html; +import com.djrapitops.plan.delivery.rendering.html.Html; /** * Represents double data returned by a DoubleProvider or PercentageProvider method. * * @author Rsl1122 */ -public class ExtensionStringData implements ExtensionData { +public class ExtensionStringData implements DescribedExtensionData { private final ExtensionDescriptive descriptive; private final boolean playerName; - private final String value; + private String value; public ExtensionStringData(ExtensionDescriptive descriptive, boolean playerName, String value) { this.descriptive = descriptive; @@ -42,6 +41,11 @@ public class ExtensionStringData implements ExtensionData { public String getFormattedValue() { String withColors = Html.swapColorCodesToSpan(value); - return !playerName ? withColors : Html.LINK.parse(PlanAPI.getInstance().getPlayerInspectPageLink(value), withColors); + return !playerName ? withColors : Html.LINK.parse("../player/" + value, withColors); + } + + ExtensionStringData concatenate(ExtensionStringData other) { + value += ", " + other.value; + return this; } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java index b46624713..5a3e3c534 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTabData.java @@ -131,11 +131,11 @@ public class ExtensionTabData implements Comparable { } private void createOrderingList() { - booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - doubleData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - percentageData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - numberData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); - stringData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add); + booleanData.values().stream().map(DescribedExtensionData::getDescriptive).forEach(descriptives::add); + doubleData.values().stream().map(DescribedExtensionData::getDescriptive).forEach(descriptives::add); + percentageData.values().stream().map(DescribedExtensionData::getDescriptive).forEach(descriptives::add); + numberData.values().stream().map(DescribedExtensionData::getDescriptive).forEach(descriptives::add); + stringData.values().stream().map(DescribedExtensionData::getDescriptive).forEach(descriptives::add); order = descriptives.stream().sorted() .map(ExtensionDescriptive::getName) @@ -176,6 +176,13 @@ public class ExtensionTabData implements Comparable { return this; } + public Factory putGroupData(ExtensionStringData extensionStringData) { + String name = extensionStringData.getDescriptive().getName(); + ExtensionStringData previous = data.stringData.get(name); + data.stringData.put(name, previous != null ? previous.concatenate(extensionStringData) : extensionStringData); + return this; + } + public Factory putTableData(ExtensionTableData extensionTableData) { data.tableData.add(extensionTableData); return this; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java index 99ce47816..7d830ea35 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/ExtensionTableData.java @@ -55,7 +55,7 @@ public class ExtensionTableData implements Comparable { if (rows.size() > 50) { htmlTable.useJqueryDataTables(); // Use a jQuery data table since there are a lot of rows. } else { - String colorName = com.djrapitops.plan.utilities.html.icon.Color.getByName(tableColor.name()).orElse(com.djrapitops.plan.utilities.html.icon.Color.NONE).getHtmlClass() + String colorName = com.djrapitops.plan.delivery.rendering.html.icon.Color.getByName(tableColor.name()).orElse(com.djrapitops.plan.delivery.rendering.html.icon.Color.NONE).getHtmlClass() .replace("col-", ""); // TODO after PluginData deprecation, change this thing htmlTable.setColor(colorName); } @@ -75,7 +75,7 @@ public class ExtensionTableData implements Comparable { if (column == null) { break; } - header.add(com.djrapitops.plan.utilities.html.icon.Icon.fromExtensionIcon(icons[i]).toHtml() + ' ' + column); + header.add(com.djrapitops.plan.delivery.rendering.html.icon.Icon.fromExtensionIcon(icons[i]).toHtml() + ' ' + column); } return header.toArray(new String[0]); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java deleted file mode 100644 index dba53f4a7..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/player/ExtensionPlayerData.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.extension.implementation.results.player; - -import com.djrapitops.plan.extension.implementation.results.ExtensionInformation; -import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; - -import java.util.*; - -/** - * Represents data of a single extension about a player. - * - * @author Rsl1122 - */ -public class ExtensionPlayerData implements Comparable { - - private final int pluginID; - - private ExtensionInformation extensionInformation; - - private List tabs; - - private ExtensionPlayerData(int pluginID) { - this.pluginID = pluginID; - - tabs = new ArrayList<>(); - } - - public int getPluginID() { - return pluginID; - } - - public ExtensionInformation getExtensionInformation() { - return extensionInformation; - } - - public boolean hasOnlyGenericTab() { - return tabs.size() == 1 && tabs.get(0).getTabInformation().getTabName().isEmpty(); - } - - public List getTabs() { - return tabs; - } - - @Override - public int compareTo(ExtensionPlayerData o) { - return String.CASE_INSENSITIVE_ORDER.compare(this.extensionInformation.getPluginName(), o.extensionInformation.getPluginName()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ExtensionPlayerData)) return false; - ExtensionPlayerData that = (ExtensionPlayerData) o; - return pluginID == that.pluginID && - extensionInformation.equals(that.extensionInformation) && - tabs.equals(that.tabs); - } - - @Override - public int hashCode() { - return Objects.hash(pluginID, extensionInformation, tabs); - } - - public static class Factory { - - private final ExtensionPlayerData data; - - public Factory(int pluginId) { - data = new ExtensionPlayerData(pluginId); - } - - public Factory setInformation(ExtensionInformation information) { - if (information.getId() != data.pluginID) { - throw new IllegalArgumentException("ID mismatch, wanted id: " + data.pluginID + " but got " + information); - } - data.extensionInformation = information; - return this; - } - - public Factory addTab(ExtensionTabData tab) { - data.tabs.add(tab); - return this; - } - - public ExtensionPlayerData build() { - Collections.sort(data.tabs); - return data; - } - - public Factory combine(Factory with) { - if (with != null) { - for (ExtensionTabData tab : with.build().getTabs()) { - Optional found = getTab(tab.getTabInformation().getTabName()); - if (found.isPresent()) { - found.get().combine(tab); - } else { - addTab(tab); - } - } - } - return this; - } - - public Optional getTab(String tabName) { - for (ExtensionTabData tab : data.tabs) { - if (tabName.equals(tab.getTabInformation().getTabName())) { - return Optional.of(tab); - } - } - return Optional.empty(); - } - - - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/server/ExtensionServerData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/server/ExtensionServerData.java deleted file mode 100644 index 80a974b8c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/results/server/ExtensionServerData.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.extension.implementation.results.server; - -import com.djrapitops.plan.extension.implementation.results.ExtensionInformation; -import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; - -import java.util.*; - -/** - * Represents data of a single extension about a server. - * - * @author Rsl1122 - */ -public class ExtensionServerData implements Comparable { - - private final int pluginID; - - private ExtensionInformation extensionInformation; - - private Map tabs; - - private ExtensionServerData(int pluginID) { - this.pluginID = pluginID; - - tabs = new HashMap<>(); - } - - public int getPluginID() { - return pluginID; - } - - public ExtensionInformation getExtensionInformation() { - return extensionInformation; - } - - public boolean hasOnlyGenericTab() { - return tabs.size() == 1 && tabs.containsKey(""); - } - - public List getTabs() { - List tabList = new ArrayList<>(tabs.values()); - Collections.sort(tabList); - return tabList; - } - - @Override - public int compareTo(ExtensionServerData o) { - return String.CASE_INSENSITIVE_ORDER.compare(this.extensionInformation.getPluginName(), o.extensionInformation.getPluginName()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof ExtensionServerData)) return false; - ExtensionServerData that = (ExtensionServerData) o; - return pluginID == that.pluginID && - Objects.equals(extensionInformation, that.extensionInformation) && - Objects.equals(tabs, that.tabs); - } - - @Override - public int hashCode() { - return Objects.hash(pluginID, extensionInformation, tabs); - } - - public static class Factory { - - private final ExtensionServerData data; - - public Factory(int pluginId) { - data = new ExtensionServerData(pluginId); - } - - public Factory combine(Factory with) { - if (with != null) { - for (ExtensionTabData tab : with.build().getTabs()) { - Optional found = getTab(tab.getTabInformation().getTabName()); - if (found.isPresent()) { - found.get().combine(tab); - } else { - addTab(tab); - } - } - } - return this; - } - - public Factory setInformation(ExtensionInformation information) { - if (information.getId() != data.pluginID) { - throw new IllegalArgumentException("ID mismatch, wanted id: " + data.pluginID + " but got " + information); - } - data.extensionInformation = information; - return this; - } - - public Factory addTab(ExtensionTabData tab) { - data.tabs.put(tab.getTabInformation().getTabName(), tab); - return this; - } - - public Optional getTab(String tabName) { - return Optional.ofNullable(data.tabs.get(tabName)); - } - - public ExtensionServerData build() { - return data; - } - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateBooleansQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateBooleansQuery.java index a801f77cf..ffd0fae54 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateBooleansQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateBooleansQuery.java @@ -16,29 +16,28 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.*; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive; import com.djrapitops.plan.extension.implementation.results.ExtensionDoubleData; import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Query aggregated boolean values from player value table. @@ -55,7 +54,7 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*; * * @author Rsl1122 */ -public class ExtensionAggregateBooleansQuery implements Query> { +public class ExtensionAggregateBooleansQuery implements Query> { private final UUID serverUUID; @@ -64,7 +63,7 @@ public class ExtensionAggregateBooleansQuery implements Query executeQuery(SQLDB db) { + public Map executeQuery(SQLDB db) { String selectTrueBooleans = SELECT + ExtensionPlayerValueTable.PROVIDER_ID + ",COUNT(1) as positive" + @@ -107,7 +106,7 @@ public class ExtensionAggregateBooleansQuery implements Query>(sql, 1000) { + return db.query(new QueryStatement>(sql, 1000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setBoolean(1, true); // selectTrueBooleans parameter @@ -116,31 +115,41 @@ public class ExtensionAggregateBooleansQuery implements Query processResults(ResultSet set) throws SQLException { - Map> tabDataByPluginID = extractTabDataByPluginID(set); - return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID); + public Map processResults(ResultSet set) throws SQLException { + return extractTabDataByPluginID(set).toExtensionDataByPluginID(); } }); } - private Map> extractTabDataByPluginID(ResultSet set) throws SQLException { - Map> tabDataByPluginID = new HashMap<>(); + private QueriedTabData extractTabDataByPluginID(ResultSet set) throws SQLException { + QueriedTabData tabData = new QueriedTabData(); while (set.next()) { int pluginID = set.getInt("plugin_id"); - Map tabData = tabDataByPluginID.getOrDefault(pluginID, new HashMap<>()); - String tabName = Optional.ofNullable(set.getString("tab_name")).orElse(""); - ExtensionTabData.Factory inMap = tabData.get(tabName); - ExtensionTabData.Factory extensionTab = inMap != null ? inMap : extractTab(tabName, set, tabData); + ExtensionTabData.Factory extensionTab = tabData.getTab(pluginID, tabName, () -> extractTabInformation(tabName, set)); ExtensionDescriptive extensionDescriptive = extractDescriptive(set); extractAndPutDataTo(extensionTab, extensionDescriptive, set); - - tabData.put(tabName, extensionTab); - tabDataByPluginID.put(pluginID, tabData); } - return tabDataByPluginID; + return tabData; + } + + private TabInformation extractTabInformation(String tabName, ResultSet set) throws SQLException { + Optional tabPriority = Optional.of(set.getInt("tab_priority")); + if (set.wasNull()) { + tabPriority = Optional.empty(); + } + Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); + + Icon tabIcon = extractTabIcon(set); + + return new TabInformation( + tabName, + tabIcon, + elementOrder.orElse(ElementOrder.values()), + tabPriority.orElse(100) + ); } private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException { @@ -169,23 +178,6 @@ public class ExtensionAggregateBooleansQuery implements Query tabData) throws SQLException { - Optional tabPriority = Optional.of(set.getInt("tab_priority")); - if (set.wasNull()) { - tabPriority = Optional.empty(); - } - Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); - - Icon tabIcon = extractTabIcon(set); - - return tabData.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation( - tabName, - tabIcon, - elementOrder.orElse(ElementOrder.values()), - tabPriority.orElse(100) - ))); - } - private Icon extractTabIcon(ResultSet set) throws SQLException { Optional iconName = Optional.ofNullable(set.getString("tab_icon_name")); if (iconName.isPresent()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateDoublesQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateDoublesQuery.java index db5dcfdc9..a1fb6519f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateDoublesQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateDoublesQuery.java @@ -16,29 +16,28 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.*; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive; import com.djrapitops.plan.extension.implementation.results.ExtensionDoubleData; import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Query aggregated boolean values from player value table. @@ -55,7 +54,7 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*; * * @author Rsl1122 */ -public class ExtensionAggregateDoublesQuery implements Query> { +public class ExtensionAggregateDoublesQuery implements Query> { private final UUID serverUUID; @@ -64,7 +63,7 @@ public class ExtensionAggregateDoublesQuery implements Query executeQuery(SQLDB db) { + public Map executeQuery(SQLDB db) { String selectDoubleAverage = SELECT + ExtensionPlayerValueTable.PROVIDER_ID + ",AVG(" + ExtensionPlayerValueTable.DOUBLE_VALUE + ") as average" + @@ -107,7 +106,7 @@ public class ExtensionAggregateDoublesQuery implements Query>(sql, 1000) { + return db.query(new QueryStatement>(sql, 1000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); @@ -115,31 +114,41 @@ public class ExtensionAggregateDoublesQuery implements Query processResults(ResultSet set) throws SQLException { - Map> tabDataByPluginID = extractTabDataByPluginID(set); - return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID); + public Map processResults(ResultSet set) throws SQLException { + return extractTabDataByPluginID(set).toExtensionDataByPluginID(); } }); } - private Map> extractTabDataByPluginID(ResultSet set) throws SQLException { - Map> tabDataByPluginID = new HashMap<>(); + private QueriedTabData extractTabDataByPluginID(ResultSet set) throws SQLException { + QueriedTabData tabData = new QueriedTabData(); while (set.next()) { int pluginID = set.getInt("plugin_id"); - Map tabData = tabDataByPluginID.getOrDefault(pluginID, new HashMap<>()); - String tabName = Optional.ofNullable(set.getString("tab_name")).orElse(""); - ExtensionTabData.Factory inMap = tabData.get(tabName); - ExtensionTabData.Factory extensionTab = inMap != null ? inMap : extractTab(tabName, set, tabData); + ExtensionTabData.Factory extensionTab = tabData.getTab(pluginID, tabName, () -> extractTabInformation(tabName, set)); ExtensionDescriptive extensionDescriptive = extractDescriptive(set); extractAndPutDataTo(extensionTab, extensionDescriptive, set); - - tabData.put(tabName, extensionTab); - tabDataByPluginID.put(pluginID, tabData); } - return tabDataByPluginID; + return tabData; + } + + private TabInformation extractTabInformation(String tabName, ResultSet set) throws SQLException { + Optional tabPriority = Optional.of(set.getInt("tab_priority")); + if (set.wasNull()) { + tabPriority = Optional.empty(); + } + Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); + + Icon tabIcon = extractTabIcon(set); + + return new TabInformation( + tabName, + tabIcon, + elementOrder.orElse(ElementOrder.values()), + tabPriority.orElse(100) + ); } private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException { @@ -165,23 +174,6 @@ public class ExtensionAggregateDoublesQuery implements Query tabData) throws SQLException { - Optional tabPriority = Optional.of(set.getInt("tab_priority")); - if (set.wasNull()) { - tabPriority = Optional.empty(); - } - Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); - - Icon tabIcon = extractTabIcon(set); - - return tabData.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation( - tabName, - tabIcon, - elementOrder.orElse(ElementOrder.values()), - tabPriority.orElse(100) - ))); - } - private Icon extractTabIcon(ResultSet set) throws SQLException { Optional iconName = Optional.ofNullable(set.getString("tab_icon_name")); if (iconName.isPresent()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateGroupsQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateGroupsQuery.java new file mode 100644 index 000000000..85d78781f --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateGroupsQuery.java @@ -0,0 +1,153 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.implementation.storage.queries; + +import com.djrapitops.plan.extension.ElementOrder; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Family; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; +import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.extension.table.TableAccessor; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Query aggregated Group values from groups table. + *

    + * Returns Map: PluginID - ExtensionServerData.Factory. + * + * @author Rsl1122 + */ +public class ExtensionAggregateGroupsQuery implements Query> { + + private final UUID serverUUID; + + public ExtensionAggregateGroupsQuery(UUID serverUUID) { + this.serverUUID = serverUUID; + } + + @Override + public Map executeQuery(SQLDB db) { + String selectGroupCounts = SELECT + + ExtensionGroupsTable.PROVIDER_ID + ',' + + ExtensionGroupsTable.GROUP_NAME + ',' + + "COUNT(1) as count" + + FROM + ExtensionGroupsTable.TABLE_NAME + + GROUP_BY + ExtensionGroupsTable.GROUP_NAME + ',' + ExtensionGroupsTable.PROVIDER_ID; + + String sql = SELECT + + "b1." + ExtensionGroupsTable.GROUP_NAME + " as group_name," + + "b1.count as count," + + "p1." + ExtensionProviderTable.ID + " as table_id," + + "p1." + ExtensionProviderTable.PLUGIN_ID + " as plugin_id," + + "p1." + ExtensionProviderTable.PROVIDER_NAME + " as table_name," + + "p1." + ExtensionProviderTable.TEXT + " as col_1_name," + + "t1." + ExtensionTabTable.TAB_NAME + " as tab_name," + + "t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," + + "t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," + + "i1." + ExtensionIconTable.ICON_NAME + " as i1_name," + + "i1." + ExtensionIconTable.FAMILY + " as i1_family," + + "i1." + ExtensionIconTable.COLOR + " as table_color," + + "i2." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," + + "i2." + ExtensionIconTable.FAMILY + " as tab_icon_family," + + "i2." + ExtensionIconTable.COLOR + " as tab_icon_color" + + FROM + '(' + selectGroupCounts + ") b1" + + INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " p1 on p1." + ExtensionProviderTable.ID + "=b1." + ExtensionGroupsTable.PROVIDER_ID + + INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " e1 on p1." + ExtensionProviderTable.PLUGIN_ID + "=e1." + ExtensionPluginTable.ID + + LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionProviderTable.TAB_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionProviderTable.ICON_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTabTable.ICON_ID + + WHERE + ExtensionPluginTable.SERVER_UUID + "=?" + + AND + "p1." + ExtensionProviderTable.HIDDEN + "=?" + + ORDER_BY + "table_id ASC, group_name ASC"; + + return db.query(new QueryStatement>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setBoolean(2, false); // Don't select hidden values + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + return extractTables(set).toQueriedTabs().toExtensionDataByPluginID(); + } + }); + } + + private QueriedTables extractTables(ResultSet set) throws SQLException { + QueriedTables tables = new QueriedTables(); + + while (set.next()) { + int pluginID = set.getInt(ExtensionProviderTable.PLUGIN_ID); + int tableID = set.getInt(ExtensionPlayerTableValueTable.TABLE_ID); + + if (!tables.contains(pluginID, tableID)) { + tables.put(pluginID, tableID, extractTable(set)); + } + tables.addRow(pluginID, tableID, set.getString("group_name"), set.getInt("count")); + } + return tables; + } + + private Table.Factory extractTable(ResultSet set) throws SQLException { + Table.Factory table = Table.builder(); + + extractColumns(set, table); + + TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE)); + TableAccessor.setTableName(table, set.getString("table_name")); + TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse("")); + TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100)); + TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values())); + TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon")); + return table; + } + + private void extractColumns(ResultSet set, Table.Factory table) throws SQLException { + String col1 = set.getString(ExtensionTableProviderTable.COL_1); + if (col1 != null) { + table.columnOne(col1, extractIcon(set, "i1")); + } + + table.columnTwo("Players", Icon.called("user").build()); + } + + private Icon extractIcon(ResultSet set, String iconColumnName) throws SQLException { + String iconName = set.getString(iconColumnName + "_name"); + if (iconName == null) { + return null; + } + return new Icon( + Family.getByName(set.getString(iconColumnName + "_family")).orElse(Family.SOLID), + iconName, + Color.NONE + ); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java index 9ff611889..03cc456ba 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregateNumbersQuery.java @@ -16,30 +16,29 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.*; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive; import com.djrapitops.plan.extension.implementation.results.ExtensionNumberData; import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Query aggregated boolean values from player value table. @@ -56,7 +55,7 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*; * * @author Rsl1122 */ -public class ExtensionAggregateNumbersQuery implements Query> { +public class ExtensionAggregateNumbersQuery implements Query> { private final UUID serverUUID; @@ -65,7 +64,7 @@ public class ExtensionAggregateNumbersQuery implements Query executeQuery(SQLDB db) { + public Map executeQuery(SQLDB db) { String selectNumberAverage = SELECT + ExtensionPlayerValueTable.PROVIDER_ID + ",AVG(" + ExtensionPlayerValueTable.LONG_VALUE + ") as average" + @@ -111,7 +110,7 @@ public class ExtensionAggregateNumbersQuery implements Query>(sql, 1000) { + return db.query(new QueryStatement>(sql, 1000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); @@ -121,31 +120,41 @@ public class ExtensionAggregateNumbersQuery implements Query processResults(ResultSet set) throws SQLException { - Map> tabDataByPluginID = extractTabDataByPluginID(set); - return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID); + public Map processResults(ResultSet set) throws SQLException { + return extractTabDataByPluginID(set).toExtensionDataByPluginID(); } }); } - private Map> extractTabDataByPluginID(ResultSet set) throws SQLException { - Map> tabDataByPluginID = new HashMap<>(); + private QueriedTabData extractTabDataByPluginID(ResultSet set) throws SQLException { + QueriedTabData tabData = new QueriedTabData(); while (set.next()) { int pluginID = set.getInt("plugin_id"); - Map tabData = tabDataByPluginID.getOrDefault(pluginID, new HashMap<>()); - String tabName = Optional.ofNullable(set.getString("tab_name")).orElse(""); - ExtensionTabData.Factory inMap = tabData.get(tabName); - ExtensionTabData.Factory extensionTab = inMap != null ? inMap : extractTab(tabName, set, tabData); + ExtensionTabData.Factory extensionTab = tabData.getTab(pluginID, tabName, () -> extractTabInformation(tabName, set)); ExtensionDescriptive extensionDescriptive = extractDescriptive(set); extractAndPutDataTo(extensionTab, extensionDescriptive, set); - - tabData.put(tabName, extensionTab); - tabDataByPluginID.put(pluginID, tabData); } - return tabDataByPluginID; + return tabData; + } + + private TabInformation extractTabInformation(String tabName, ResultSet set) throws SQLException { + Optional tabPriority = Optional.of(set.getInt("tab_priority")); + if (set.wasNull()) { + tabPriority = Optional.empty(); + } + Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); + + Icon tabIcon = extractTabIcon(set); + + return new TabInformation( + tabName, + tabIcon, + elementOrder.orElse(ElementOrder.values()), + tabPriority.orElse(100) + ); } private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException { @@ -172,23 +181,6 @@ public class ExtensionAggregateNumbersQuery implements Query tabData) throws SQLException { - Optional tabPriority = Optional.of(set.getInt("tab_priority")); - if (set.wasNull()) { - tabPriority = Optional.empty(); - } - Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); - - Icon tabIcon = extractTabIcon(set); - - return tabData.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation( - tabName, - tabIcon, - elementOrder.orElse(ElementOrder.values()), - tabPriority.orElse(100) - ))); - } - private Icon extractTabIcon(ResultSet set) throws SQLException { Optional iconName = Optional.ofNullable(set.getString("tab_icon_name")); if (iconName.isPresent()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregatePercentagesQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregatePercentagesQuery.java index ebac42ed3..ef115d465 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregatePercentagesQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionAggregatePercentagesQuery.java @@ -16,29 +16,28 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.*; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive; import com.djrapitops.plan.extension.implementation.results.ExtensionDoubleData; import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Query aggregated boolean values from player value table. @@ -55,7 +54,7 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*; * * @author Rsl1122 */ -public class ExtensionAggregatePercentagesQuery implements Query> { +public class ExtensionAggregatePercentagesQuery implements Query> { private final UUID serverUUID; @@ -64,7 +63,7 @@ public class ExtensionAggregatePercentagesQuery implements Query executeQuery(SQLDB db) { + public Map executeQuery(SQLDB db) { String selectPercentageAverage = SELECT + ExtensionPlayerValueTable.PROVIDER_ID + ",AVG(" + ExtensionPlayerValueTable.PERCENTAGE_VALUE + ") as average" + @@ -98,7 +97,7 @@ public class ExtensionAggregatePercentagesQuery implements Query>(sql, 1000) { + return db.query(new QueryStatement>(sql, 1000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); @@ -106,31 +105,41 @@ public class ExtensionAggregatePercentagesQuery implements Query processResults(ResultSet set) throws SQLException { - Map> tabDataByPluginID = extractTabDataByPluginID(set); - return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID); + public Map processResults(ResultSet set) throws SQLException { + return extractTabDataByPluginID(set).toExtensionDataByPluginID(); } }); } - private Map> extractTabDataByPluginID(ResultSet set) throws SQLException { - Map> tabDataByPluginID = new HashMap<>(); + private QueriedTabData extractTabDataByPluginID(ResultSet set) throws SQLException { + QueriedTabData tabData = new QueriedTabData(); while (set.next()) { int pluginID = set.getInt("plugin_id"); - Map tabData = tabDataByPluginID.getOrDefault(pluginID, new HashMap<>()); - String tabName = Optional.ofNullable(set.getString("tab_name")).orElse(""); - ExtensionTabData.Factory inMap = tabData.get(tabName); - ExtensionTabData.Factory extensionTab = inMap != null ? inMap : extractTab(tabName, set, tabData); + ExtensionTabData.Factory extensionTab = tabData.getTab(pluginID, tabName, () -> extractTabInformation(tabName, set)); ExtensionDescriptive extensionDescriptive = extractDescriptive(set); extractAndPutDataTo(extensionTab, extensionDescriptive, set); - - tabData.put(tabName, extensionTab); - tabDataByPluginID.put(pluginID, tabData); } - return tabDataByPluginID; + return tabData; + } + + private TabInformation extractTabInformation(String tabName, ResultSet set) throws SQLException { + Optional tabPriority = Optional.of(set.getInt("tab_priority")); + if (set.wasNull()) { + tabPriority = Optional.empty(); + } + Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); + + Icon tabIcon = extractTabIcon(set); + + return new TabInformation( + tabName, + tabIcon, + elementOrder.orElse(ElementOrder.values()), + tabPriority.orElse(100) + ); } private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException { @@ -155,23 +164,6 @@ public class ExtensionAggregatePercentagesQuery implements Query tabData) throws SQLException { - Optional tabPriority = Optional.of(set.getInt("tab_priority")); - if (set.wasNull()) { - tabPriority = Optional.empty(); - } - Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); - - Icon tabIcon = extractTabIcon(set); - - return tabData.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation( - tabName, - tabIcon, - elementOrder.orElse(ElementOrder.values()), - tabPriority.orElse(100) - ))); - } - private Icon extractTabIcon(ResultSet set) throws SQLException { Optional iconName = Optional.ofNullable(set.getString("tab_icon_name")); if (iconName.isPresent()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionInformationQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionInformationQueries.java index cf2969a25..56e758940 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionInformationQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionInformationQueries.java @@ -16,22 +16,22 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.results.ExtensionInformation; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Queries for information about DataExtensions stored in the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerDataQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerDataQuery.java index d1140ab49..f0a61beac 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerDataQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerDataQuery.java @@ -16,13 +16,6 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.icon.Color; @@ -30,32 +23,38 @@ import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.TabInformation; import com.djrapitops.plan.extension.implementation.results.*; -import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerValueTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** - * Query all ExtensionPlayerData by Server UUIDs. + * Query all ExtensionData by Server UUIDs. *

    - * Returns Map: Server UUID - List of ExtensionPlayerData. + * Returns Map: Server UUID - List of ExtensionData. *

    * How it is done: * - Two queries are run, one that fetches all extensions and one that fetches all data of the player. * - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data * - (Tab Name can be empty.) - * - Multi-map is sorted into ExtensionPlayerData objects by PluginID, one per ID - * - This map is sorted into final Map: Server UUID - List of ExtensionPlayerData at the highest level. + * - Multi-map is sorted into ExtensionData objects by PluginID, one per ID + * - This map is sorted into final Map: Server UUID - List of ExtensionData at the highest level. *

    * There are multiple data extraction methods to make extracting the value query easier. * * @author Rsl1122 */ -public class ExtensionPlayerDataQuery implements Query>> { +public class ExtensionPlayerDataQuery implements Query>> { private final UUID playerUUID; @@ -64,25 +63,25 @@ public class ExtensionPlayerDataQuery implements Query> executeQuery(SQLDB db) { + public Map> executeQuery(SQLDB db) { Map> extensionsByServerUUID = db.query(ExtensionInformationQueries.allExtensions()); - Map extensionDataByPluginID = db.query(fetchIncompletePlayerDataByPluginID()); + Map extensionDataByPluginID = db.query(fetchIncompletePlayerDataByPluginID()); - Map tableDataByPluginID = db.query(new ExtensionPlayerTablesQuery(playerUUID)); - combine(extensionDataByPluginID, tableDataByPluginID); + combine(extensionDataByPluginID, db.query(new ExtensionPlayerTablesQuery(playerUUID))); + combine(extensionDataByPluginID, db.query(new ExtensionPlayerGroupsQuery(playerUUID))); return flatMapByServerUUID(extensionsByServerUUID, extensionDataByPluginID); } private void combine( - Map extensionDataByPluginID, - Map aggregates + Map extensionDataByPluginID, + Map aggregates ) { - for (Map.Entry entry : aggregates.entrySet()) { + for (Map.Entry entry : aggregates.entrySet()) { Integer pluginID = entry.getKey(); - ExtensionPlayerData.Factory data = entry.getValue(); + ExtensionData.Factory data = entry.getValue(); - ExtensionPlayerData.Factory found = extensionDataByPluginID.get(pluginID); + ExtensionData.Factory found = extensionDataByPluginID.get(pluginID); if (found == null) { extensionDataByPluginID.put(pluginID, data); } else { @@ -91,17 +90,17 @@ public class ExtensionPlayerDataQuery implements Query> flatMapByServerUUID(Map> extensionsByServerUUID, Map extensionDataByPluginID) { - Map> extensionDataByServerUUID = new HashMap<>(); + private Map> flatMapByServerUUID(Map> extensionsByServerUUID, Map extensionDataByPluginID) { + Map> extensionDataByServerUUID = new HashMap<>(); for (Map.Entry> entry : extensionsByServerUUID.entrySet()) { UUID serverUUID = entry.getKey(); for (ExtensionInformation extensionInformation : entry.getValue()) { - ExtensionPlayerData.Factory data = extensionDataByPluginID.get(extensionInformation.getId()); + ExtensionData.Factory data = extensionDataByPluginID.get(extensionInformation.getId()); if (data == null) { continue; } - List list = extensionDataByServerUUID.getOrDefault(serverUUID, new ArrayList<>()); + List list = extensionDataByServerUUID.getOrDefault(serverUUID, new ArrayList<>()); list.add(data.setInformation(extensionInformation).build()); extensionDataByServerUUID.put(serverUUID, list); } @@ -109,7 +108,7 @@ public class ExtensionPlayerDataQuery implements Query> fetchIncompletePlayerDataByPluginID() { + private Query> fetchIncompletePlayerDataByPluginID() { String sql = SELECT + "v1." + ExtensionPlayerValueTable.BOOLEAN_VALUE + " as boolean_value," + "v1." + ExtensionPlayerValueTable.DOUBLE_VALUE + " as double_value," + @@ -140,7 +139,7 @@ public class ExtensionPlayerDataQuery implements Query>(sql, 1000) { + return new QueryStatement>(sql, 1000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, playerUUID.toString()); @@ -148,44 +147,41 @@ public class ExtensionPlayerDataQuery implements Query processResults(ResultSet set) throws SQLException { - Map> tabDataByPluginID = extractTabDataByPluginID(set); - return flatMapToPlayerData(tabDataByPluginID); + public Map processResults(ResultSet set) throws SQLException { + return extractTabDataByPluginID(set).toExtensionDataByPluginID(); } }; } - private Map flatMapToPlayerData(Map> tabDataByPluginID) { - Map dataByPluginID = new HashMap<>(); - for (Map.Entry> entry : tabDataByPluginID.entrySet()) { - Integer pluginID = entry.getKey(); - ExtensionPlayerData.Factory data = dataByPluginID.getOrDefault(pluginID, new ExtensionPlayerData.Factory(pluginID)); - for (ExtensionTabData.Factory tabData : entry.getValue().values()) { - data.addTab(tabData.build()); - } - dataByPluginID.put(pluginID, data); - } - return dataByPluginID; - } - - private Map> extractTabDataByPluginID(ResultSet set) throws SQLException { - Map> tabDataByPluginID = new HashMap<>(); + private QueriedTabData extractTabDataByPluginID(ResultSet set) throws SQLException { + QueriedTabData tabData = new QueriedTabData(); while (set.next()) { int pluginID = set.getInt("plugin_id"); - Map tabData = tabDataByPluginID.getOrDefault(pluginID, new HashMap<>()); - String tabName = Optional.ofNullable(set.getString("tab_name")).orElse(""); - ExtensionTabData.Factory inMap = tabData.get(tabName); - ExtensionTabData.Factory extensionTab = inMap != null ? inMap : extractTab(tabName, set, tabData); + ExtensionTabData.Factory extensionTab = tabData.getTab(pluginID, tabName, () -> extractTabInformation(tabName, set)); ExtensionDescriptive extensionDescriptive = extractDescriptive(set); extractAndPutDataTo(extensionTab, extensionDescriptive, set); - - tabData.put(tabName, extensionTab); - tabDataByPluginID.put(pluginID, tabData); } - return tabDataByPluginID; + return tabData; + } + + private TabInformation extractTabInformation(String tabName, ResultSet set) throws SQLException { + Optional tabPriority = Optional.of(set.getInt("tab_priority")); + if (set.wasNull()) { + tabPriority = Optional.empty(); + } + Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); + + Icon tabIcon = extractTabIcon(set); + + return new TabInformation( + tabName, + tabIcon, + elementOrder.orElse(ElementOrder.values()), + tabPriority.orElse(100) + ); } private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException { @@ -235,23 +231,6 @@ public class ExtensionPlayerDataQuery implements Query tabData) throws SQLException { - Optional tabPriority = Optional.of(set.getInt("tab_priority")); - if (set.wasNull()) { - tabPriority = Optional.empty(); - } - Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); - - Icon tabIcon = extractTabIcon(set); - - return tabData.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation( - tabName, - tabIcon, - elementOrder.orElse(ElementOrder.values()), - tabPriority.orElse(100) - ))); - } - private Icon extractTabIcon(ResultSet set) throws SQLException { Optional iconName = Optional.ofNullable(set.getString("tab_icon_name")); if (iconName.isPresent()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerGroupsQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerGroupsQuery.java new file mode 100644 index 000000000..ff05c3dc4 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerGroupsQuery.java @@ -0,0 +1,158 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.implementation.storage.queries; + +import com.djrapitops.plan.extension.ElementOrder; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Family; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; +import com.djrapitops.plan.extension.implementation.results.ExtensionDescriptive; +import com.djrapitops.plan.extension.implementation.results.ExtensionStringData; +import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Query player's Groups by Plugin ID inside ExtensionData objects. + *

    + * - Group names are represented as a String value, concatenated to a single one. + * - String values are placed into Tabs they belong to + * - Tabs are placed to plugin they belong to + * - The map is returned + * + * @author Rsl1122 + */ +public class ExtensionPlayerGroupsQuery implements Query> { + + private final UUID playerUUID; + + public ExtensionPlayerGroupsQuery(UUID playerUUID) { + this.playerUUID = playerUUID; + } + + @Override + public Map executeQuery(SQLDB db) { + return db.query(fetchGroupsByPluginID()); + } + + private Query> fetchGroupsByPluginID() { + String sql = SELECT + + "v1." + ExtensionGroupsTable.GROUP_NAME + " as group_name," + + "p1." + ExtensionProviderTable.PLUGIN_ID + " as plugin_id," + + "p1." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," + + "p1." + ExtensionProviderTable.TEXT + " as text," + + "t1." + ExtensionTabTable.TAB_NAME + " as tab_name," + + "t1." + ExtensionTabTable.TAB_PRIORITY + " as tab_priority," + + "t1." + ExtensionTabTable.ELEMENT_ORDER + " as element_order," + + "i1." + ExtensionIconTable.ICON_NAME + " as provider_icon_name," + + "i1." + ExtensionIconTable.FAMILY + " as provider_icon_family," + + "i1." + ExtensionIconTable.COLOR + " as provider_icon_color," + + "i2." + ExtensionIconTable.ICON_NAME + " as tab_icon_name," + + "i2." + ExtensionIconTable.FAMILY + " as tab_icon_family," + + "i2." + ExtensionIconTable.COLOR + " as tab_icon_color" + + FROM + ExtensionGroupsTable.TABLE_NAME + " v1" + + INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " p1 on p1." + ExtensionProviderTable.ID + "=v1." + ExtensionPlayerValueTable.PROVIDER_ID + + LEFT_JOIN + ExtensionTabTable.TABLE_NAME + " t1 on t1." + ExtensionTabTable.ID + "=p1." + ExtensionProviderTable.TAB_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionProviderTable.ICON_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i2 on i2." + ExtensionIconTable.ID + "=p1." + ExtensionTabTable.ICON_ID + + WHERE + ExtensionGroupsTable.USER_UUID + "=?" + + AND + "p1." + ExtensionProviderTable.HIDDEN + "=?" + + ORDER_BY + ExtensionGroupsTable.GROUP_NAME + " ASC"; + + return new QueryStatement>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + statement.setBoolean(2, false); // Don't select hidden values + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + return extractTabDataByPluginID(set).toExtensionDataByPluginID(); + } + }; + } + + private QueriedTabData extractTabDataByPluginID(ResultSet set) throws SQLException { + QueriedTabData tabData = new QueriedTabData(); + + while (set.next()) { + int pluginID = set.getInt("plugin_id"); + String tabName = Optional.ofNullable(set.getString("tab_name")).orElse(""); + ExtensionTabData.Factory extensionTab = tabData.getTab(pluginID, tabName, () -> extractTabInformation(tabName, set)); + + ExtensionDescriptive extensionDescriptive = extractDescriptive(set); + + String groupName = set.getString(ExtensionGroupsTable.GROUP_NAME); + extensionTab.putGroupData(new ExtensionStringData(extensionDescriptive, false, groupName)); + } + return tabData; + } + + private TabInformation extractTabInformation(String tabName, ResultSet set) throws SQLException { + Optional tabPriority = Optional.of(set.getInt("tab_priority")); + if (set.wasNull()) { + tabPriority = Optional.empty(); + } + Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); + + Icon tabIcon = extractTabIcon(set); + + return new TabInformation( + tabName, + tabIcon, + elementOrder.orElse(ElementOrder.values()), + tabPriority.orElse(100) + ); + } + + private ExtensionDescriptive extractDescriptive(ResultSet set) throws SQLException { + String name = set.getString("provider_name"); + String text = set.getString(ExtensionProviderTable.TEXT); + + String iconName = set.getString("provider_icon_name"); + Family family = Family.getByName(set.getString("provider_icon_family")).orElse(Family.SOLID); + Color color = Color.getByName(set.getString("provider_icon_color")).orElse(Color.NONE); + Icon icon = new Icon(family, iconName, color); + + return new ExtensionDescriptive(name, text, null, icon, 0); + } + + private Icon extractTabIcon(ResultSet set) throws SQLException { + Optional iconName = Optional.ofNullable(set.getString("tab_icon_name")); + if (iconName.isPresent()) { + Family iconFamily = Family.getByName(set.getString("tab_icon_family")).orElse(Family.SOLID); + Color iconColor = Color.getByName(set.getString("tab_icon_color")).orElse(Color.NONE); + return new Icon(iconFamily, iconName.get(), iconColor); + } else { + return TabInformation.defaultIcon(); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java index d106195c8..3a96dce92 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionPlayerTablesQuery.java @@ -16,48 +16,45 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; -import com.djrapitops.plan.extension.implementation.TabInformation; -import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; -import com.djrapitops.plan.extension.implementation.results.ExtensionTableData; -import com.djrapitops.plan.extension.implementation.results.player.ExtensionPlayerData; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; import com.djrapitops.plan.extension.table.Table; import com.djrapitops.plan.extension.table.TableAccessor; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerTableValueTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTableProviderTable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Query player tables from tableprovider table. *

    - * Returns Map: PluginID - ExtensionPlayerData.Factory. + * Returns Map: PluginID - ExtensionData.Factory. *

    * How it is done: * - TableProviders are queried and formed into Table.Factory objects sorted into a multi-map: PluginID - TableID - Table.Factory * - Table values are queried and merged into the above multimap * - Data query is sorted into a multi-map: PluginID - Tab Name - Tab Data * - (Tab Name can be empty.) - * - Multi-map is sorted into ExtensionPlayerData objects by PluginID, one per ID + * - Multi-map is sorted into ExtensionData objects by PluginID, one per ID *

    * There are multiple data extraction methods to make extracting the value query easier. * * @author Rsl1122 */ -public class ExtensionPlayerTablesQuery implements Query> { +public class ExtensionPlayerTablesQuery implements Query> { private final UUID playerUUID; @@ -65,63 +62,13 @@ public class ExtensionPlayerTablesQuery implements Query flatMapByPluginID(Map> tabDataByPluginID) { - Map dataByPluginID = new HashMap<>(); - for (Map.Entry> entry : tabDataByPluginID.entrySet()) { - Integer pluginID = entry.getKey(); - ExtensionPlayerData.Factory data = dataByPluginID.getOrDefault(pluginID, new ExtensionPlayerData.Factory(pluginID)); - for (ExtensionTabData.Factory tabData : entry.getValue().values()) { - data.addTab(tabData.build()); - } - dataByPluginID.put(pluginID, data); - } - return dataByPluginID; - } - @Override - public Map executeQuery(SQLDB db) { - Map> tablesByPluginIDAndTableID = db.query(queryTableProviders()); - Map> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID)); - - Map> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues); - return flatMapByPluginID(tabDataByPluginID); + public Map executeQuery(SQLDB db) { + QueriedTables tablesWithValues = db.query(placeValuesToTables(db.query(queryTableProviders()))); + return tablesWithValues.toQueriedTabs().toExtensionDataByPluginID(); } - /** - * @param tablesByPluginIDAndTableID {@code >} - * @return {@code } - */ - private Map> mapToTabsByPluginID(Map> tablesByPluginIDAndTableID) { - Map> byPluginID = new HashMap<>(); - - for (Map.Entry> entry : tablesByPluginIDAndTableID.entrySet()) { - Integer pluginID = entry.getKey(); - Map byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>()); - for (Table.Factory table : entry.getValue().values()) { - // Extra Table information - String tableName = TableAccessor.getTableName(table); - Color tableColor = TableAccessor.getColor(table); - - // Extra tab information - String tabName = TableAccessor.getTabName(table); - int tabPriority = TableAccessor.getTabPriority(table); - ElementOrder[] tabOrder = TableAccessor.getTabOrder(table); - Icon tabIcon = TableAccessor.getTabIcon(table); - - ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority))); - tab.putTableData(new ExtensionTableData( - tableName, table.build(), tableColor - )); - byTabName.put(tabName, tab); - } - byPluginID.put(pluginID, byTabName); - } - - return byPluginID; - } - - // Map: > - private Query>> queryTableValues(Map> tables) { + private Query placeValuesToTables(QueriedTables tables) { String selectTableValues = SELECT + ExtensionTableProviderTable.PLUGIN_ID + ',' + ExtensionPlayerTableValueTable.TABLE_ID + ',' + @@ -133,43 +80,29 @@ public class ExtensionPlayerTablesQuery implements Query>>(selectTableValues, 10000) { + return new QueryStatement(selectTableValues, 10000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, playerUUID.toString()); } @Override - public Map> processResults(ResultSet set) throws SQLException { + public QueriedTables processResults(ResultSet set) throws SQLException { while (set.next()) { - Table.Factory table = getTable(set); - if (table == null) { - continue; - } - - Object[] row = extractTableRow(set); - if (row.length > 0) { - table.addRow(row); - } + tables.addRow( + set.getInt(ExtensionTableProviderTable.PLUGIN_ID), + set.getInt(ExtensionPlayerTableValueTable.TABLE_ID), + extractTableRow(set) + ); } return tables; } - - private Table.Factory getTable(ResultSet set) throws SQLException { - int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID); - Map byTableID = tables.get(pluginID); - if (byTableID == null) { - return null; - } - int tableID = set.getInt(ExtensionPlayerTableValueTable.TABLE_ID); - return byTableID.get(tableID); - } }; } private Object[] extractTableRow(ResultSet set) throws SQLException { List row = new ArrayList<>(); - for (int i = 1; i <= 5; i++) { + for (int i = 1; i <= 4; i++) { String columnName = "col_" + i + "_value"; // See ExtensionPlayerTableValueTable.VALUE_1 String value = set.getString(columnName); if (value == null) { @@ -180,8 +113,7 @@ public class ExtensionPlayerTablesQuery implements Query> - private Query>> queryTableProviders() { + private Query queryTableProviders() { String selectTables = SELECT + "p1." + ExtensionTableProviderTable.ID + " as table_id," + "p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," + @@ -219,41 +151,41 @@ public class ExtensionPlayerTablesQuery implements Query>>(selectTables, 100) { + return new QueryStatement(selectTables, 100) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, playerUUID.toString()); } @Override - public Map> processResults(ResultSet set) throws SQLException { - Map> byPluginID = new HashMap<>(); - + public QueriedTables processResults(ResultSet set) throws SQLException { + QueriedTables tables = new QueriedTables(); while (set.next()) { - int pluginID = set.getInt("plugin_id"); - Map byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>()); - - int tableID = set.getInt("table_id"); - Table.Factory table = Table.builder(); - - extractColumns(set, table); - - TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE)); - TableAccessor.setTableName(table, set.getString("table_name")); - TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse("")); - TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100)); - TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values())); - TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon")); - - byTableID.put(tableID, table); - byPluginID.put(pluginID, byTableID); + tables.put( + set.getInt(ExtensionTableProviderTable.PLUGIN_ID), + set.getInt(ExtensionPlayerTableValueTable.TABLE_ID), + extractTable(set) + ); } - - return byPluginID; + return tables; } }; } + private Table.Factory extractTable(ResultSet set) throws SQLException { + Table.Factory table = Table.builder(); + + extractColumns(set, table); + + TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE)); + TableAccessor.setTableName(table, set.getString("table_name")); + TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse("")); + TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100)); + TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values())); + TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon")); + return table; + } + private void extractColumns(ResultSet set, Table.Factory table) throws SQLException { String col1 = set.getString(ExtensionTableProviderTable.COL_1); if (col1 != null) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java index a88d91ced..39756bae8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerDataQuery.java @@ -16,10 +16,6 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.*; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.icon.Color; @@ -27,14 +23,17 @@ import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.TabInformation; import com.djrapitops.plan.extension.implementation.results.*; -import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Query ExtensionServerData of a server. @@ -53,7 +52,7 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.*; * * @author Rsl1122 */ -public class ExtensionServerDataQuery implements Query> { +public class ExtensionServerDataQuery implements Query> { private final UUID serverUUID; @@ -61,42 +60,30 @@ public class ExtensionServerDataQuery implements Query this.serverUUID = serverUUID; } - static Map flatMapToServerData(Map> tabDataByPluginID) { - Map dataByPluginID = new HashMap<>(); - for (Map.Entry> entry : tabDataByPluginID.entrySet()) { - Integer pluginID = entry.getKey(); - ExtensionServerData.Factory data = dataByPluginID.getOrDefault(pluginID, new ExtensionServerData.Factory(pluginID)); - for (ExtensionTabData.Factory tabData : entry.getValue().values()) { - data.addTab(tabData.build()); - } - dataByPluginID.put(pluginID, data); - } - return dataByPluginID; - } - @Override - public List executeQuery(SQLDB db) { + public List executeQuery(SQLDB db) { List extensionsOfServer = db.query(ExtensionInformationQueries.extensionsOfServer(serverUUID)); - Map extensionDataByPluginID = db.query(fetchIncompleteServerDataByPluginID()); + Map extensionDataByPluginID = db.query(fetchIncompleteServerDataByPluginID()); combine(extensionDataByPluginID, db.query(new ExtensionAggregateBooleansQuery(serverUUID))); combine(extensionDataByPluginID, db.query(new ExtensionAggregateDoublesQuery(serverUUID))); combine(extensionDataByPluginID, db.query(new ExtensionAggregateNumbersQuery(serverUUID))); combine(extensionDataByPluginID, db.query(new ExtensionAggregatePercentagesQuery(serverUUID))); combine(extensionDataByPluginID, db.query(new ExtensionServerTablesQuery(serverUUID))); + combine(extensionDataByPluginID, db.query(new ExtensionAggregateGroupsQuery(serverUUID))); return combineWithExtensionInfo(extensionsOfServer, extensionDataByPluginID); } private void combine( - Map extensionDataByPluginID, - Map aggregates + Map extensionDataByPluginID, + Map aggregates ) { - for (Map.Entry entry : aggregates.entrySet()) { + for (Map.Entry entry : aggregates.entrySet()) { Integer pluginID = entry.getKey(); - ExtensionServerData.Factory data = entry.getValue(); + ExtensionData.Factory data = entry.getValue(); - ExtensionServerData.Factory found = extensionDataByPluginID.get(pluginID); + ExtensionData.Factory found = extensionDataByPluginID.get(pluginID); if (found == null) { extensionDataByPluginID.put(pluginID, data); } else { @@ -105,23 +92,23 @@ public class ExtensionServerDataQuery implements Query } } - private List combineWithExtensionInfo( + private List combineWithExtensionInfo( List extensionsOfServer, - Map extensionDataByPluginID + Map extensionDataByPluginID ) { - List extensionServerData = new ArrayList<>(); + List extensionData = new ArrayList<>(); for (ExtensionInformation extensionInformation : extensionsOfServer) { - ExtensionServerData.Factory data = extensionDataByPluginID.get(extensionInformation.getId()); + ExtensionData.Factory data = extensionDataByPluginID.get(extensionInformation.getId()); if (data == null) { continue; } - extensionServerData.add(data.setInformation(extensionInformation).build()); + extensionData.add(data.setInformation(extensionInformation).build()); } - return extensionServerData; + return extensionData; } - private Query> fetchIncompleteServerDataByPluginID() { + private Query> fetchIncompleteServerDataByPluginID() { String sql = SELECT + "v1." + ExtensionServerValueTable.BOOLEAN_VALUE + " as boolean_value," + "v1." + ExtensionServerValueTable.DOUBLE_VALUE + " as double_value," + @@ -153,7 +140,7 @@ public class ExtensionServerDataQuery implements Query WHERE + ExtensionPluginTable.SERVER_UUID + "=?" + AND + "p1." + ExtensionProviderTable.HIDDEN + "=?"; - return new QueryStatement>(sql, 1000) { + return new QueryStatement>(sql, 1000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); @@ -161,31 +148,41 @@ public class ExtensionServerDataQuery implements Query } @Override - public Map processResults(ResultSet set) throws SQLException { - Map> tabDataByPluginID = extractTabDataByPluginID(set); - return flatMapToServerData(tabDataByPluginID); + public Map processResults(ResultSet set) throws SQLException { + return extractTabDataByPluginID(set).toExtensionDataByPluginID(); } }; } - private Map> extractTabDataByPluginID(ResultSet set) throws SQLException { - Map> tabDataByPluginID = new HashMap<>(); + private QueriedTabData extractTabDataByPluginID(ResultSet set) throws SQLException { + QueriedTabData tabData = new QueriedTabData(); while (set.next()) { int pluginID = set.getInt("plugin_id"); - Map tabData = tabDataByPluginID.getOrDefault(pluginID, new HashMap<>()); - String tabName = Optional.ofNullable(set.getString("tab_name")).orElse(""); - ExtensionTabData.Factory inMap = tabData.get(tabName); - ExtensionTabData.Factory extensionTab = inMap != null ? inMap : extractTab(tabName, set, tabData); + ExtensionTabData.Factory extensionTab = tabData.getTab(pluginID, tabName, () -> extractTabInformation(tabName, set)); ExtensionDescriptive extensionDescriptive = extractDescriptive(set); extractAndPutDataTo(extensionTab, extensionDescriptive, set); - - tabData.put(tabName, extensionTab); - tabDataByPluginID.put(pluginID, tabData); } - return tabDataByPluginID; + return tabData; + } + + private TabInformation extractTabInformation(String tabName, ResultSet set) throws SQLException { + Optional tabPriority = Optional.of(set.getInt("tab_priority")); + if (set.wasNull()) { + tabPriority = Optional.empty(); + } + Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); + + Icon tabIcon = extractTabIcon(set); + + return new TabInformation( + tabName, + tabIcon, + elementOrder.orElse(ElementOrder.values()), + tabPriority.orElse(100) + ); } private void extractAndPutDataTo(ExtensionTabData.Factory extensionTab, ExtensionDescriptive descriptive, ResultSet set) throws SQLException { @@ -235,23 +232,6 @@ public class ExtensionServerDataQuery implements Query return new ExtensionDescriptive(name, text, description, icon, priority); } - private ExtensionTabData.Factory extractTab(String tabName, ResultSet set, Map tabData) throws SQLException { - Optional tabPriority = Optional.of(set.getInt("tab_priority")); - if (set.wasNull()) { - tabPriority = Optional.empty(); - } - Optional elementOrder = Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize); - - Icon tabIcon = extractTabIcon(set); - - return tabData.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation( - tabName, - tabIcon, - elementOrder.orElse(ElementOrder.values()), - tabPriority.orElse(100) - ))); - } - private Icon extractTabIcon(ResultSet set) throws SQLException { Optional iconName = Optional.ofNullable(set.getString("tab_icon_name")); if (iconName.isPresent()) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java index 8a4954064..9e43eb366 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/ExtensionServerPlayerDataTableQuery.java @@ -16,15 +16,15 @@ */ package com.djrapitops.plan.extension.implementation.storage.queries; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.*; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.results.*; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -34,7 +34,7 @@ import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Query Extension data of x most recent players on a server. @@ -55,16 +55,31 @@ public class ExtensionServerPlayerDataTableQuery implements Query executeQuery(SQLDB db) { - return db.query(fetchIncompletePlayerDataByPluginID()); + return combine(db.query(fetchPlayerData()), db.query(fetchPlayerGroups())); } - private Query> fetchIncompletePlayerDataByPluginID() { + private Map combine(Map one, Map two) { + for (Map.Entry entry : two.entrySet()) { + UUID playerUUID = entry.getKey(); + ExtensionTabData data = entry.getValue(); + ExtensionTabData existingData = one.get(playerUUID); + if (existingData != null) { + existingData.combine(data); + } else { + existingData = data; + } + one.put(playerUUID, existingData); + } + return one; + } + + private Query> fetchPlayerData() { String selectLimitedNumberOfPlayerUUIDsByLastSeenDate = SELECT + SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID + ",MAX(" + SessionsTable.SESSION_END + ") as last_seen" + FROM + SessionsTable.TABLE_NAME + - GROUP_BY + SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID + ',' + SessionsTable.SESSION_END + - ORDER_BY + SessionsTable.SESSION_END + " DESC LIMIT ?"; + GROUP_BY + SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID + + ORDER_BY + "last_seen DESC LIMIT ?"; String sql = SELECT + "v1." + ExtensionPlayerValueTable.USER_UUID + " as uuid," + @@ -73,6 +88,7 @@ public class ExtensionServerPlayerDataTableQuery implements Query> fetchPlayerGroups() { + String selectLimitedNumberOfPlayerUUIDsByLastSeenDate = SELECT + + SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID + + ",MAX(" + SessionsTable.SESSION_END + ") as last_seen" + + FROM + SessionsTable.TABLE_NAME + + GROUP_BY + SessionsTable.TABLE_NAME + '.' + SessionsTable.USER_UUID + + ORDER_BY + "last_seen DESC LIMIT ?"; + + String sql = SELECT + + "v1." + ExtensionGroupsTable.USER_UUID + " as uuid," + + "v1." + ExtensionGroupsTable.GROUP_NAME + " as group_value," + + "p1." + ExtensionProviderTable.PROVIDER_NAME + " as provider_name," + + "p1." + ExtensionProviderTable.TEXT + " as text," + + "i1." + ExtensionIconTable.ICON_NAME + " as provider_icon_name," + + "i1." + ExtensionIconTable.FAMILY + " as provider_icon_family" + + FROM + ExtensionGroupsTable.TABLE_NAME + " v1" + + INNER_JOIN + '(' + selectLimitedNumberOfPlayerUUIDsByLastSeenDate + ") as last_seen_q on last_seen_q.uuid=v1." + ExtensionGroupsTable.USER_UUID + + INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " p1 on p1." + ExtensionProviderTable.ID + "=v1." + ExtensionGroupsTable.PROVIDER_ID + + INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " e1 on e1." + ExtensionPluginTable.ID + "=p1." + ExtensionProviderTable.PLUGIN_ID + + LEFT_JOIN + ExtensionIconTable.TABLE_NAME + " i1 on i1." + ExtensionIconTable.ID + "=p1." + ExtensionProviderTable.ICON_ID + + WHERE + "e1." + ExtensionPluginTable.SERVER_UUID + "=?"; + + return new QueryStatement>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, xMostRecentPlayers); // Limit to x most recently seen players + statement.setString(2, serverUUID.toString()); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + return extractDataByPlayer(set); + } + }; + } + private Map extractDataByPlayer(ResultSet set) throws SQLException { Map dataByPlayer = new HashMap<>(); @@ -120,6 +172,12 @@ public class ExtensionServerPlayerDataTableQuery implements Query> { +public class ExtensionServerTablesQuery implements Query> { private final UUID serverUUID; @@ -66,49 +60,12 @@ public class ExtensionServerTablesQuery implements Query executeQuery(SQLDB db) { - Map> tablesByPluginIDAndTableID = db.query(queryTableProviders()); - Map> tablesWithValues = db.query(queryTableValues(tablesByPluginIDAndTableID)); - - Map> tabDataByPluginID = mapToTabsByPluginID(tablesWithValues); - return ExtensionServerDataQuery.flatMapToServerData(tabDataByPluginID); + public Map executeQuery(SQLDB db) { + QueriedTables tablesWithValues = db.query(queryTableValues(db.query(queryTableProviders()))); + return tablesWithValues.toQueriedTabs().toExtensionDataByPluginID(); } - /** - * @param tablesByPluginIDAndTableID {@code >} - * @return {@code } - */ - private Map> mapToTabsByPluginID(Map> tablesByPluginIDAndTableID) { - Map> byPluginID = new HashMap<>(); - - for (Map.Entry> entry : tablesByPluginIDAndTableID.entrySet()) { - Integer pluginID = entry.getKey(); - Map byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>()); - for (Table.Factory table : entry.getValue().values()) { - // Extra Table information - String tableName = TableAccessor.getTableName(table); - Color tableColor = TableAccessor.getColor(table); - - // Extra tab information - String tabName = TableAccessor.getTabName(table); - int tabPriority = TableAccessor.getTabPriority(table); - ElementOrder[] tabOrder = TableAccessor.getTabOrder(table); - Icon tabIcon = TableAccessor.getTabIcon(table); - - ExtensionTabData.Factory tab = byTabName.getOrDefault(tabName, new ExtensionTabData.Factory(new TabInformation(tabName, tabIcon, tabOrder, tabPriority))); - tab.putTableData(new ExtensionTableData( - tableName, table.build(), tableColor - )); - byTabName.put(tabName, tab); - } - byPluginID.put(pluginID, byTabName); - } - - return byPluginID; - } - - // Map: > - private Query>> queryTableValues(Map> tables) { + private Query queryTableValues(QueriedTables tables) { String selectTableValues = SELECT + ExtensionTableProviderTable.PLUGIN_ID + ',' + ExtensionServerTableValueTable.TABLE_ID + ',' + @@ -121,37 +78,23 @@ public class ExtensionServerTablesQuery implements Query>>(selectTableValues, 10000) { + return new QueryStatement(selectTableValues, 10000) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); } @Override - public Map> processResults(ResultSet set) throws SQLException { + public QueriedTables processResults(ResultSet set) throws SQLException { while (set.next()) { - Table.Factory table = getTable(set); - if (table == null) { - continue; - } - - Object[] row = extractTableRow(set); - if (row.length > 0) { - table.addRow(row); - } + tables.addRow( + set.getInt(ExtensionTableProviderTable.PLUGIN_ID), + set.getInt(ExtensionPlayerTableValueTable.TABLE_ID), + extractTableRow(set) + ); } return tables; } - - private Table.Factory getTable(ResultSet set) throws SQLException { - int pluginID = set.getInt(ExtensionTableProviderTable.PLUGIN_ID); - Map byTableID = tables.get(pluginID); - if (byTableID == null) { - return null; - } - int tableID = set.getInt(ExtensionServerTableValueTable.TABLE_ID); - return byTableID.get(tableID); - } }; } @@ -168,8 +111,7 @@ public class ExtensionServerTablesQuery implements Query> - private Query>> queryTableProviders() { + private Query queryTableProviders() { String selectTables = SELECT + "p1." + ExtensionTableProviderTable.ID + " as table_id," + "p1." + ExtensionTableProviderTable.PLUGIN_ID + " as plugin_id," + @@ -212,41 +154,43 @@ public class ExtensionServerTablesQuery implements Query>>(selectTables, 100) { + return new QueryStatement(selectTables, 100) { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, serverUUID.toString()); } @Override - public Map> processResults(ResultSet set) throws SQLException { - Map> byPluginID = new HashMap<>(); + public QueriedTables processResults(ResultSet set) throws SQLException { + QueriedTables tables = new QueriedTables(); while (set.next()) { - int pluginID = set.getInt("plugin_id"); - Map byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>()); - - int tableID = set.getInt("table_id"); - Table.Factory table = Table.builder(); - - extractColumns(set, table); - - TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE)); - TableAccessor.setTableName(table, set.getString("table_name")); - TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse("")); - TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100)); - TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values())); - TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon")); - - byTableID.put(tableID, table); - byPluginID.put(pluginID, byTableID); + tables.put( + set.getInt(ExtensionTableProviderTable.PLUGIN_ID), + set.getInt(ExtensionPlayerTableValueTable.TABLE_ID), + extractTable(set) + ); } - return byPluginID; + return tables; } }; } + private Table.Factory extractTable(ResultSet set) throws SQLException { + Table.Factory table = Table.builder(); + + extractColumns(set, table); + + TableAccessor.setColor(table, Color.getByName(set.getString("table_color")).orElse(Color.NONE)); + TableAccessor.setTableName(table, set.getString("table_name")); + TableAccessor.setTabName(table, Optional.ofNullable(set.getString("tab_name")).orElse("")); + TableAccessor.setTabPriority(table, Optional.of(set.getInt("tab_priority")).orElse(100)); + TableAccessor.setTabOrder(table, Optional.ofNullable(set.getString(ExtensionTabTable.ELEMENT_ORDER)).map(ElementOrder::deserialize).orElse(ElementOrder.values())); + TableAccessor.setTabIcon(table, extractIcon(set, "tab_icon")); + return table; + } + private void extractColumns(ResultSet set, Table.Factory table) throws SQLException { String col1 = set.getString(ExtensionTableProviderTable.COL_1); if (col1 != null) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/QueriedTabData.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/QueriedTabData.java new file mode 100644 index 000000000..d0be34828 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/QueriedTabData.java @@ -0,0 +1,70 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.implementation.storage.queries; + +import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionData; +import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; +import com.djrapitops.plan.utilities.java.ThrowingSupplier; + +import java.util.HashMap; +import java.util.Map; + +/** + * Query utility for extracting Tabs. + * + * @author Rsl1122 + */ +public class QueriedTabData { + + private final Map> byPluginID; + + public QueriedTabData() { + byPluginID = new HashMap<>(); + } + + public ExtensionTabData.Factory getTab(int pluginID, String tabName, ThrowingSupplier newDefault) throws K { + Map byTabName = byPluginID.getOrDefault(pluginID, new HashMap<>()); + + ExtensionTabData.Factory tab = byTabName.get(tabName); + if (tab == null) { + tab = new ExtensionTabData.Factory(newDefault.get()); + } + + byTabName.put(tabName, tab); + byPluginID.put(pluginID, byTabName); + return tab; + } + + public Map toExtensionDataByPluginID() { + Map dataByPluginID = new HashMap<>(); + for (Map.Entry> entry : byPluginID.entrySet()) { + Integer pluginID = entry.getKey(); + + ExtensionData.Factory data = dataByPluginID.get(pluginID); + if (data == null) { + data = new ExtensionData.Factory(pluginID); + } + + for (ExtensionTabData.Factory tabData : entry.getValue().values()) { + data.addTab(tabData.build()); + } + dataByPluginID.put(pluginID, data); + } + return dataByPluginID; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/QueriedTables.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/QueriedTables.java new file mode 100644 index 000000000..d2af0eb47 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/queries/QueriedTables.java @@ -0,0 +1,101 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.implementation.storage.queries; + +import com.djrapitops.plan.extension.ElementOrder; +import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.extension.implementation.results.ExtensionTabData; +import com.djrapitops.plan.extension.implementation.results.ExtensionTableData; +import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.extension.table.TableAccessor; + +import java.util.HashMap; +import java.util.Map; + +/** + * Query utility for extracting Tables. + * + * @author Rsl1122 + */ +public class QueriedTables { + + // Map: > + private final Map> byPluginID; + + public QueriedTables() { + byPluginID = new HashMap<>(); + } + + public boolean contains(int pluginID, int tableID) { + Map byTableID = byPluginID.get(pluginID); + return byTableID != null && byTableID.containsKey(tableID); + } + + public void put(int pluginID, int tableID, Table.Factory table) { + Map byTableID = byPluginID.getOrDefault(pluginID, new HashMap<>()); + byTableID.put(tableID, table); + byPluginID.put(pluginID, byTableID); + } + + public void addRow(int pluginID, int tableID, Object... row) { + if (row.length <= 0) return; + + Map byTableID = byPluginID.get(pluginID); + if (byTableID == null) return; + + Table.Factory table = byTableID.get(tableID); + if (table == null) return; + + table.addRow(row); + } + + public QueriedTabData toQueriedTabs() { + QueriedTabData tabData = new QueriedTabData(); + + for (Map.Entry> entry : byPluginID.entrySet()) { + Integer pluginID = entry.getKey(); + + for (Table.Factory table : entry.getValue().values()) { + // Extra Table information + String tableName = TableAccessor.getTableName(table); + Color tableColor = TableAccessor.getColor(table); + + // Extra tab information + String tabName = TableAccessor.getTabName(table); + + ExtensionTabData.Factory tab = tabData.getTab(pluginID, tabName, () -> extractTabInformation(table)); + tab.putTableData(new ExtensionTableData( + tableName, table.build(), tableColor + )); + } + } + + return tabData; + } + + private TabInformation extractTabInformation(Table.Factory table) { + String tabName = TableAccessor.getTabName(table); + int tabPriority = TableAccessor.getTabPriority(table); + ElementOrder[] tabOrder = TableAccessor.getTabOrder(table); + Icon tabIcon = TableAccessor.getTabIcon(table); + + return new TabInformation(tabName, tabIcon, tabOrder, tabPriority); + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreIconTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreIconTransaction.java index 05173f089..577a1a14a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreIconTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreIconTransaction.java @@ -16,18 +16,18 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction to store an Icon to the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StorePluginTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StorePluginTransaction.java index f5c2cd897..ed3c4378d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StorePluginTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StorePluginTransaction.java @@ -16,19 +16,19 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; /** * Transaction to update command usage information in the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java index a4c44b37d..fafd00614 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/StoreTabInformationTransaction.java @@ -16,21 +16,21 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; import com.djrapitops.plan.extension.ElementOrder; import com.djrapitops.plan.extension.implementation.TabInformation; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; /** * Transaction for storing {@link TabInformation}s. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreBooleanProviderTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreBooleanProviderTransaction.java index e7d5dd0a6..e530773e7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreBooleanProviderTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreBooleanProviderTransaction.java @@ -16,14 +16,14 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.providers; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; import com.djrapitops.plan.extension.implementation.ProviderInformation; import com.djrapitops.plan.extension.implementation.providers.DataProvider; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -31,9 +31,9 @@ import java.sql.Types; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionProviderTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable.*; /** * Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.BooleanDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreDoubleProviderTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreDoubleProviderTransaction.java index 725da2073..b002ba186 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreDoubleProviderTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreDoubleProviderTransaction.java @@ -16,14 +16,14 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.providers; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; import com.djrapitops.plan.extension.implementation.ProviderInformation; import com.djrapitops.plan.extension.implementation.providers.DataProvider; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -31,9 +31,9 @@ import java.sql.Types; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionProviderTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable.*; /** * Transaction to store information about a dobule {@link DataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreGroupProviderTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreGroupProviderTransaction.java new file mode 100644 index 000000000..66f2dec9b --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreGroupProviderTransaction.java @@ -0,0 +1,126 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.implementation.storage.transactions.providers; + +import com.djrapitops.plan.extension.implementation.ProviderInformation; +import com.djrapitops.plan.extension.implementation.providers.DataProvider; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Optional; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable.*; + +/** + * Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.GroupDataProvider}. + * + * @author Rsl1122 + */ +public class StoreGroupProviderTransaction extends Transaction { + + private final UUID serverUUID; + private ProviderInformation providerInformation; + + public StoreGroupProviderTransaction(DataProvider provider, UUID serverUUID) { + this.serverUUID = serverUUID; + providerInformation = provider.getProviderInformation(); + } + + @Override + protected void performOperations() { + execute(storeProvider()); + } + + private Executable storeProvider() { + return connection -> { + if (!updateProvider().execute(connection)) { + return insertProvider().execute(connection); + } + return false; + }; + } + + private Executable updateProvider() { + String sql = "UPDATE " + TABLE_NAME + + " SET " + + TEXT + "=?," + + CONDITION + "=?," + + TAB_ID + "=" + ExtensionTabTable.STATEMENT_SELECT_TAB_ID + "," + + ICON_ID + "=" + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + + WHERE + PLUGIN_ID + "=" + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + + AND + PROVIDER_NAME + "=?"; + + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, providerInformation.getText()); + Optional condition = providerInformation.getCondition(); + if (condition.isPresent()) { + statement.setString(2, condition.get()); + } else { + statement.setNull(2, Types.VARCHAR); + } + ExtensionTabTable.set3TabValuesToStatement(statement, 3, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID); + ExtensionIconTable.set3IconValuesToStatement(statement, 6, providerInformation.getIcon()); + ExtensionPluginTable.set2PluginValuesToStatement(statement, 9, providerInformation.getPluginName(), serverUUID); + statement.setString(11, providerInformation.getName()); + } + }; + } + + private Executable insertProvider() { + String sql = "INSERT INTO " + TABLE_NAME + "(" + + PROVIDER_NAME + "," + + TEXT + "," + + CONDITION + "," + + SHOW_IN_PLAYERS_TABLE + "," + + TAB_ID + "," + + ICON_ID + "," + + PLUGIN_ID + + ") VALUES (?,?,?,?," + + ExtensionTabTable.STATEMENT_SELECT_TAB_ID + "," + + ExtensionIconTable.STATEMENT_SELECT_ICON_ID + "," + + ExtensionPluginTable.STATEMENT_SELECT_PLUGIN_ID + ")"; + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, providerInformation.getName()); + statement.setString(2, providerInformation.getText()); + Optional condition = providerInformation.getCondition(); + if (condition.isPresent()) { + statement.setString(3, condition.get()); + } else { + statement.setNull(3, Types.VARCHAR); + } + statement.setBoolean(4, providerInformation.isShownInPlayersTable()); + ExtensionTabTable.set3TabValuesToStatement(statement, 5, providerInformation.getTab().orElse("No Tab"), providerInformation.getPluginName(), serverUUID); + ExtensionIconTable.set3IconValuesToStatement(statement, 8, providerInformation.getIcon()); + ExtensionPluginTable.set2PluginValuesToStatement(statement, 11, providerInformation.getPluginName(), serverUUID); + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreNumberProviderTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreNumberProviderTransaction.java index c02f1b2fb..d3ff9f8ad 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreNumberProviderTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreNumberProviderTransaction.java @@ -16,15 +16,15 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.providers; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; import com.djrapitops.plan.extension.FormatType; import com.djrapitops.plan.extension.implementation.ProviderInformation; import com.djrapitops.plan.extension.implementation.providers.DataProvider; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -32,9 +32,9 @@ import java.sql.Types; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionProviderTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable.*; /** * Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.NumberDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreStringProviderTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreStringProviderTransaction.java index 836b9dc46..fb5bdbae0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreStringProviderTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreStringProviderTransaction.java @@ -16,14 +16,14 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.providers; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; import com.djrapitops.plan.extension.implementation.ProviderInformation; import com.djrapitops.plan.extension.implementation.providers.DataProvider; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; @@ -31,9 +31,9 @@ import java.sql.Types; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionProviderTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable.*; /** * Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.StringDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java index 5b203abe9..8584e576a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/providers/StoreTableProviderTransaction.java @@ -16,25 +16,25 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.providers; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionIconTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTabTable; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Icon; import com.djrapitops.plan.extension.implementation.ProviderInformation; import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionIconTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTabTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionTableProviderTable.*; /** * Transaction to store information about a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java index b82406b33..9087eda3a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveInvalidResultsTransaction.java @@ -16,17 +16,17 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Collection; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction to remove method results that correspond to {@link com.djrapitops.plan.extension.annotation.InvalidateMethod} annotations. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java index 8bbda64c4..7cdb6654c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalPlayerResultsTransaction.java @@ -16,19 +16,16 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable; -import com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction to remove older results that violate an updated condition value. @@ -47,11 +44,13 @@ public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transa private final String playerValueTable; private final String playerTableValueTable; private final String tableTable; + private final String groupTable; public RemoveUnsatisfiedConditionalPlayerResultsTransaction() { providerTable = ExtensionProviderTable.TABLE_NAME; playerValueTable = ExtensionPlayerValueTable.TABLE_NAME; tableTable = ExtensionTableProviderTable.TABLE_NAME; + groupTable = ExtensionGroupsTable.TABLE_NAME; playerTableValueTable = ExtensionPlayerTableValueTable.TABLE_NAME; } @@ -60,6 +59,7 @@ public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transa String selectSatisfiedConditions = getSatisfiedConditionsSQL(); execute(deleteUnsatisfiedValues(selectSatisfiedConditions)); + execute(deleteUnsatisfiedGroupValues(selectSatisfiedConditions)); execute(deleteUnsatisfiedTableValues(selectSatisfiedConditions)); } @@ -100,7 +100,7 @@ public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transa FROM + providerTable + INNER_JOIN + playerValueTable + " on " + providerTable + '.' + ExtensionProviderTable.ID + "=" + ExtensionPlayerValueTable.PROVIDER_ID + LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled - " on (" + + " on (" + // Join when uuid and plugin_id match and condition for the group provider is satisfied playerValueTable + '.' + ExtensionPlayerValueTable.USER_UUID + "=q1." + ExtensionPlayerValueTable.USER_UUID + AND + ExtensionProviderTable.CONDITION + @@ -114,7 +114,7 @@ public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transa // Nested query here is required because MySQL limits update statements with nested queries: // The nested query creates a temporary table that bypasses the same table query-update limit. // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception. - String sql = "DELETE FROM " + playerValueTable + + String sql = DELETE_FROM + playerValueTable + WHERE + ExtensionPlayerValueTable.ID + " IN (" + SELECT + ExtensionPlayerValueTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)"; return new ExecStatement(sql) { @@ -130,7 +130,7 @@ public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transa String selectUnsatisfiedValueIDs = SELECT + ExtensionTableProviderTable.ID + FROM + tableTable + LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled - " on (" + + " on (" + // Join when plugin_id matches and condition for the group provider is satisfied tableTable + '.' + ExtensionTableProviderTable.CONDITION + "=q1." + ExtensionProviderTable.PROVIDED_CONDITION + AND + tableTable + '.' + ExtensionTableProviderTable.PLUGIN_ID + @@ -142,7 +142,7 @@ public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transa // Nested query here is required because MySQL limits update statements with nested queries: // The nested query creates a temporary table that bypasses the same table query-update limit. // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception. - String deleteValuesSQL = "DELETE FROM " + playerTableValueTable + + String deleteValuesSQL = DELETE_FROM + playerTableValueTable + WHERE + ExtensionPlayerTableValueTable.TABLE_ID + " IN (" + SELECT + ExtensionTableProviderTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)"; return new ExecStatement(deleteValuesSQL) { @@ -153,4 +153,40 @@ public class RemoveUnsatisfiedConditionalPlayerResultsTransaction extends Transa } }; } + + private Executable deleteUnsatisfiedGroupValues(String selectSatisfiedConditions) { + // plan_extensions_player_groups.id is needed for removal of the correct row. + // The id is known if group_id & uuid are known + // - + // Conditions are in plan_extensions_providers + // selectSatisfiedConditions lists 'provided_condition' Strings + String selectUnsatisfiedIDs = SELECT + groupTable + '.' + ID + + FROM + groupTable + + INNER_JOIN + providerTable + " on " + providerTable + '.' + ID + '=' + groupTable + '.' + ExtensionGroupsTable.PROVIDER_ID + + LEFT_JOIN + selectSatisfiedConditions + // Left join to preserve values that don't have their condition fulfilled + " on (" + // Join when uuid and plugin_id match and condition for the group provider is satisfied + groupTable + '.' + P_UUID + + "=q1." + P_UUID + + AND + ExtensionProviderTable.CONDITION + + "=q1." + ExtensionProviderTable.PROVIDED_CONDITION + + AND + providerTable + '.' + ExtensionProviderTable.PLUGIN_ID + + "=q1." + ExtensionProviderTable.PLUGIN_ID + + ')' + + WHERE + "q1." + ExtensionProviderTable.PROVIDED_CONDITION + IS_NULL + // Conditions that were not in the satisfied condition query + AND + ExtensionProviderTable.CONDITION + IS_NOT_NULL; // Ignore values that don't need condition + + // Nested query here is required because MySQL limits update statements with nested queries: + // The nested query creates a temporary table that bypasses the same table query-update limit. + // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception. + String deleteValuesSQL = DELETE_FROM + groupTable + + WHERE + ID + " IN (" + SELECT + ID + FROM + '(' + selectUnsatisfiedIDs + ") as ids)"; + + return new ExecStatement(deleteValuesSQL) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setBoolean(1, true); // Select provided conditions with 'true' value + statement.setBoolean(2, false); // Select negated conditions with 'false' value + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java index 2718ef6f9..8075e6fdb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/RemoveUnsatisfiedConditionalServerResultsTransaction.java @@ -16,19 +16,19 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; -import com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable; -import com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionServerTableValueTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionServerValueTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction to remove older results that violate an updated condition value. @@ -86,7 +86,7 @@ public class RemoveUnsatisfiedConditionalServerResultsTransaction extends Transa // Nested query here is required because MySQL limits update statements with nested queries: // The nested query creates a temporary table that bypasses the same table query-update limit. // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception. - String sql = "DELETE FROM " + serverValueTable + + String sql = DELETE_FROM + serverValueTable + WHERE + ExtensionServerValueTable.ID + " IN (" + SELECT + ExtensionServerValueTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)"; return new ExecStatement(sql) { @@ -136,7 +136,7 @@ public class RemoveUnsatisfiedConditionalServerResultsTransaction extends Transa // Nested query here is required because MySQL limits update statements with nested queries: // The nested query creates a temporary table that bypasses the same table query-update limit. // Note: MySQL versions 5.6.7+ might optimize this nested query away leading to an exception. - String deleteValuesSQL = "DELETE FROM " + serverTableValueTable + + String deleteValuesSQL = DELETE_FROM + serverTableValueTable + WHERE + ExtensionServerTableValueTable.TABLE_ID + " IN (" + SELECT + ExtensionTableProviderTable.ID + FROM + '(' + selectUnsatisfiedValueIDs + ") as ids)"; return new ExecStatement(deleteValuesSQL) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerBooleanResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerBooleanResultTransaction.java index 9b01dcbb0..9549e5137 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerBooleanResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerBooleanResultTransaction.java @@ -16,18 +16,18 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.BooleanDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerDoubleResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerDoubleResultTransaction.java index f64c182cf..5e10c51ac 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerDoubleResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerDoubleResultTransaction.java @@ -16,18 +16,18 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.DoubleDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerGroupsResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerGroupsResultTransaction.java new file mode 100644 index 000000000..171931919 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerGroupsResultTransaction.java @@ -0,0 +1,92 @@ +/* + * 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 . + */ +package com.djrapitops.plan.extension.implementation.storage.transactions.results; + +import com.djrapitops.plan.storage.database.sql.tables.ExtensionGroupsTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; +import org.apache.commons.lang3.StringUtils; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.GroupDataProvider}. + * + * @author Rsl1122 + */ +public class StorePlayerGroupsResultTransaction extends Transaction { + + private final String pluginName; + private final UUID serverUUID; + private final String providerName; + private final UUID playerUUID; + + private final String[] value; + + public StorePlayerGroupsResultTransaction(String pluginName, UUID serverUUID, String providerName, UUID playerUUID, String[] value) { + this.pluginName = pluginName; + this.serverUUID = serverUUID; + this.providerName = providerName; + this.playerUUID = playerUUID; + this.value = value; + } + + @Override + protected void performOperations() { + execute(deleteOldValues()); + for (String group : value) { + String groupName = StringUtils.truncate(group, 50); + execute(insertGroup(groupName)); + } + } + + private Executable deleteOldValues() { + String sql = DELETE_FROM + ExtensionGroupsTable.TABLE_NAME + + WHERE + ExtensionGroupsTable.USER_UUID + "=?" + + AND + ExtensionGroupsTable.PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID; + + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + ExtensionProviderTable.set3PluginValuesToStatement(statement, 2, providerName, pluginName, serverUUID); + } + }; + } + + private Executable insertGroup(String group) { + String sql = "INSERT INTO " + ExtensionGroupsTable.TABLE_NAME + "(" + + ExtensionGroupsTable.GROUP_NAME + "," + + ExtensionGroupsTable.USER_UUID + "," + + ExtensionGroupsTable.PROVIDER_ID + + ") VALUES (?,?," + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID + ")"; + return new ExecStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, group); + statement.setString(2, playerUUID.toString()); + ExtensionProviderTable.set3PluginValuesToStatement(statement, 3, providerName, pluginName, serverUUID); + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerNumberResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerNumberResultTransaction.java index f20ecd43d..87ec721e7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerNumberResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerNumberResultTransaction.java @@ -16,18 +16,18 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.NumberDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerPercentageResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerPercentageResultTransaction.java index 0bb8e1336..7da968200 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerPercentageResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerPercentageResultTransaction.java @@ -16,18 +16,18 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.PercentageDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerStringResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerStringResultTransaction.java index a858d7c3c..0b412e3fb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerStringResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerStringResultTransaction.java @@ -16,18 +16,18 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.StringDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java index 73f0b6a53..9b734df7a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StorePlayerTableResultTransaction.java @@ -16,12 +16,17 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.access.*; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.exceptions.database.DBOpException; import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; +import org.apache.commons.lang3.StringUtils; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -29,8 +34,8 @@ import java.sql.SQLException; import java.sql.Types; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; -import static com.djrapitops.plan.db.sql.tables.ExtensionPlayerTableValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerTableValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}. @@ -74,7 +79,7 @@ public class StorePlayerTableResultTransaction extends Transaction { } private Executable deleteOldValues(int tableID) { - String sql = "DELETE FROM " + TABLE_NAME + + String sql = DELETE_FROM + TABLE_NAME + WHERE + TABLE_ID + "=?" + AND + USER_UUID + "=?"; @@ -107,7 +112,7 @@ public class StorePlayerTableResultTransaction extends Transaction { statement.setString(2, playerUUID.toString()); for (int i = 0; i < maxColumnSize; i++) { Object value = row[i]; - setStringOrNull(statement, 3 + i, value != null ? value.toString() : null); + setStringOrNull(statement, 3 + i, value != null ? StringUtils.truncate(value.toString(), 250) : null); } // Rest are set null if not 4 columns wide. for (int i = maxColumnSize; i < 4; i++) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerBooleanResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerBooleanResultTransaction.java index dd8bbe976..3956cb649 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerBooleanResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerBooleanResultTransaction.java @@ -16,17 +16,17 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionServerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.BooleanDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerDoubleResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerDoubleResultTransaction.java index 559a36582..5199a7258 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerDoubleResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerDoubleResultTransaction.java @@ -16,17 +16,17 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionServerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.DoubleDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerNumberResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerNumberResultTransaction.java index 62098154a..8c389e818 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerNumberResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerNumberResultTransaction.java @@ -16,17 +16,17 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionServerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.NumberDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerPercentageResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerPercentageResultTransaction.java index 9fb317107..b34ab8f98 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerPercentageResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerPercentageResultTransaction.java @@ -16,17 +16,17 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionServerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.PercentageDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerStringResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerStringResultTransaction.java index f6c58510f..411afd904 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerStringResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerStringResultTransaction.java @@ -16,17 +16,17 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; -import static com.djrapitops.plan.db.sql.tables.ExtensionServerValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionServerValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.PercentageDataProvider}. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java index ff5211097..f3fbc8d62 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/extension/implementation/storage/transactions/results/StoreServerTableResultTransaction.java @@ -16,12 +16,17 @@ */ package com.djrapitops.plan.extension.implementation.storage.transactions.results; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.access.*; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ExtensionPluginTable; -import com.djrapitops.plan.db.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.exceptions.database.DBOpException; import com.djrapitops.plan.extension.table.Table; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionTableProviderTable; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; +import org.apache.commons.lang3.StringUtils; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -29,8 +34,8 @@ import java.sql.SQLException; import java.sql.Types; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; -import static com.djrapitops.plan.db.sql.tables.ExtensionServerTableValueTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.tables.ExtensionServerTableValueTable.*; /** * Transaction to store method result of a {@link com.djrapitops.plan.extension.implementation.providers.TableDataProvider}. @@ -72,7 +77,7 @@ public class StoreServerTableResultTransaction extends Transaction { } private Executable deleteOldValues(int tableID) { - String sql = "DELETE FROM " + TABLE_NAME + + String sql = DELETE_FROM + TABLE_NAME + WHERE + TABLE_ID + "=?" + AND + SERVER_UUID + "=?"; @@ -106,7 +111,7 @@ public class StoreServerTableResultTransaction extends Transaction { statement.setString(2, serverUUID.toString()); for (int i = 0; i < maxColumnSize; i++) { Object value = row[i]; - setStringOrNull(statement, 3 + i, value != null ? value.toString() : null); + setStringOrNull(statement, 3 + i, value != null ? StringUtils.truncate(value.toString(), 250) : null); } // Rest are set null if not 5 columns wide. for (int i = maxColumnSize; i < 5; i++) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/ServerShutdownSave.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/ServerShutdownSave.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/ServerShutdownSave.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/ServerShutdownSave.java index 847e54f4e..051ab89d9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/ServerShutdownSave.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/ServerShutdownSave.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan; +package com.djrapitops.plan.gathering; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.transactions.events.ServerShutdownTransaction; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.transactions.events.ServerShutdownTransaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/ShutdownHook.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/ShutdownHook.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/ShutdownHook.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/ShutdownHook.java index b10c621f0..2a1f0ba3a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/ShutdownHook.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/ShutdownHook.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan; +package com.djrapitops.plan.gathering; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/afk/AFKTracker.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/afk/AFKTracker.java index 8b6550488..8f4069090 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/afk/AFKTracker.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/afk/AFKTracker.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.afk; +package com.djrapitops.plan.gathering.afk; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; import java.util.*; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/cache/CacheSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/CacheSystem.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/system/cache/CacheSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/CacheSystem.java index 593c3c668..1c4b867d7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/cache/CacheSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/CacheSystem.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.cache; +package com.djrapitops.plan.gathering.cache; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.SubSystem; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/cache/GeolocationCache.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/GeolocationCache.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/cache/GeolocationCache.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/GeolocationCache.java index 9f26e20f6..e11ed515a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/cache/GeolocationCache.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/GeolocationCache.java @@ -14,17 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.cache; +package com.djrapitops.plan.gathering.cache; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DataGatheringSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.exception.GeoIp2Exception; import com.maxmind.geoip2.model.CountryResponse; @@ -43,8 +45,7 @@ import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.file.Files; -import java.util.HashMap; -import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.zip.GZIPInputStream; /** @@ -61,7 +62,7 @@ public class GeolocationCache implements SubSystem { private final PlanFiles files; private final PlanConfig config; private final PluginLogger logger; - private final Map cached; + private final Cache cache; private File geolocationDB; @@ -77,7 +78,9 @@ public class GeolocationCache implements SubSystem { this.config = config; this.logger = logger; - this.cached = new HashMap<>(); + this.cache = Caffeine.newBuilder() + .expireAfterAccess(1, TimeUnit.MINUTES) + .build(); } @Override @@ -109,16 +112,7 @@ public class GeolocationCache implements SubSystem { * @see #getUnCachedCountry(String) */ public String getCountry(String ipAddress) { - String country = getCachedCountry(ipAddress); - - if (country != null) { - return country; - } else { - country = getUnCachedCountry(ipAddress); - cached.put(ipAddress, country); - - return country; - } + return cache.get(ipAddress, this::getUnCachedCountry); } /** @@ -128,7 +122,7 @@ public class GeolocationCache implements SubSystem { * @return The cached country, {@code null} if the country is not cached */ private String getCachedCountry(String ipAddress) { - return cached.get(ipAddress); + return cache.getIfPresent(ipAddress); } /** @@ -200,18 +194,19 @@ public class GeolocationCache implements SubSystem { * @return true if the IP Address is cached */ boolean isCached(String ipAddress) { - return cached.containsKey(ipAddress); + return cache.getIfPresent(ipAddress) != null; } @Override public void disable() { - cached.clear(); + clearCache(); } /** * Clears the cache */ public void clearCache() { - cached.clear(); + cache.invalidateAll(); + cache.cleanUp(); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/cache/NicknameCache.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/NicknameCache.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/cache/NicknameCache.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/NicknameCache.java index b793ec7fe..7e3c9e585 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/cache/NicknameCache.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/NicknameCache.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.cache; +package com.djrapitops.plan.gathering.cache; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.db.access.queries.objects.NicknameQueries; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.objects.NicknameQueries; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/cache/SessionCache.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/SessionCache.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/system/cache/SessionCache.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/SessionCache.java index 23290f35f..fc9d057c4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/cache/SessionCache.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/cache/SessionCache.java @@ -14,17 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.cache; +package com.djrapitops.plan.gathering.cache; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.google.common.collect.ImmutableMap; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.gathering.domain.Session; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** @@ -43,7 +40,8 @@ public class SessionCache { } public static Map getActiveSessions() { - return ImmutableMap.copyOf(ACTIVE_SESSIONS); + refreshActiveSessionsState(); + return Collections.unmodifiableMap(new HashMap<>(ACTIVE_SESSIONS)); } public static void clear() { @@ -52,7 +50,7 @@ public class SessionCache { public static void refreshActiveSessionsState() { for (Session session : ACTIVE_SESSIONS.values()) { - session.getUnsafe(SessionKeys.WORLD_TIMES).updateState(System.currentTimeMillis()); + session.getValue(SessionKeys.WORLD_TIMES).ifPresent(worldTimes -> worldTimes.updateState(System.currentTimeMillis())); } } @@ -74,11 +72,12 @@ public class SessionCache { * @return Optional: previous session. Recipients of this object should decide if it needs to be saved. */ public Optional cacheSession(UUID playerUUID, Session session) { + Optional inProgress = Optional.empty(); if (getCachedSession(playerUUID).isPresent()) { - return endSession(playerUUID, session.getUnsafe(SessionKeys.START)); + inProgress = endSession(playerUUID, session.getUnsafe(SessionKeys.START)); } ACTIVE_SESSIONS.put(playerUUID, session); - return Optional.empty(); + return inProgress; } /** @@ -93,12 +92,8 @@ public class SessionCache { if (session == null || session.getUnsafe(SessionKeys.START) > time) { return Optional.empty(); } - removeSessionFromCache(playerUUID); + ACTIVE_SESSIONS.remove(playerUUID); session.endSession(time); return Optional.of(session); } - - protected void removeSessionFromCache(UUID playerUUID) { - ACTIVE_SESSIONS.remove(playerUUID); - } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/BaseUser.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/BaseUser.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/BaseUser.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/BaseUser.java index b83985cc3..ef39843f4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/BaseUser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/BaseUser.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container; +package com.djrapitops.plan.gathering.domain; import com.djrapitops.plugin.utilities.Verify; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/time/GMTimes.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/GMTimes.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/data/time/GMTimes.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/GMTimes.java index 6d5f4c082..64e89b7d4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/time/GMTimes.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/GMTimes.java @@ -14,11 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.time; +package com.djrapitops.plan.gathering.domain; import com.djrapitops.plugin.utilities.Verify; import java.util.Map; +import java.util.Optional; /** * TimeKeeper class that tracks the time spent in each GameMode based on Playtime. @@ -56,6 +57,18 @@ public class GMTimes extends TimeKeeper { return new String[]{SURVIVAL, CREATIVE, ADVENTURE, SPECTATOR}; } + public Optional getMostUsedGameMode() { + long max = 0; + String maxGM = null; + for (Map.Entry entry : times.entrySet()) { + if (entry.getValue() > max) { + max = entry.getValue(); + maxGM = entry.getKey(); + } + } + return Optional.ofNullable(maxGM); + } + /** * Sets times for all 4 gamemodes. *

    diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/GeoInfo.java similarity index 52% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/GeoInfo.java index f5ab5b430..b5ccb9951 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/GeoInfo.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/GeoInfo.java @@ -14,15 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container; +package com.djrapitops.plan.gathering.domain; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.data.store.objects.DateMap; -import com.google.common.base.Objects; +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.domain.DateMap; import java.io.Serializable; -import java.net.Inet6Address; -import java.net.InetAddress; +import java.util.Objects; /** * Data class that contains information about IP and Geolocation. @@ -35,14 +33,10 @@ public class GeoInfo implements DateHolder, Serializable { private final String geolocation; private final long date; - public GeoInfo(InetAddress address, String geolocation, long lastUsed) { - this(formatIP(address), geolocation, lastUsed); - } - - public GeoInfo(String ip, String geolocation, long date) { - this.ip = ip; + public GeoInfo(String geolocation, long lastUsed) { + this.ip = "ip"; this.geolocation = geolocation; - this.date = date; + this.date = lastUsed; } public static DateMap intoDateMap(Iterable geoInfo) { @@ -53,42 +47,7 @@ public class GeoInfo implements DateHolder, Serializable { return map; } - static String formatIP(InetAddress address) { - String ip = address.getHostAddress(); - if ("localhost".equals(ip)) { - return ip; - } - if (address instanceof Inet6Address) { - StringBuilder b = new StringBuilder(); - int i = 0; - for (String part : ip.split(":")) { - if (i >= 3) { - break; - } - - b.append(part).append(':'); - - i++; - } - - return b.append("xx..").toString(); - } else { - StringBuilder b = new StringBuilder(); - int i = 0; - for (String part : ip.split("\\.")) { - if (i >= 2) { - break; - } - - b.append(part).append('.'); - - i++; - } - - return b.append("xx.xx").toString(); - } - } - + @Deprecated public String getIp() { return ip; } @@ -107,20 +66,18 @@ public class GeoInfo implements DateHolder, Serializable { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GeoInfo geoInfo = (GeoInfo) o; - return Objects.equal(ip, geoInfo.ip) && - Objects.equal(geolocation, geoInfo.geolocation); + return Objects.equals(geolocation, geoInfo.geolocation); } @Override public int hashCode() { - return Objects.hashCode(ip, geolocation); + return Objects.hash(ip, geolocation); } @Override public String toString() { return "GeoInfo{" + - "ip='" + ip + '\'' + - ", geolocation='" + geolocation + '\'' + + "geolocation='" + geolocation + '\'' + ", date=" + date + '}'; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/Ping.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/Ping.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/Ping.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/Ping.java index c0f897df8..8b0d0555b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/Ping.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/Ping.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container; +package com.djrapitops.plan.gathering.domain; -import com.djrapitops.plan.data.store.objects.DateObj; +import com.djrapitops.plan.delivery.domain.DateObj; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/PlayerDeath.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/PlayerDeath.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/PlayerDeath.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/PlayerDeath.java index 98b048dfc..33102528d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/PlayerDeath.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/PlayerDeath.java @@ -14,12 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container; +package com.djrapitops.plan.gathering.domain; -import com.djrapitops.plan.data.store.objects.DateHolder; +import com.djrapitops.plan.delivery.domain.DateHolder; import java.util.UUID; +/** + * @deprecated Use {@link PlayerKill} instead. + */ +@Deprecated public class PlayerDeath implements DateHolder { private final UUID killer; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/PlayerKill.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/PlayerKill.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/PlayerKill.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/PlayerKill.java index 8c8fa9f8d..919dee670 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/PlayerKill.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/PlayerKill.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container; +package com.djrapitops.plan.gathering.domain; -import com.djrapitops.plan.data.store.objects.DateHolder; +import com.djrapitops.plan.delivery.domain.DateHolder; import java.util.Objects; import java.util.Optional; @@ -35,6 +35,7 @@ public class PlayerKill implements DateHolder { private final long date; private String victimName; + private String killerName; /** * Creates a PlayerKill object with given parameters. @@ -50,12 +51,15 @@ public class PlayerKill implements DateHolder { } public PlayerKill(UUID victim, String weapon, long date, String victimName) { - this.victim = victim; - this.date = date; - this.weapon = weapon; + this(victim, weapon, date); this.victimName = victimName; } + public PlayerKill(UUID victim, String weapon, long date, String victimName, String killerName) { + this(victim, weapon, date, victimName); + this.killerName = killerName; + } + /** * Get the victim's UUID. * @@ -69,6 +73,10 @@ public class PlayerKill implements DateHolder { return Optional.ofNullable(victimName); } + public Optional getKillerName() { + return Optional.ofNullable(killerName); + } + @Override public long getDate() { return date; @@ -105,4 +113,12 @@ public class PlayerKill implements DateHolder { "date=" + date + ", " + "weapon='" + weapon + "'}"; } + + public boolean isSelfKill() { + return getVictimName().map(v -> v.equals(killerName)).orElse(false); + } + + public boolean isNotSelfKill() { + return !isSelfKill(); + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/Session.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/Session.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/Session.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/Session.java index db1ac4fed..61469ce42 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/Session.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/Session.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container; +package com.djrapitops.plan.gathering.domain; -import com.djrapitops.plan.data.store.containers.DynamicDataContainer; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.data.time.WorldTimes; +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.domain.container.DynamicDataContainer; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.TimeUnit; /** * DataContainer for information about a player's play session. @@ -41,9 +41,10 @@ public class Session extends DynamicDataContainer implements DateHolder { private int mobKills; private int deaths; private long afkTime; + private boolean firstSession; /** - * Creates a new session. + * Creates a new session based on a join event. * * @param uuid UUID of the Player. * @param serverUUID UUID of the server. @@ -65,7 +66,6 @@ public class Session extends DynamicDataContainer implements DateHolder { putSupplier(SessionKeys.START, this::getSessionStart); putSupplier(SessionKeys.WORLD_TIMES, this::getWorldTimes); putSupplier(SessionKeys.PLAYER_KILLS, this::getPlayerKills); - putRawData(SessionKeys.PLAYER_DEATHS, new ArrayList<>()); putSupplier(SessionKeys.MOB_KILL_COUNT, this::getMobKills); putSupplier(SessionKeys.DEATH_COUNT, this::getDeaths); putSupplier(SessionKeys.AFK_TIME, this::getAfkTime); @@ -74,8 +74,6 @@ public class Session extends DynamicDataContainer implements DateHolder { putSupplier(SessionKeys.LENGTH, () -> getValue(SessionKeys.END).orElse(System.currentTimeMillis()) - getUnsafe(SessionKeys.START)); putSupplier(SessionKeys.ACTIVE_TIME, () -> getUnsafe(SessionKeys.LENGTH) - getUnsafe(SessionKeys.AFK_TIME)); - - putRawData(SessionKeys.LONGEST_WORLD_PLAYED, "Key is Deprecated, use WorldAliasSettings#getLongestWorldPlayed(Session) instead."); } /** @@ -92,7 +90,11 @@ public class Session extends DynamicDataContainer implements DateHolder { * @param deaths Death count during the session. * @param afkTime Time spent AFK during the session. */ - public Session(int id, UUID uuid, UUID serverUUID, long sessionStart, long sessionEnd, int mobKills, int deaths, long afkTime) { + public Session( + int id, UUID uuid, UUID serverUUID, + long sessionStart, long sessionEnd, + int mobKills, int deaths, long afkTime + ) { this.sessionStart = sessionStart; worldTimes = new WorldTimes(); playerKills = new ArrayList<>(); @@ -108,17 +110,15 @@ public class Session extends DynamicDataContainer implements DateHolder { putRawData(SessionKeys.END, sessionEnd); putSupplier(SessionKeys.WORLD_TIMES, this::getWorldTimes); putSupplier(SessionKeys.PLAYER_KILLS, this::getPlayerKills); - putRawData(SessionKeys.PLAYER_DEATHS, new ArrayList<>()); putSupplier(SessionKeys.MOB_KILL_COUNT, this::getMobKills); putSupplier(SessionKeys.DEATH_COUNT, this::getDeaths); putSupplier(SessionKeys.AFK_TIME, this::getAfkTime); + putSupplier(SessionKeys.FIRST_SESSION, this::isFirstSession); putSupplier(SessionKeys.PLAYER_KILL_COUNT, () -> getUnsafe(SessionKeys.PLAYER_KILLS).size()); putSupplier(SessionKeys.LENGTH, () -> getValue(SessionKeys.END).orElse(System.currentTimeMillis()) - getUnsafe(SessionKeys.START)); putSupplier(SessionKeys.ACTIVE_TIME, () -> getUnsafe(SessionKeys.LENGTH) - getUnsafe(SessionKeys.AFK_TIME)); - - putRawData(SessionKeys.LONGEST_WORLD_PLAYED, "Key is Deprecated, use WorldAliasSettings#getLongestWorldPlayed(Session) instead."); } /** @@ -199,6 +199,16 @@ public class Session extends DynamicDataContainer implements DateHolder { putRawData(SessionKeys.DB_ID, sessionID); } + public void setAsFirstSessionIfMatches(Long registerDate) { + if (registerDate != null && Math.abs(sessionStart - registerDate) < TimeUnit.SECONDS.toMillis(15L)) { + this.firstSession = true; + } + } + + public boolean isFirstSession() { + return firstSession; + } + public List getPlayerKills() { return playerKills; } @@ -236,7 +246,7 @@ public class Session extends DynamicDataContainer implements DateHolder { public String toString() { return "Session{" + "sessionStart=" + getUnsafe(SessionKeys.START) + - ", sessionEnd=" + getUnsafe(SessionKeys.END) + + ", sessionEnd=" + getValue(SessionKeys.END).orElse(null) + ", worldTimes=" + worldTimes + ", playerKills=" + playerKills + ", mobKills=" + mobKills + diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/TPS.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/TPS.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/TPS.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/TPS.java index b61300648..c7bf0fa7a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/TPS.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/TPS.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container; +package com.djrapitops.plan.gathering.domain; -import com.djrapitops.plan.data.store.objects.DateHolder; +import com.djrapitops.plan.delivery.domain.DateHolder; import java.util.Objects; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/time/TimeKeeper.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/TimeKeeper.java similarity index 99% rename from Plan/common/src/main/java/com/djrapitops/plan/data/time/TimeKeeper.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/TimeKeeper.java index ed474f708..9e9f4ff60 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/time/TimeKeeper.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/TimeKeeper.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.time; +package com.djrapitops.plan.gathering.domain; import com.djrapitops.plugin.utilities.Verify; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/UserInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/UserInfo.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/UserInfo.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/UserInfo.java index 3dca56255..e98104666 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/UserInfo.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/UserInfo.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container; +package com.djrapitops.plan.gathering.domain; import java.util.Objects; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/time/WorldTimes.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/WorldTimes.java similarity index 99% rename from Plan/common/src/main/java/com/djrapitops/plan/data/time/WorldTimes.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/WorldTimes.java index 147772014..31f720aec 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/time/WorldTimes.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/WorldTimes.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.time; +package com.djrapitops.plan.gathering.domain; import java.util.HashMap; import java.util.Map; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/container/builders/TPSBuilder.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/builders/TPSBuilder.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/data/container/builders/TPSBuilder.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/builders/TPSBuilder.java index a91dab8d4..a915d9d70 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/container/builders/TPSBuilder.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/domain/builders/TPSBuilder.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.container.builders; +package com.djrapitops.plan.gathering.domain.builders; -import com.djrapitops.plan.data.container.TPS; +import com.djrapitops.plan.gathering.domain.TPS; /** * Builder for TPS to make it easier to manage. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/EmptyImportSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/EmptyImportSystem.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/system/importing/EmptyImportSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/EmptyImportSystem.java index f2f4e76f6..8f2595010 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/EmptyImportSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/EmptyImportSystem.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing; +package com.djrapitops.plan.gathering.importing; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/ImportSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/ImportSystem.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/system/importing/ImportSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/ImportSystem.java index 1c3e48888..97dbd548e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/ImportSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/ImportSystem.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing; +package com.djrapitops.plan.gathering.importing; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.importing.importers.Importer; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.gathering.importing.importers.Importer; import com.djrapitops.plugin.utilities.Verify; import java.util.*; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/data/ServerImportData.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/data/ServerImportData.java similarity index 68% rename from Plan/common/src/main/java/com/djrapitops/plan/system/importing/data/ServerImportData.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/data/ServerImportData.java index 1319b15f6..8b387e350 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/data/ServerImportData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/data/ServerImportData.java @@ -14,23 +14,24 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing.data; +package com.djrapitops.plan.gathering.importing.data; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.builders.TPSBuilder; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.gathering.domain.builders.TPSBuilder; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; /** * @author Fuzzlemann */ public class ServerImportData { - private Map commandUsages; private List tpsData; - private ServerImportData(Map commandUsages, List tpsData) { - this.commandUsages = commandUsages; + private ServerImportData(List tpsData) { this.tpsData = tpsData; } @@ -38,14 +39,6 @@ public class ServerImportData { return new ServerImportDataBuilder(); } - public Map getCommandUsages() { - return commandUsages; - } - - public void setCommandUsages(Map commandUsages) { - this.commandUsages = commandUsages; - } - public List getTpsData() { return tpsData; } @@ -55,23 +48,12 @@ public class ServerImportData { } public static final class ServerImportDataBuilder { - private final Map commandUsages = new HashMap<>(); private final List tpsData = new ArrayList<>(); private ServerImportDataBuilder() { /* Private Constructor */ } - public ServerImportDataBuilder commandUsage(String command, Integer usages) { - this.commandUsages.put(command, usages); - return this; - } - - public ServerImportDataBuilder commandUsages(Map commandUsages) { - this.commandUsages.putAll(commandUsages); - return this; - } - public ServerImportDataBuilder tpsData(long date, double ticksPerSecond, int players, double cpuUsage, long usedMemory, int entityCount, int chunksLoaded) { TPS tps = TPSBuilder.get() .date(date) @@ -97,7 +79,7 @@ public class ServerImportData { } public ServerImportData build() { - return new ServerImportData(commandUsages, tpsData); + return new ServerImportData(tpsData); } } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/data/UserImportData.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/data/UserImportData.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/system/importing/data/UserImportData.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/data/UserImportData.java index 93d92dca6..79984df96 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/data/UserImportData.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/data/UserImportData.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing.data; +package com.djrapitops.plan.gathering.importing.data; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.data.time.GMTimes; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.gathering.domain.GMTimes; +import com.djrapitops.plan.gathering.domain.PlayerKill; import java.util.*; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/importers/Importer.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/importers/Importer.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/system/importing/importers/Importer.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/importers/Importer.java index a04380c0e..cc9bfe46a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/importing/importers/Importer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/importing/importers/Importer.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.importing.importers; +package com.djrapitops.plan.gathering.importing.importers; public interface Importer { void processImport(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/listeners/ListenerSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/listeners/ListenerSystem.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/system/listeners/ListenerSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/listeners/ListenerSystem.java index 8524de03f..e1e3d7b2b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/listeners/ListenerSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/listeners/ListenerSystem.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.listeners; +package com.djrapitops.plan.gathering.listeners; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.system.SubSystem; +import com.djrapitops.plan.SubSystem; public abstract class ListenerSystem implements SubSystem { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/status/Status.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/listeners/Status.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/system/status/Status.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/listeners/Status.java index 4efd7d4ce..922926b5f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/status/Status.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/listeners/Status.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.status; +package com.djrapitops.plan.gathering.listeners; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TPSCountTimer.java b/Plan/common/src/main/java/com/djrapitops/plan/gathering/timed/TPSCounter.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TPSCountTimer.java rename to Plan/common/src/main/java/com/djrapitops/plan/gathering/timed/TPSCounter.java index 80b7c8d10..354a998d6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/TPSCountTimer.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/gathering/timed/TPSCounter.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks; +package com.djrapitops.plan.gathering.timed; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.db.access.transactions.events.TPSStoreTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.transactions.events.TPSStoreTransaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -36,7 +36,7 @@ import java.util.List; * * @author Rsl1122 */ -public abstract class TPSCountTimer extends AbsRunnable { +public abstract class TPSCounter extends AbsRunnable { protected final List history; @@ -49,7 +49,7 @@ public abstract class TPSCountTimer extends AbsRunnable { protected int latestPlayersOnline = 0; - public TPSCountTimer( + public TPSCounter( DBSystem dbSystem, ServerInfo serverInfo, PluginLogger logger, diff --git a/Plan/common/src/main/java/com/djrapitops/plan/identification/Identifiers.java b/Plan/common/src/main/java/com/djrapitops/plan/identification/Identifiers.java new file mode 100644 index 000000000..2e6c30f87 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/identification/Identifiers.java @@ -0,0 +1,71 @@ +/* + * 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 . + */ +package com.djrapitops.plan.identification; + +import com.djrapitops.plan.delivery.webserver.RequestTarget; +import com.djrapitops.plan.exceptions.connection.BadRequestException; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +/** + * Utility for getting server identifier from different sources. + * + * @author Rsl1122 + */ +@Singleton +public class Identifiers { + + protected final DBSystem dbSystem; + + @Inject + public Identifiers(DBSystem dbSystem) { + this.dbSystem = dbSystem; + } + + public UUID getServerUUID(RequestTarget target) throws BadRequestException { + String serverIndentifier = target.getParameter("server") + .orElseThrow(() -> new BadRequestException("'server' parameter was not defined.")); + + return UUIDUtility.parseFromString(serverIndentifier) + .orElse(getServerUUIDFromName(serverIndentifier)); + } + + private UUID getServerUUIDFromName(String serverName) throws BadRequestException { + return dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverName)) + .map(Server::getUuid) + .orElseThrow(() -> new BadRequestException("Given 'server' was not found in the database: '" + serverName + "'")); + } + + public UUID getPlayerUUID(RequestTarget target) throws BadRequestException { + String playerIdentifier = target.getParameter("player") + .orElseThrow(() -> new BadRequestException("'player' parameter was not defined.")); + + return UUIDUtility.parseFromString(playerIdentifier) + .orElse(getPlayerUUIDFromName(playerIdentifier)); + } + + private UUID getPlayerUUIDFromName(String playerName) throws BadRequestException { + return dbSystem.getDatabase() + .query(UserIdentifierQueries.fetchPlayerUUIDOf(playerName)) + .orElseThrow(() -> new BadRequestException("Given 'player' was not found in the database: '" + playerName + "'")); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/Server.java b/Plan/common/src/main/java/com/djrapitops/plan/identification/Server.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/server/Server.java rename to Plan/common/src/main/java/com/djrapitops/plan/identification/Server.java index 8444fda99..b9abb4fa9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/Server.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/identification/Server.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server; +package com.djrapitops.plan.identification; import java.util.Objects; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/identification/ServerInfo.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerInfo.java rename to Plan/common/src/main/java/com/djrapitops/plan/identification/ServerInfo.java index 4bbb56980..daa2cb78c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerInfo.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/identification/ServerInfo.java @@ -14,13 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server; +package com.djrapitops.plan.identification; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.identification.properties.ServerProperties; import com.djrapitops.plugin.utilities.Verify; +import java.util.Optional; import java.util.UUID; /** @@ -47,6 +48,10 @@ public abstract class ServerInfo implements SubSystem { return getServer().getUuid(); } + public Optional getServerUUIDSafe() { + return Optional.ofNullable(server).map(Server::getUuid); + } + public ServerProperties getServerProperties() { return serverProperties; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerInfoFile.java b/Plan/common/src/main/java/com/djrapitops/plan/identification/ServerInfoFile.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerInfoFile.java rename to Plan/common/src/main/java/com/djrapitops/plan/identification/ServerInfoFile.java index 2018b23e2..974bd805f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerInfoFile.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/identification/ServerInfoFile.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server; +package com.djrapitops.plan.identification; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.settings.config.Config; -import com.djrapitops.plan.system.settings.config.ConfigReader; +import com.djrapitops.plan.settings.config.Config; +import com.djrapitops.plan.settings.config.ConfigReader; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.utilities.Verify; import javax.inject.Inject; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerServerInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/identification/ServerServerInfo.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerServerInfo.java rename to Plan/common/src/main/java/com/djrapitops/plan/identification/ServerServerInfo.java index c6e31ddf2..03310d69c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/ServerServerInfo.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/identification/ServerServerInfo.java @@ -14,19 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server; +package com.djrapitops.plan.identification; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.transactions.StoreServerInformationTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.webserver.WebServer; +import com.djrapitops.plan.delivery.webserver.WebServer; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.properties.ServerProperties; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.transactions.StoreServerInformationTransaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import dagger.Lazy; @@ -125,7 +125,7 @@ public class ServerServerInfo extends ServerInfo { server = foundServer.get(); // Update information - String name = config.get(PluginSettings.SERVER_NAME).replaceAll("[^a-zA-Z0-9_\\s]", "_"); + String name = config.get(PluginSettings.SERVER_NAME); server.setName("plan".equalsIgnoreCase(name) ? "Server " + server.getId() : name); String webAddress = webServer.get().getAccessAddress(); @@ -162,7 +162,7 @@ public class ServerServerInfo extends ServerInfo { private Server createServerObject(UUID serverUUID) { String webAddress = webServer.get().getAccessAddress(); - String name = config.get(PluginSettings.SERVER_NAME).replaceAll("[^a-zA-Z0-9_\\s]", "_"); + String name = config.get(PluginSettings.SERVER_NAME); int maxPlayers = serverProperties.getMaxPlayers(); return new Server(-1, serverUUID, name, webAddress, maxPlayers); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/uuid/UUIDUtility.java b/Plan/common/src/main/java/com/djrapitops/plan/identification/UUIDUtility.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/uuid/UUIDUtility.java rename to Plan/common/src/main/java/com/djrapitops/plan/identification/UUIDUtility.java index 53492f257..1c12ae08b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/uuid/UUIDUtility.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/identification/UUIDUtility.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.uuid; +package com.djrapitops.plan.identification; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries; -import com.djrapitops.plan.system.database.DBSystem; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries; import com.djrapitops.plugin.api.utility.UUIDFetcher; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -50,6 +50,14 @@ public class UUIDUtility { this.errorHandler = errorHandler; } + public static Optional parseFromString(String uuidString) { + try { + return Optional.of(UUID.fromString(uuidString)); + } catch (IllegalArgumentException malformedUUIDException) { + return Optional.empty(); + } + } + /** * Get UUID of a player. * diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java b/Plan/common/src/main/java/com/djrapitops/plan/identification/properties/ServerProperties.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java rename to Plan/common/src/main/java/com/djrapitops/plan/identification/properties/ServerProperties.java index fc9855c00..2c51d854e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/server/properties/ServerProperties.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/identification/properties/ServerProperties.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.server.properties; +package com.djrapitops.plan.identification.properties; import java.util.function.IntSupplier; import java.util.function.Supplier; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/modules/APFModule.java b/Plan/common/src/main/java/com/djrapitops/plan/modules/APFModule.java index 3c9c06148..96ece2542 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/modules/APFModule.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/modules/APFModule.java @@ -47,7 +47,7 @@ public class APFModule { @Named("currentVersion") @Singleton String provideCurrentVersion(IPlugin plugin) { - return plugin.getVersion(); + return plugin.getVersion().replace("%buildNumber%", "?"); } @Provides diff --git a/Plan/common/src/main/java/com/djrapitops/plan/modules/FilesModule.java b/Plan/common/src/main/java/com/djrapitops/plan/modules/FilesModule.java index c75ec9faa..bbe513b9b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/modules/FilesModule.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/modules/FilesModule.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.modules; -import com.djrapitops.plan.system.file.PlanFiles; +import com.djrapitops.plan.storage.file.PlanFiles; import dagger.Module; import dagger.Provides; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/modules/ProxySuperClassBindingModule.java b/Plan/common/src/main/java/com/djrapitops/plan/modules/ProxySuperClassBindingModule.java index 72ec7f1da..e14f14bf9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/modules/ProxySuperClassBindingModule.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/modules/ProxySuperClassBindingModule.java @@ -16,18 +16,12 @@ */ package com.djrapitops.plan.modules; -import com.djrapitops.plan.api.CommonAPI; -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.database.ProxyDBSystem; -import com.djrapitops.plan.system.importing.EmptyImportSystem; -import com.djrapitops.plan.system.importing.ImportSystem; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.ProxyInfoSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.connection.ProxyConnectionSystem; -import com.djrapitops.plan.system.settings.ConfigSystem; -import com.djrapitops.plan.system.settings.ProxyConfigSystem; +import com.djrapitops.plan.gathering.importing.EmptyImportSystem; +import com.djrapitops.plan.gathering.importing.ImportSystem; +import com.djrapitops.plan.settings.ConfigSystem; +import com.djrapitops.plan.settings.ProxyConfigSystem; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.ProxyDBSystem; import dagger.Binds; import dagger.Module; @@ -39,21 +33,12 @@ import dagger.Module; @Module public interface ProxySuperClassBindingModule { - @Binds - PlanAPI bindProxyPlanAPI(CommonAPI proxyAPI); - @Binds DBSystem bindProxyDatabaseSystem(ProxyDBSystem proxyDBSystem); @Binds ConfigSystem bindProxyConfigSystem(ProxyConfigSystem proxyConfigSystem); - @Binds - InfoSystem bindProxyInfoSystem(ProxyInfoSystem proxyInfoSystem); - - @Binds - ConnectionSystem bindProxyConnectionSystem(ProxyConnectionSystem proxyConnectionSystem); - @Binds ImportSystem bindImportSystem(EmptyImportSystem emptyImportSystem); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/modules/ServerSuperClassBindingModule.java b/Plan/common/src/main/java/com/djrapitops/plan/modules/ServerSuperClassBindingModule.java deleted file mode 100644 index 56a1e8e3d..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/modules/ServerSuperClassBindingModule.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.modules; - -import com.djrapitops.plan.api.CommonAPI; -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.ServerInfoSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.connection.ServerConnectionSystem; -import dagger.Binds; -import dagger.Module; - -/** - * Module for binding Server specific classes to the interface implementations. - * - * @author Rsl1122 - */ -@Module -public interface ServerSuperClassBindingModule { - - @Binds - PlanAPI bindServerPlanAPI(CommonAPI serverAPI); - - @Binds - InfoSystem bindServerInfoSystem(ServerInfoSystem serverInfoSystem); - - @Binds - ConnectionSystem bindServerConnectionSystem(ServerConnectionSystem serverConnectionSystem); -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/modules/SystemObjectProvidingModule.java b/Plan/common/src/main/java/com/djrapitops/plan/modules/SystemObjectProvidingModule.java index 4d9708407..ec5efe87a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/modules/SystemObjectProvidingModule.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/modules/SystemObjectProvidingModule.java @@ -16,10 +16,10 @@ */ package com.djrapitops.plan.modules; -import com.djrapitops.plan.data.plugin.PluginsConfigSection; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.LocaleSystem; -import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.ExtensionSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.LocaleSystem; import dagger.Module; import dagger.Provides; @@ -41,8 +41,8 @@ public class SystemObjectProvidingModule { @Provides @Singleton - PluginsConfigSection providePluginsConfigSection(PlanConfig config) { - return config.getPluginsConfigSection(); + ExtensionSettings providePluginsConfigSection(PlanConfig config) { + return config.getExtensionSettings(); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/CriticalCallable.java b/Plan/common/src/main/java/com/djrapitops/plan/processing/CriticalCallable.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/system/processing/CriticalCallable.java rename to Plan/common/src/main/java/com/djrapitops/plan/processing/CriticalCallable.java index 073ed62ba..f6293a34b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/CriticalCallable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/processing/CriticalCallable.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.processing; +package com.djrapitops.plan.processing; import java.util.concurrent.Callable; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/CriticalRunnable.java b/Plan/common/src/main/java/com/djrapitops/plan/processing/CriticalRunnable.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/system/processing/CriticalRunnable.java rename to Plan/common/src/main/java/com/djrapitops/plan/processing/CriticalRunnable.java index 3a61079ec..38266de58 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/CriticalRunnable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/processing/CriticalRunnable.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.processing; +package com.djrapitops.plan.processing; public interface CriticalRunnable extends Runnable { } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java b/Plan/common/src/main/java/com/djrapitops/plan/processing/Processing.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java rename to Plan/common/src/main/java/com/djrapitops/plan/processing/Processing.java index ca08796ee..704ffdc06 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/Processing.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/processing/Processing.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.processing; +package com.djrapitops.plan.processing; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import dagger.Lazy; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; import javax.inject.Inject; import javax.inject.Singleton; @@ -54,7 +54,12 @@ public class Processing implements SubSystem { } protected ExecutorService createExecutor(int i, String s) { - return Executors.newFixedThreadPool(i, new ThreadFactoryBuilder().setNameFormat(s).build()); + return Executors.newFixedThreadPool(i, + new BasicThreadFactory.Builder() + .namingPattern(s) + .uncaughtExceptionHandler((thread, throwable) -> + errorHandler.log(L.WARN, Processing.class, throwable) + ).build()); } public void submit(Runnable runnable) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/player/MobKillProcessor.java b/Plan/common/src/main/java/com/djrapitops/plan/processing/processors/player/MobKillProcessor.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/player/MobKillProcessor.java rename to Plan/common/src/main/java/com/djrapitops/plan/processing/processors/player/MobKillProcessor.java index 5199d641b..10f276015 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/player/MobKillProcessor.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/processing/processors/player/MobKillProcessor.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.processing.processors.player; +package com.djrapitops.plan.processing.processors.player; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.processing.CriticalRunnable; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.processing.CriticalRunnable; import java.util.Optional; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/player/PlayerKillProcessor.java b/Plan/common/src/main/java/com/djrapitops/plan/processing/processors/player/PlayerKillProcessor.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/player/PlayerKillProcessor.java rename to Plan/common/src/main/java/com/djrapitops/plan/processing/processors/player/PlayerKillProcessor.java index 44930fd85..2b832e963 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/player/PlayerKillProcessor.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/processing/processors/player/PlayerKillProcessor.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.processing.processors.player; +package com.djrapitops.plan.processing.processors.player; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.processing.CriticalRunnable; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.processing.CriticalRunnable; import java.util.Optional; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/query/CommonQueriesImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/query/CommonQueriesImplementation.java index c08e867fc..bba7113ce 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/query/CommonQueriesImplementation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/query/CommonQueriesImplementation.java @@ -16,16 +16,14 @@ */ package com.djrapitops.plan.query; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.containers.ContainerFetchQueries; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries; -import com.djrapitops.plan.db.access.queries.schema.H2SchemaQueries; -import com.djrapitops.plan.db.access.queries.schema.MySQLSchemaQueries; -import com.djrapitops.plan.db.access.queries.schema.SQLiteSchemaQueries; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; +import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries; +import com.djrapitops.plan.storage.database.queries.schema.H2SchemaQueries; +import com.djrapitops.plan.storage.database.queries.schema.MySQLSchemaQueries; +import com.djrapitops.plan.storage.database.queries.schema.SQLiteSchemaQueries; import java.util.Optional; import java.util.Set; @@ -41,21 +39,12 @@ public class CommonQueriesImplementation implements CommonQueries { @Override public long fetchPlaytime(UUID playerUUID, UUID serverUUID, long after, long before) { - // TODO Replace with single query later - PlayerContainer player = db.query(ContainerFetchQueries.fetchPlayerContainer(playerUUID)); - return SessionsMutator.forContainer(player) - .filterSessionsBetween(after, before) - .filterPlayedOnServer(serverUUID) - .toPlaytime(); + return db.query(SessionQueries.playtimeOfPlayer(after, before, playerUUID)).getOrDefault(serverUUID, 0L); } @Override public long fetchLastSeen(UUID playerUUID, UUID serverUUID) { - // TODO Replace with single query later - PlayerContainer player = db.query(ContainerFetchQueries.fetchPlayerContainer(playerUUID)); - return SessionsMutator.forContainer(player) - .filterPlayedOnServer(serverUUID) - .toLastSeen(); + return db.query(SessionQueries.lastSeen(playerUUID, serverUUID)); } @Override diff --git a/Plan/common/src/main/java/com/djrapitops/plan/query/QueryServiceImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/query/QueryServiceImplementation.java index 746c371d1..7e9dba053 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/query/QueryServiceImplementation.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/query/QueryServiceImplementation.java @@ -16,14 +16,13 @@ */ package com.djrapitops.plan.query; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.QueryAPIExecutable; -import com.djrapitops.plan.db.access.QueryAPIQuery; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.QueryAPIExecutable; +import com.djrapitops.plan.storage.database.queries.QueryAPIQuery; +import com.djrapitops.plan.storage.database.transactions.Transaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -127,7 +126,7 @@ public class QueryServiceImplementation implements QueryService { @Override public Optional getServerUUID() { - return Optional.ofNullable(serverInfo.getServer()).map(Server::getUuid); + return serverInfo.getServerUUIDSafe(); } @Override diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/BukkitConfigSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/BukkitConfigSystem.java similarity index 79% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/BukkitConfigSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/BukkitConfigSystem.java index 6cc19dc24..ef4b8af42 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/BukkitConfigSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/BukkitConfigSystem.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings; +package com.djrapitops.plan.settings; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.settings.changes.ConfigUpdater; -import com.djrapitops.plan.system.settings.config.ConfigReader; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.network.ServerSettingsManager; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.settings.theme.Theme; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.config.ConfigReader; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.changes.ConfigUpdater; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.network.ServerSettingsManager; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -62,7 +62,7 @@ public class BukkitConfigSystem extends ConfigSystem { @Override public void enable() throws EnableException { super.enable(); - if (config.isTrue(PluginSettings.BUNGEE_COPY_CONFIG)) { + if (config.isTrue(PluginSettings.PROXY_COPY_CONFIG)) { serverSettingsManager.enable(); } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/ConfigSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/ConfigSystem.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/ConfigSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/ConfigSystem.java index 42d6bb700..284b40568 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/ConfigSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/ConfigSystem.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings; +package com.djrapitops.plan.settings; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.settings.theme.Theme; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.debug.*; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/Permissions.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/Permissions.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/Permissions.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/Permissions.java index 02d5e43b3..455f696c7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/Permissions.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/Permissions.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings; +package com.djrapitops.plan.settings; /** * Permissions class is used easily check every permission node. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/ProxyConfigSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/ProxyConfigSystem.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/ProxyConfigSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/ProxyConfigSystem.java index ef4369e10..162596839 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/ProxyConfigSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/ProxyConfigSystem.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings; +package com.djrapitops.plan.settings; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.settings.changes.ConfigUpdater; -import com.djrapitops.plan.system.settings.config.ConfigReader; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.network.NetworkSettingManager; -import com.djrapitops.plan.system.settings.theme.Theme; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.config.ConfigReader; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.changes.ConfigUpdater; +import com.djrapitops.plan.settings.network.NetworkSettingManager; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/SettingsServiceImplementation.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/SettingsServiceImplementation.java new file mode 100644 index 000000000..3d7a2d291 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/SettingsServiceImplementation.java @@ -0,0 +1,104 @@ +/* + * 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 . + */ +package com.djrapitops.plan.settings; + +import com.djrapitops.plan.settings.config.ConfigNode; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plugin.logging.L; +import com.djrapitops.plugin.logging.error.ErrorHandler; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +/** + * Implementation for {@link SettingsService}. + * + * @author Rsl1122 + */ +@Singleton +public class SettingsServiceImplementation implements SettingsService { + + private final PlanConfig config; + private final ErrorHandler errorHandler; + + @Inject + public SettingsServiceImplementation( + PlanConfig config, + ErrorHandler errorHandler + ) { + this.config = config; + this.errorHandler = errorHandler; + } + + public void register() { + SettingsService.SettingsServiceHolder.set(this); + } + + @Override + public String getString(String path, Supplier defaultValue) { + String pluginPath = getPluginPath(path); + Optional node = config.getNode(pluginPath); + if (node.isPresent()) { + return node.get().getString(); + } else { + set(pluginPath, defaultValue); + return config.getString(pluginPath); + } + } + + public void set(String pluginPath, Supplier defaultValue) { + config.set(pluginPath, defaultValue.get()); + + try { + config.save(); + } catch (IOException e) { + errorHandler.log(L.ERROR, this.getClass(), e); + } + } + + @Override + public Integer getInteger(String path, Supplier defaultValue) { + String pluginPath = getPluginPath(path); + Optional node = config.getNode(pluginPath); + if (node.isPresent()) { + return node.get().getInteger(); + } else { + set(pluginPath, defaultValue); + return config.getInteger(pluginPath); + } + } + + @Override + public List getStringList(String path, Supplier> defaultValue) { + String pluginPath = getPluginPath(path); + Optional node = config.getNode(pluginPath); + if (node.isPresent()) { + return node.get().getStringList(); + } else { + set(pluginPath, defaultValue); + return config.getStringList(pluginPath); + } + } + + private String getPluginPath(String path) { + return "Plugins." + path; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/SpongeConfigSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/SpongeConfigSystem.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/SpongeConfigSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/SpongeConfigSystem.java index 8ed90a2cc..5696d2f28 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/SpongeConfigSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/SpongeConfigSystem.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings; +package com.djrapitops.plan.settings; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.settings.changes.ConfigUpdater; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.network.ServerSettingsManager; -import com.djrapitops.plan.system.settings.paths.DataGatheringSettings; -import com.djrapitops.plan.system.settings.paths.WebserverSettings; -import com.djrapitops.plan.system.settings.theme.Theme; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.changes.ConfigUpdater; +import com.djrapitops.plan.settings.config.paths.DataGatheringSettings; +import com.djrapitops.plan.settings.config.paths.WebserverSettings; +import com.djrapitops.plan.settings.network.ServerSettingsManager; +import com.djrapitops.plan.settings.theme.Theme; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/Config.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/Config.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/Config.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/Config.java index 1213ebb2d..5cbc930e2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/Config.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/Config.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.djrapitops.plan.system.settings.config; +package com.djrapitops.plan.settings.config; import com.djrapitops.plugin.utilities.Verify; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigNode.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigNode.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigNode.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigNode.java index ea0c9ee4b..33d2e4a95 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigNode.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigNode.java @@ -21,9 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.djrapitops.plan.system.settings.config; +package com.djrapitops.plan.settings.config; import com.djrapitops.plugin.utilities.Verify; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.util.*; @@ -78,7 +79,7 @@ public class ConfigNode { } private String[] splitPathInTwo(String path) { - String[] split = path.split("\\.", 2); + String[] split = StringUtils.split(path, ".", 2); if (split.length <= 1) { return new String[]{split[0], ""}; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigReader.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigReader.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigReader.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigReader.java index f10b8d707..b9e62bd72 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigReader.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigReader.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.config; +package com.djrapitops.plan.settings.config; import com.djrapitops.plugin.utilities.Verify; @@ -144,6 +144,7 @@ public class ConfigReader implements Closeable { private ConfigNode parseNode(String line) { // Parse a node "Key: value" + // Can not use StringUtils.split(line, ":", 2) - Relies on 2nd empty String for parent node parsing String[] keyAndValue = line.split(":", 2); if (keyAndValue.length <= 1) { return handleMultiline(line); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigValueParser.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigValueParser.java index ead1610bb..cc4a51258 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigValueParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigValueParser.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.config; +package com.djrapitops.plan.settings.config; import com.djrapitops.plugin.utilities.Verify; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import java.util.ArrayList; @@ -161,7 +162,7 @@ public interface ConfigValueParser { @Override public List compose(String fromValue) { List values = new ArrayList<>(); - for (String line : fromValue.split("\\n")) { + for (String line : StringUtils.split(fromValue, "\n")) { if (line.trim().isEmpty()) { continue; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigWriter.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigWriter.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigWriter.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigWriter.java index 1b6ceb0b8..805dc76d7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/ConfigWriter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ConfigWriter.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.config; +package com.djrapitops.plan.settings.config; import com.djrapitops.plugin.utilities.Verify; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -110,10 +111,10 @@ public class ConfigWriter { if (value == null || value.isEmpty()) { addKey(key, lines); - } else if (value.contains("\n")) { + } else if (StringUtils.contains(value, "\n")) { // List values include newline characters, // see ConfigValueParser.StringListParser - addListValue(key, value.split("\\n"), lines); + addListValue(key, StringUtils.split(value, "\n"), lines); } else { addNormalValue(key, value, lines); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/PluginsConfigSection.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ExtensionSettings.java similarity index 75% rename from Plan/common/src/main/java/com/djrapitops/plan/data/plugin/PluginsConfigSection.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/ExtensionSettings.java index 85602e04c..75fd0cbfd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/data/plugin/PluginsConfigSection.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/ExtensionSettings.java @@ -14,36 +14,27 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.data.plugin; - -import com.djrapitops.plan.system.settings.config.ConfigNode; -import com.djrapitops.plan.system.settings.config.PlanConfig; +package com.djrapitops.plan.settings.config; import java.io.IOException; import java.util.HashSet; import java.util.Set; /** - * Class responsible for generating and generating settings for PluginData - * objects to the config. + * Class responsible for generating and generating settings for DataExtensions to the config. * * @author Rsl1122 */ -public class PluginsConfigSection { +public class ExtensionSettings { private final PlanConfig config; - public PluginsConfigSection( + public ExtensionSettings( PlanConfig config ) { this.config = config; } - @Deprecated - public boolean hasSection(PluginData dataSource) { - return hasSection(dataSource.getSourcePlugin()); - } - public boolean hasSection(String pluginName) { ConfigNode section = getPluginsSection(); return section.getNode(pluginName + ".Enabled").isPresent(); @@ -54,11 +45,6 @@ public class PluginsConfigSection { .orElse(config.addNode("Plugins")); } - @Deprecated - public void createSection(PluginData dataSource) throws IOException { - createSection(dataSource.getSourcePlugin()); - } - public void createSection(String pluginName) throws IOException { ConfigNode section = getPluginsSection(); @@ -67,11 +53,6 @@ public class PluginsConfigSection { section.save(); } - @Deprecated - public boolean isEnabled(PluginData dataSource) { - return isEnabled(dataSource.getSourcePlugin()); - } - public boolean isEnabled(String pluginName) { ConfigNode section = getPluginsSection(); return section.getBoolean(pluginName + ".Enabled"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/PlanConfig.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/PlanConfig.java index 7e8879f2a..4b32df8c2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/PlanConfig.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/PlanConfig.java @@ -14,11 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.config; +package com.djrapitops.plan.settings.config; -import com.djrapitops.plan.data.plugin.PluginsConfigSection; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.system.settings.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.config.paths.key.Setting; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.utilities.Verify; @@ -39,7 +38,7 @@ import java.util.concurrent.TimeUnit; @Singleton public class PlanConfig extends Config { - private final PluginsConfigSection pluginsConfigSection; + private final ExtensionSettings extensionSettings; private final WorldAliasSettings worldAliasSettings; private final PluginLogger logger; @@ -54,7 +53,7 @@ public class PlanConfig extends Config { this.worldAliasSettings = worldAliasSettings; this.logger = logger; - pluginsConfigSection = new PluginsConfigSection(this); + extensionSettings = new ExtensionSettings(this); } public int getTimeZoneOffsetHours() { @@ -121,8 +120,12 @@ public class PlanConfig extends Config { set(setting.getPath(), value); } - public PluginsConfigSection getPluginsConfigSection() { - return pluginsConfigSection; + public TimeZone getTimeZone() { + return get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT"); + } + + public ExtensionSettings getExtensionSettings() { + return extensionSettings; } public WorldAliasSettings getWorldAliasSettings() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/WorldAliasSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/WorldAliasSettings.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/WorldAliasSettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/WorldAliasSettings.java index 8c9678c3e..da4e47a23 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/config/WorldAliasSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/WorldAliasSettings.java @@ -14,16 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.config; +package com.djrapitops.plan.settings.config; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.time.GMTimes; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.paths.DisplaySettings; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.formatting.Formatters; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.formatting.Formatter; +import com.djrapitops.plan.delivery.formatting.Formatters; +import com.djrapitops.plan.gathering.domain.GMTimes; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.WorldTimes; +import com.djrapitops.plan.processing.Processing; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.GenericLang; +import com.djrapitops.plan.settings.locale.lang.HtmlLang; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; import com.djrapitops.plugin.utilities.Verify; @@ -47,17 +50,20 @@ public class WorldAliasSettings { private final Lazy config; private final Supplier> percentageFormatter; + private final Lazy locale; private final Processing processing; private final ErrorHandler errorHandler; @Inject public WorldAliasSettings( Lazy config, + Lazy locale, Lazy formatters, Processing processing, ErrorHandler errorHandler ) { this.config = config; + this.locale = locale; this.processing = processing; this.errorHandler = errorHandler; @@ -112,7 +118,7 @@ public class WorldAliasSettings { String worldName = entry.getKey(); long playtime = entry.getValue(); - if (!aliases.contains(worldName)) { + if (worldName != null && !aliases.contains(worldName)) { addWorld(worldName); } @@ -153,13 +159,13 @@ public class WorldAliasSettings { ConfigNode aliases = getAliasSection(); if (!session.supports(SessionKeys.WORLD_TIMES)) { - return "No World Time Data"; + return locale.get().getString(HtmlLang.UNIT_NO_DATA); } WorldTimes worldTimes = session.getValue(SessionKeys.WORLD_TIMES).orElse(new WorldTimes()); if (!session.supports(SessionKeys.END)) { return worldTimes.getCurrentWorld() .map(currentWorld -> "Current: " + (aliases.contains(currentWorld) ? aliases.getString(currentWorld) : currentWorld)) - .orElse("Current: Unavailable"); + .orElse("Current: " + locale.get().getString(GenericLang.UNAVAILABLE)); } Map playtimePerAlias = getPlaytimePerAlias(worldTimes); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigChange.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigChange.java index 32048a335..142795eb5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigChange.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigChange.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.changes; +package com.djrapitops.plan.settings.config.changes; -import com.djrapitops.plan.system.settings.config.Config; +import com.djrapitops.plan.settings.config.Config; /** * Represents a change made to the config structure. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigUpdater.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigUpdater.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigUpdater.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigUpdater.java index a58a183a4..0720aa702 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/changes/ConfigUpdater.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/changes/ConfigUpdater.java @@ -14,13 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.changes; +package com.djrapitops.plan.settings.config.changes; -import com.djrapitops.plan.system.settings.config.Config; +import com.djrapitops.plan.settings.config.Config; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.google.common.annotations.VisibleForTesting; import javax.inject.Inject; import javax.inject.Singleton; @@ -52,7 +51,7 @@ public class ConfigUpdater { config.save(); } - @VisibleForTesting + // VisibleForTesting ConfigChange[] configEnhancementPatch() { return new ConfigChange[]{ new ConfigChange.Moved("Plugin.Locale", "Plugin.Logging.Locale"), @@ -126,7 +125,19 @@ public class ConfigUpdater { new ConfigChange.Removed("Analysis"), new ConfigChange.Removed("Data"), new ConfigChange.Removed("Customization"), - new ConfigChange.Removed("Theme") + new ConfigChange.Removed("Theme"), + + // 5.0.0 + new ConfigChange.Removed("Display_options.Sessions.Replace_accordion_with_table"), + new ConfigChange.Removed("Display_options.Sessions.Show_most_played_world_in_title"), + new ConfigChange.Removed("Time.Thresholds.Activity_index.Login_threshold"), + new ConfigChange.Removed("Time.Periodic_tasks.Clean_caches_every"), + new ConfigChange.Removed("Time.Periodic_tasks.Analysis_refresh_every"), + new ConfigChange.Removed("Display_options.Show_player_IPs"), + new ConfigChange.Removed("Export.Parts.JavaScript_and_CSS"), + new ConfigChange.Moved("Plugins.LiteBans", "Plugins.Litebans"), + new ConfigChange.Moved("Plugins.BuyCraft", "Plugins.Buycraft"), + new ConfigChange.Moved("Plugin.Configuration.Allow_bungeecord_to_manage_settings", "Plugin.Configuration.Allow_proxy_to_manage_settings") }; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DataGatheringSettings.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DataGatheringSettings.java index e5f851c61..58051c53e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DataGatheringSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DataGatheringSettings.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.system.settings.paths.key.BooleanSetting; -import com.djrapitops.plan.system.settings.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.BooleanSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; /** * {@link Setting} values that are in "Data_gathering" section. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DatabaseSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DatabaseSettings.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DatabaseSettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DatabaseSettings.java index 1aa35782a..8db2b787c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DatabaseSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DatabaseSettings.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.StringSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.StringSetting; +import com.djrapitops.plan.storage.database.DBType; import org.apache.commons.lang3.math.NumberUtils; /** diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DisplaySettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DisplaySettings.java similarity index 79% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DisplaySettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DisplaySettings.java index 8da90a2b2..272d2c1b8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/DisplaySettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/DisplaySettings.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.system.settings.config.ConfigNode; -import com.djrapitops.plan.system.settings.paths.key.BooleanSetting; -import com.djrapitops.plan.system.settings.paths.key.IntegerSetting; -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.StringSetting; +import com.djrapitops.plan.settings.config.ConfigNode; +import com.djrapitops.plan.settings.config.paths.key.BooleanSetting; +import com.djrapitops.plan.settings.config.paths.key.IntegerSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.StringSetting; /** * {@link Setting} values that are in "Display_options" section. @@ -30,14 +30,11 @@ import com.djrapitops.plan.system.settings.paths.key.StringSetting; public class DisplaySettings { public static final Setting THEME = new StringSetting("Display_options.Theme"); - public static final Setting REPLACE_SESSION_ACCORDION_WITH_TABLE = new BooleanSetting("Display_options.Sessions.Replace_accordion_with_table"); public static final Setting SESSIONS_PER_PAGE = new IntegerSetting("Display_options.Sessions.Show_on_page"); - public static final Setting SESSION_MOST_PLAYED_WORLD_IN_TITLE = new BooleanSetting("Display_options.Sessions.Show_most_played_world_in_title"); public static final Setting ORDER_WORLD_PIE_BY_PERC = new BooleanSetting("Display_options.Sessions.Order_world_pies_by_percentage"); public static final Setting PLAYERS_PER_SERVER_PAGE = new IntegerSetting("Display_options.Players_table.Show_on_server_page"); public static final Setting PLAYERS_PER_PLAYERS_PAGE = new IntegerSetting("Display_options.Players_table.Show_on_players_page"); public static final Setting OPEN_PLAYER_LINKS_IN_NEW_TAB = new BooleanSetting("Display_options.Open_player_links_in_new_tab"); - public static final Setting PLAYER_IPS = new BooleanSetting("Display_options.Show_player_IPs"); public static final Setting GAPS_IN_GRAPH_DATA = new BooleanSetting("Display_options.Graphs.Show_gaps_in_data"); public static final Setting GRAPH_TPS_THRESHOLD_HIGH = new IntegerSetting("Display_options.Graphs.TPS.High_threshold"); public static final Setting GRAPH_TPS_THRESHOLD_MED = new IntegerSetting("Display_options.Graphs.TPS.Medium_threshold"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/ExportSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/ExportSettings.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/ExportSettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/ExportSettings.java index 7a10c4306..ea7ab0a79 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/ExportSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/ExportSettings.java @@ -14,11 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.system.settings.paths.key.BooleanSetting; -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.StringSetting; +import com.djrapitops.plan.settings.config.paths.key.BooleanSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.StringSetting; +import com.djrapitops.plan.settings.config.paths.key.TimeSetting; /** * {@link Setting} values that are in "Export" section. @@ -29,13 +30,13 @@ public class ExportSettings { public static final Setting HTML_EXPORT_PATH = new StringSetting("Export.HTML_Export_path"); public static final Setting JSON_EXPORT_PATH = new StringSetting("Export.JSON_Export_path"); - public static final Setting JS_AND_CSS = new BooleanSetting("Export.Parts.JavaScript_and_CSS"); public static final Setting PLAYER_PAGES = new BooleanSetting("Export.Parts.Player_pages"); public static final Setting PLAYER_JSON = new BooleanSetting("Export.Parts.Player_JSON"); public static final Setting PLAYERS_PAGE = new BooleanSetting("Export.Parts.Players_page"); public static final Setting SERVER_PAGE = new BooleanSetting("Export.Parts.Server_page"); public static final Setting SERVER_JSON = new BooleanSetting("Export.Parts.Server_JSON"); public static final Setting EXPORT_ON_ONLINE_STATUS_CHANGE = new BooleanSetting("Export.Export_player_on_login_and_logout"); + public static final Setting EXPORT_PERIOD = new TimeSetting("Export.Server_refresh_period"); private ExportSettings() { /* static variable class */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/FormatSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/FormatSettings.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/FormatSettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/FormatSettings.java index d476d3583..fef288f79 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/FormatSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/FormatSettings.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.system.settings.paths.key.BooleanSetting; -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.StringSetting; +import com.djrapitops.plan.settings.config.paths.key.BooleanSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.StringSetting; /** * {@link Setting} values that are in "Formatting" section. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/PluginSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/PluginSettings.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/PluginSettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/PluginSettings.java index c55d4665a..bb3a1b2a6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/PluginSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/PluginSettings.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.system.settings.paths.key.BooleanSetting; -import com.djrapitops.plan.system.settings.paths.key.IntegerSetting; -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.StringSetting; +import com.djrapitops.plan.settings.config.paths.key.BooleanSetting; +import com.djrapitops.plan.settings.config.paths.key.IntegerSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.StringSetting; /** * {@link Setting} values that are in "Server" or "Plugin" section. @@ -36,7 +36,7 @@ public class PluginSettings { public static final Setting KEEP_LOGS_DAYS = new IntegerSetting("Plugin.Logging.Delete_logs_after_days", Setting::timeValidator); public static final Setting CHECK_FOR_UPDATES = new BooleanSetting("Plugin.Update_notifications.Check_for_updates"); public static final Setting NOTIFY_ABOUT_DEV_RELEASES = new BooleanSetting("Plugin.Update_notifications.Notify_about_DEV_releases"); - public static final Setting BUNGEE_COPY_CONFIG = new BooleanSetting("Plugin.Configuration.Allow_bungeecord_to_manage_settings"); + public static final Setting PROXY_COPY_CONFIG = new BooleanSetting("Plugin.Configuration.Allow_proxy_to_manage_settings"); private PluginSettings() { /* static variable class */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/ProxySettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/ProxySettings.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/ProxySettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/ProxySettings.java index c766b097d..2ef6c9321 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/ProxySettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/ProxySettings.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.StringSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.StringSetting; /** * {@link Setting} values that are in "Database" section. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/TimeSettings.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/TimeSettings.java index 0eb48a5fc..d0f41aad7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/TimeSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/TimeSettings.java @@ -14,12 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.system.settings.paths.key.BooleanSetting; -import com.djrapitops.plan.system.settings.paths.key.IntegerSetting; -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.TimeSetting; +import com.djrapitops.plan.settings.config.paths.key.BooleanSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.TimeSetting; /** * {@link Setting} values that are in "Time" section. @@ -33,14 +32,12 @@ public class TimeSettings { public static final Setting PING_PLAYER_LOGIN_DELAY = new TimeSetting("Time.Delays.Ping_player_join_delay"); public static final Setting DB_TRANSACTION_FINISH_WAIT_DELAY = new TimeSetting("Time.Delays.Wait_for_DB_Transactions_on_disable"); public static final Setting AFK_THRESHOLD = new TimeSetting("Time.Thresholds.AFK_threshold"); - public static final Setting ACTIVE_LOGIN_THRESHOLD = new IntegerSetting("Time.Thresholds.Activity_index.Login_threshold", Setting::timeValidator); public static final Setting ACTIVE_PLAY_THRESHOLD = new TimeSetting("Time.Thresholds.Activity_index.Playtime_threshold"); public static final Setting DELETE_INACTIVE_PLAYERS_AFTER = new TimeSetting("Time.Thresholds.Remove_inactive_player_data_after"); public static final Setting DELETE_TPS_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_time_series_data_after"); public static final Setting DELETE_PING_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_ping_data_after"); - public static final Setting ANALYSIS_REFRESH_PERIOD = new TimeSetting("Time.Periodic_tasks.Analysis_refresh_every"); + public static final Setting DELETE_EXTENSION_DATA_AFTER = new TimeSetting("Time.Thresholds.Remove_disabled_extension_data_after"); public static final Setting EXTENSION_DATA_REFRESH_PERIOD = new TimeSetting("Time.Periodic_tasks.Extension_data_refresh_every"); - public static final Setting CLEAN_CACHE_PERIOD = new TimeSetting("Time.Periodic_tasks.Clean_caches_every"); public static final Setting CLEAN_DATABASE_PERIOD = new TimeSetting("Time.Periodic_tasks.Clean_Database_every"); public static final Setting CONFIG_UPDATE_INTERVAL = new TimeSetting("Time.Periodic_tasks.Check_DB_for_server_config_files_every"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/WebserverSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/WebserverSettings.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/WebserverSettings.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/WebserverSettings.java index c50635dec..390e2b5fb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/WebserverSettings.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/WebserverSettings.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths; +package com.djrapitops.plan.settings.config.paths; -import com.djrapitops.plan.system.settings.paths.key.BooleanSetting; -import com.djrapitops.plan.system.settings.paths.key.IntegerSetting; -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.StringSetting; +import com.djrapitops.plan.settings.config.paths.key.BooleanSetting; +import com.djrapitops.plan.settings.config.paths.key.IntegerSetting; +import com.djrapitops.plan.settings.config.paths.key.Setting; +import com.djrapitops.plan.settings.config.paths.key.StringSetting; /** * {@link Setting} values that are in "Webserver" section. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/BooleanSetting.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/BooleanSetting.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/BooleanSetting.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/BooleanSetting.java index 22e104265..fbee28bb2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/BooleanSetting.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/BooleanSetting.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths.key; +package com.djrapitops.plan.settings.config.paths.key; -import com.djrapitops.plan.system.settings.config.ConfigNode; +import com.djrapitops.plan.settings.config.ConfigNode; import java.util.function.Predicate; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/IntegerSetting.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/IntegerSetting.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/IntegerSetting.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/IntegerSetting.java index abfb4b652..37ac0c153 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/IntegerSetting.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/IntegerSetting.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths.key; +package com.djrapitops.plan.settings.config.paths.key; -import com.djrapitops.plan.system.settings.config.ConfigNode; +import com.djrapitops.plan.settings.config.ConfigNode; import java.util.function.Predicate; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/Setting.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/Setting.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/Setting.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/Setting.java index 11bec5c99..af75c6905 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/Setting.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/Setting.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths.key; +package com.djrapitops.plan.settings.config.paths.key; -import com.djrapitops.plan.data.store.Type; -import com.djrapitops.plan.system.settings.config.ConfigNode; +import com.djrapitops.plan.delivery.domain.keys.Type; +import com.djrapitops.plan.settings.config.ConfigNode; import java.util.function.Predicate; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/StringListSetting.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/StringListSetting.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/StringListSetting.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/StringListSetting.java index b9c0710ed..2ab27d9b8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/StringListSetting.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/StringListSetting.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths.key; +package com.djrapitops.plan.settings.config.paths.key; -import com.djrapitops.plan.data.store.Type; -import com.djrapitops.plan.system.settings.config.ConfigNode; +import com.djrapitops.plan.delivery.domain.keys.Type; +import com.djrapitops.plan.settings.config.ConfigNode; import java.util.List; import java.util.function.Predicate; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/StringSetting.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/StringSetting.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/StringSetting.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/StringSetting.java index f8a74d738..c8fccc6c4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/StringSetting.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/StringSetting.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths.key; +package com.djrapitops.plan.settings.config.paths.key; -import com.djrapitops.plan.system.settings.config.ConfigNode; +import com.djrapitops.plan.settings.config.ConfigNode; import java.util.function.Predicate; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/TimeSetting.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/TimeSetting.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/TimeSetting.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/TimeSetting.java index 525f7dd16..cc532f507 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/key/TimeSetting.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/config/paths/key/TimeSetting.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.paths.key; +package com.djrapitops.plan.settings.config.paths.key; -import com.djrapitops.plan.system.settings.config.ConfigNode; +import com.djrapitops.plan.settings.config.ConfigNode; import com.djrapitops.plugin.utilities.Verify; import java.util.concurrent.TimeUnit; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LangCode.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LangCode.java index 77b127077..eab75cbb1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LangCode.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LangCode.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale; +package com.djrapitops.plan.settings.locale; /** * Language enum of supported languages, follows ISO 639-1 for language codes. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Locale.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Locale.java new file mode 100644 index 000000000..672549b1e --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Locale.java @@ -0,0 +1,203 @@ +/* + * 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 . + */ +package com.djrapitops.plan.settings.locale; + +import com.djrapitops.plan.settings.locale.lang.HtmlLang; +import com.djrapitops.plan.settings.locale.lang.JSLang; +import com.djrapitops.plan.settings.locale.lang.Lang; +import com.djrapitops.plan.storage.file.FileResource; +import com.djrapitops.plan.storage.file.PlanFiles; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Represents loaded language information. + * + * @author Rsl1122 + */ +public class Locale extends HashMap { + + public static Locale forLangCodeString(PlanFiles files, String code) throws IOException { + return forLangCode(LangCode.fromString(code), files); + } + + private LangCode langCode; + + public Locale() { + this(LangCode.EN); + } + + public Locale(LangCode langCode) { + this.langCode = langCode; + } + + public static Locale forLangCode(LangCode code, PlanFiles files) throws IOException { + return new LocaleFileReader(files.getResourceFromJar("locale/" + code.getFileName())).load(code); + } + + public static Locale fromFile(File file) throws IOException { + return new LocaleFileReader(new FileResource(file.getName(), file)).load(LangCode.CUSTOM); + } + + public LangCode getLangCode() { + return langCode; + } + + @Override + public Message get(Object key) { + Message storedValue = super.get(key); + if (key instanceof Lang && storedValue == null) { + return new Message(((Lang) key).getDefault()); + } else { + return storedValue; + } + } + + public Optional getNonDefault(Object key) { + Message storedValue = super.get(key); + if (key instanceof Lang && storedValue == null) { + return Optional.empty(); + } else { + return Optional.of(storedValue); + } + } + + public String getString(Lang key) { + return get(key).toString(); + } + + public String getString(Lang key, Serializable... values) { + return get(key).parse(values); + } + + public String[] getArray(Lang key) { + return get(key).toArray(); + } + + public String[] getArray(Lang key, Serializable... values) { + return get(key).toArray(values); + } + + public void loadFromAnotherLocale(Locale locale) { + putAll(locale); + this.langCode = locale.langCode; + } + + public String replaceLanguageInHtml(String from) { + if (isEmpty()) { + return from; + } + + Pattern scripts = Pattern.compile("(||)"); + + Matcher scriptMatcher = scripts.matcher(from); + List foundScripts = new ArrayList<>(); + while (scriptMatcher.find()) { + foundScripts.add(scriptMatcher.toMatchResult().group(0)); + } + + TranslatedString translated = new TranslatedString(from); + Arrays.stream(HtmlLang.values()) + // Longest first so that entries that contain each other don't partially replace. + .sorted((one, two) -> Integer.compare( + two.getIdentifier().length(), + one.getIdentifier().length() + )) + .forEach(lang -> getNonDefault(lang).ifPresent(replacement -> + translated.translate(lang.getDefault(), replacement.toString())) + ); + + StringBuilder complete = new StringBuilder(translated.length()); + + String[] parts = scripts.split(translated.toString()); + for (int i = 0; i < parts.length; i++) { + complete.append(parts[i]); + if (i < parts.length - 1) { + complete.append(replaceLanguageInJavascript(foundScripts.get(i))); + } + } + + return complete.toString(); + } + + public String replaceLanguageInJavascript(String from) { + if (isEmpty()) { + return from; + } + + TranslatedString translated = new TranslatedString(from); + Arrays.stream(JSLang.values()) + // Longest first so that entries that contain each other don't partially replace. + .sorted((one, two) -> Integer.compare( + two.getIdentifier().length(), + one.getIdentifier().length() + )) + .forEach(lang -> getNonDefault(lang).ifPresent(replacement -> + translated.translate(lang.getDefault(), replacement.toString())) + ); + + for (Lang extra : new Lang[]{ + HtmlLang.UNIT_NO_DATA, + HtmlLang.TITLE_WORLD_PLAYTIME, + HtmlLang.LABEL_OPERATOR, + HtmlLang.LABEL_BANNED, + HtmlLang.SIDE_SESSIONS, + HtmlLang.LABEL_PLAYTIME, + HtmlLang.LABEL_AFK_TIME, + HtmlLang.LABEL_LONGEST_SESSION, + HtmlLang.LABEL_SESSION_MEDIAN, + HtmlLang.LABEL_PLAYER_KILLS, + HtmlLang.LABEL_MOB_KILLS, + HtmlLang.LABEL_DEATHS, + HtmlLang.LABEL_PLAYERS_ONLINE, + HtmlLang.LABEL_REGISTERED, + HtmlLang.TITLE_SERVER, + HtmlLang.TITLE_LENGTH, + HtmlLang.TITLE_AVG_PING, + HtmlLang.TITLE_BEST_PING, + HtmlLang.TITLE_WORST_PING, + HtmlLang.LABEL_FREE_DISK_SPACE, + HtmlLang.LABEL_NEW_PLAYERS, + HtmlLang.LABEL_UNIQUE_PLAYERS, + + }) { + getNonDefault(extra).ifPresent(replacement -> + translated.translate(extra.getDefault(), replacement.toString())); + } + + return translated.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Locale)) return false; + if (!super.equals(o)) return false; + Locale locale = (Locale) o; + return langCode == locale.langCode; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), langCode); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleFileReader.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleFileReader.java index 1738c9e96..e2ad59432 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileReader.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleFileReader.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale; +package com.djrapitops.plan.settings.locale; -import com.djrapitops.plan.system.file.Resource; -import com.djrapitops.plan.system.locale.lang.Lang; +import com.djrapitops.plan.settings.locale.lang.Lang; +import com.djrapitops.plan.storage.file.Resource; import java.io.IOException; import java.util.List; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileWriter.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleFileWriter.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileWriter.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleFileWriter.java index c9852d199..b0dd3b18d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleFileWriter.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleFileWriter.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale; +package com.djrapitops.plan.settings.locale; -import com.djrapitops.plan.system.locale.lang.Lang; +import com.djrapitops.plan.settings.locale.lang.Lang; import com.djrapitops.plan.utilities.comparators.LocaleEntryComparator; import com.djrapitops.plan.utilities.comparators.StringLengthComparator; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleSystem.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleSystem.java index bac488c3f..757cd3ca6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/LocaleSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/LocaleSystem.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale; +package com.djrapitops.plan.settings.locale; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.locale.lang.*; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.webserver.auth.FailReason; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.delivery.webserver.auth.FailReason; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.locale.lang.*; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -73,13 +73,10 @@ public class LocaleSystem implements SubSystem { PluginLang.values(), ManageLang.values(), GenericLang.values(), - CommonHtmlLang.values(), - PlayerPageLang.values(), - ServerPageLang.values(), - NetworkPageLang.values(), + HtmlLang.values(), ErrorPageLang.values(), FailReason.values(), - HealthInfoLang.values() + JSLang.values(), }; return Arrays.stream(lang) @@ -109,7 +106,11 @@ public class LocaleSystem implements SubSystem { private void writeNewDefaultLocale(File localeFile) { try { - new LocaleFileWriter(localeFile.exists() ? Locale.fromFile(localeFile) : locale).writeToFile(localeFile); + Locale writing = loadSettingLocale().orElse(locale); + if (localeFile.exists()) { + writing.putAll(Locale.fromFile(localeFile)); + } + new LocaleFileWriter(writing).writeToFile(localeFile); } catch (IOException | IllegalStateException e) { logger.error("Failed to write new Locale file at " + localeFile.getAbsolutePath()); errorHandler.log(L.WARN, this.getClass(), e); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Message.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Message.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/Message.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Message.java index b78467126..18f99c46b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Message.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/Message.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale; +package com.djrapitops.plan.settings.locale; import com.djrapitops.plugin.utilities.Verify; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringSubstitutor; import java.io.Serializable; @@ -51,11 +52,11 @@ public class Message { } public String[] toArray() { - return content.split("\\\\"); + return StringUtils.split(content, '\\'); } public String[] toArray(Serializable... p) { - return parse(p).split("\\\\"); + return parse(p).split(content, '\\'); } @Override diff --git a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/react/ReactValue.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/TranslatedString.java similarity index 54% rename from PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/react/ReactValue.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/TranslatedString.java index 34c021ca4..c3276d6c1 100644 --- a/PlanPluginBridge/src/main/java/com/djrapitops/pluginbridge/plan/react/ReactValue.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/TranslatedString.java @@ -14,41 +14,36 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.pluginbridge.plan.react; +package com.djrapitops.plan.settings.locale; -import com.volmit.react.api.SampledType; +import org.apache.commons.lang3.StringUtils; /** - * Data container for React data points. + * Utility for translating String. + *

    + * Improves performance by avoiding a double for-each loop since this class can be considered final in the lambda + * expression in {@link Locale#replaceLanguageInHtml(String)}. * * @author Rsl1122 */ -public class ReactValue implements Comparable { +class TranslatedString { - private final SampledType type; - private final long date; - private final double dataValue; + private String translating; - public ReactValue(SampledType type, long date, double dataValue) { - this.type = type; - this.date = date; - this.dataValue = dataValue; + TranslatedString(String translating) { + this.translating = translating; } - public SampledType getType() { - return type; - } - - public long getDate() { - return date; - } - - public double getDataValue() { - return dataValue; + public void translate(String replace, String with) { + translating = StringUtils.replace(translating, replace, with); } @Override - public int compareTo(ReactValue o) { - return Long.compare(this.date, o.date); + public String toString() { + return translating; + } + + public int length() { + return translating.length(); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CmdHelpLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CmdHelpLang.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CmdHelpLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CmdHelpLang.java index 52291faef..5803ccfbb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CmdHelpLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CmdHelpLang.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** * Lang for short help messages in Commands. @@ -36,8 +36,6 @@ public enum CmdHelpLang implements Lang { WEB_REGISTER("Command Help - /plan register", "Register a Web User"), WEB("Command Help - /plan webuser", "Manage Web Users"), DEV("Command Help - /plan dev", "Development mode command"), - SETUP("Command Help - /planbungee setup", "Toggle set-up mode"), - CON("Command Help - /planbungee con", "Debug Proxy-Server connections"), DISABLE("Command Help - /planbungee disable", "Disable the plugin temporarily"), MANAGE_MOVE("Command Help - /plan manage move", "Move data between Databases"), @@ -46,12 +44,9 @@ public enum CmdHelpLang implements Lang { MANAGE_REMOVE("Command Help - /plan manage remove", "Remove Player's data"), MANAGE_HOTSWAP("Command Help - /plan manage hotswap", "Change Database quickly"), MANAGE_CLEAR("Command Help - /plan manage clear", "Clear a Database"), - MANAGE_CON("Command Help - /plan manage con", "Debug Server-Proxy connections"), MANAGE_IMPORT("Command Help - /plan manage import", "Import data from elsewhere"), MANAGE_EXPORT("Command Help - /plan manage export", "Trigger export manually"), MANAGE_DISABLE("Command Help - /plan manage disable", "Disable a feature temporarily"), - MANAGE_SETUP("Command Help - /plan manage setup", "Set-up Server-Proxy connection"), - WEB_LEVEL("Command Help - /plan web level", "Information about permission levels"), WEB_LIST("Command Help - /plan web list", "List Web Users"), WEB_CHECK("Command Help - /plan web check", "Inspect a Web User"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CommandLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CommandLang.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CommandLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CommandLang.java index 52a1f29b3..b64c6daca 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CommandLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/CommandLang.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** * {@link Lang} implementation for general command language. @@ -42,19 +42,6 @@ public enum CommandLang implements Lang { NO_WEB_USER_NOTIFY("Cmd Notify - No WebUser", "You might not have a web user, use /plan register "), WEB_PERMISSION_LEVELS("Cmd Web - Permission Levels", ">\\§70: Access all pages\\§71: Access '/players' and all player pages\\§72: Access player page with the same username as the webuser\\§73+: No permissions"), - CONNECT_SUCCESS("Cmd Setup - Success", "§aConnection successful, Plan may restart in a few seconds.."), - CONNECT_FORBIDDEN("Cmd Setup - Forbidden", "§eConnection succeeded, but Proxy has set-up mode disabled - use 'planbungee setup' on the proxy to enable it."), - CONNECT_BAD_REQUEST("Cmd Setup - Bad Request", "§eConnection succeeded, but Receiving server was a Bukkit or Sponge server. Use another address instead."), - CONNECT_UNAUTHORIZED("Cmd Setup - Unauthorized", "§eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support"), - CONNECT_FAIL("Cmd Setup - Generic Fail", "§eConnection failed: ${0}"), - CONNECT_INTERNAL_ERROR("Cmd Setup - Internal Error", "§eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page."), - CONNECT_GATEWAY("Cmd Setup - Gateway Error", "§eConnection succeeded, but Proxy failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug."), - CONNECT_WEBSERVER_NOT_ENABLED("Cmd Setup - WebServer not Enabled", "§cWebServer is not enabled on this server! Make sure it enables on boot!"), - CONNECT_URL_MISTAKE("Cmd Setup - Url mistake", "§cMake sure you're using the full address (Starts with http:// or https://) - Check Proxy enable log for the full address."), - - SETUP_ALLOWED("Cmd Setup - Allowed", "§aSet-up is now Allowed"), - SETUP_FORBIDDEN("Cmd Setup - Disallowed", "§cSet-up is now Forbidden"), - LINK_CLICK_ME("Cmd - Click Me", "Click me"), LINK_PREFIX("Cmd - Link", " §2Link: §f"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/DeepHelpLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/DeepHelpLang.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/DeepHelpLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/DeepHelpLang.java index 0ad3b310f..841959bab 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/DeepHelpLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/DeepHelpLang.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** * {@link Lang} implementation for in depth help language when /command ? is used. @@ -24,7 +24,6 @@ package com.djrapitops.plan.system.locale.lang; public enum DeepHelpLang implements Lang { PLAN("In Depth Help - /plan ?", "> §2Main Command\\ Access to subcommands and help\\ §2/plan §fList subcommands\\ §2/plan ? §fIn depth help"), ANALYZE("In Depth Help - /plan analyze ?", "> §2Analysis Command\\ Refreshes server page and displays link to the web page."), - SETUP("In Depth Help - /planbungee setup ?", "> §2Set-up toggle Command\\ Toggles set-up mode on Proxy.\\ Safeguard against unauthorized MySQL snooping with another server."), DISABLE("In Depth Help - /planbungee disable ?", "> §2Disable Command\\ Runs Plan onDisable on Proxy.\\ Plugin can be enabled with /planbungee reload afterwards.\\ §bDoes not support swapping jar on the fly"), INSPECT("In Depth Help - /plan inspect ?", "> §2Inspect Command\\ Refreshes player page and displays link to the web page."), PLAYERS("In Depth Help - /plan players ?", "> §2Players Command\\ Displays link to the players page."), @@ -38,15 +37,12 @@ public enum DeepHelpLang implements Lang { MANAGE_BACKUP("In Depth Help - /plan manage backup ?", "> §2Backup Subcommand\\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder."), MANAGE_CLEAR("In Depth Help - /plan manage clear ?", "> §2Clear Subcommand\\ Removes everything in the active database. Use with caution."), - MANAGE_CON("In Depth Help - /plan manage con ?", "> §2Connection Debug Subcommand\\ Used to debug connections in the network.\\ Sends a request to each server in the database."), MANAGE_DISABLE("In Depth Help - /plan manage disable ?", "> §2Disable Subcommand\\ Can disable parts of the plugin until next reload.\\ Accepted arguments:\\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro."), MANAGE_IMPORT("In Depth Help - /plan manage import ?", "> §2Import Subcommand\\ Import data from other sources.\\ Accepted Arguments:\\ §2offline §fBukkit player data, only register date and name."), MANAGE_EXPORT("In Depth Help - /plan manage export ?", "> §2Export Subcommand\\ Trigger export to result folders.\\ Accepted Arguments:\\ §2list §fList possible arguments.\\ §2players §fExport /players, /player pages + /player/raw json depending on config values.\\ §2server_json §fExport /server/raw JSON if enabled in config."), MANAGE_MOVE("In Depth Help - /plan manage move ?", "> §2Move Subcommand\\ Move data from SQLite to MySQL or other way around.\\ Target database is cleared before transfer."), MANAGE_REMOVE("In Depth Help - /plan manage remove ?", "> §2Remove Subcommand\\ Remove player's data from the active database."), MANAGE_RESTORE("In Depth Help - /plan manage restore ?", "> §2Restore Subcommand\\ Restore a previous backup SQLite database (.db file)\\ You can also restore database.db from another server to MySQL.\\ Target database is cleared before transfer."), - MANAGE_SETUP("In Depth Help - /plan manage setup ?", "> §2Setup Subcommand\\ Set-up a connection between Bungee and this server for network functionality.\\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee."), - WEB_REGISTER("In Depth Help - /plan web register ?", "> §2Register Subcommand\\ Registers a new Web User.\\ Registering a user for another player requires plan.webmanage permission.\\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt."), MANAGE_RAW_DATA("In Depth Help - /plan manage raw ?", "> §2Raw Data Subcommand\\ Displays link to raw JSON data page.\\ Not available if Plan webserver is not enabled."), MANAGE_UNINSTALLED("In Depth Help - /plan manage uninstalled ?", "> §2Uninstalled Server Subcommand\\ Marks a server as uninstalled in the database.\\ Can not mark the server the command is being used on as uninstalled.\\ Will affect ConnectionSystem."); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ErrorPageLang.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ErrorPageLang.java index eb2270d64..4c0b3e180 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ErrorPageLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ErrorPageLang.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** * {@link Lang} implementation for all error pages. @@ -32,12 +32,7 @@ public enum ErrorPageLang implements Lang { FORBIDDEN_403("Forbidden"), ACCESS_DENIED_403("Access Denied"), NOT_FOUND_404("Not Found"), - PAGE_NOT_FOUND_404("Page does not exist."), - ANALYSIS_REFRESH("Analysis is being refreshed.."), - ANALYSIS_REFRESH_LONG("Analysis is being run, refresh the page after a few seconds.."), - INSPECT_REFRESH("Player page request is being processed.."), - INSPECT_REFRESH_LONG("Page will refresh automatically.."), - PLUGIN_TAB_REFRESH("Calculating..."); + PAGE_NOT_FOUND_404("Page does not exist."); private final String defaultValue; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/GenericLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/GenericLang.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/GenericLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/GenericLang.java index 8d48f0e66..8e05e53fb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/GenericLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/GenericLang.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** * {@link Lang} implementation for single words. @@ -25,6 +25,7 @@ public enum GenericLang implements Lang { YES("Positive", "Yes"), NO("Negative", "No"), UNKNOWN("Unknown", "Unknown"), + UNAVAILABLE("Unavailable", "Unavailable"), TODAY("Today", "'Today'"), YESTERDAY("Yesterday", "'Yesterday'"); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java new file mode 100644 index 000000000..5030ce0e0 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/HtmlLang.java @@ -0,0 +1,233 @@ +/* + * 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 . + */ +package com.djrapitops.plan.settings.locale.lang; + +/** + * Lang enum for all text included in the html files. + * + * @author Rsl1122 + */ +public enum HtmlLang implements Lang { + + TITLE_NETWORK("Network"), + // Network Page Navigation + SIDE_INFORMATION("INFORMATION"), // Nav group title + SIDE_NETWORK_OVERVIEW("Network Overview"), + SIDE_SERVERS("Servers"), + SIDE_OVERVIEW("Overview"), + SIDE_SESSIONS("Sessions"), + SIDE_PLAYERBASE("Playerbase"), + SIDE_PLAYER_LIST("Player List"), + SIDE_PLAYERBASE_OVERVIEW("Playerbase Overview"), + SIDE_GEOLOCATIONS("Geolocations"), + SIDE_PLUGINS("PLUGINS"), // Nav group title + UNIT_NO_DATA("No Data"), // Generic + // Modals + TITLE_THEME_SELECT("Theme Select"), + LINK_NIGHT_MODE("Night Mode"), + TEXT_PLUGIN_INFORMATION("Information about the plugin"), + TEXT_LICENSED_UNDER("is developed and licensed under"), + LINK_WIKI("Plan Wiki, Tutorials & Documentation"), + LINK_ISSUES("Report Issues"), + LINK_DISCORD("General Support on Discord"), + TEXT_DEVELOPED_BY("is developed by"), + TEXT_CONTRIBUTORS_THANKS("In addition following awesome people have contributed:"), + TEXT_CONTRIBUTORS_CODE("code contributor"), + TEXT_CONTRIBUTORS_LOCALE("translator"), + TEXT_CONTRIBUTORS_MONEY("Extra special thanks to those who have monetarily supported the development."), + TEXT_METRICS("bStats Metrics"), + TITLE_VERSION("Version"), + TITLE_IS_AVAILABLE("is Available"), + TEXT_VERSION("A new version has been released and is now available for download."), + TEXT_DEV_VERSION("This version is a DEV release."), + LINK_CHANGELOG("View Changelog"), + LINK_DOWNLOAD("Download"), + // Network overview tab + TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY("Network Online Activity"), + TITLE_GRAPH_DAY_BY_DAY("Day by Day"), + UNIT_THE_PLAYERS("Players"), + TITLE_LAST_24_HOURS("Last 24 hours"), + TITLE_LAST_7_DAYS("Last 7 days"), + TITLE_LAST_30_DAYS("Last 30 days"), + LABEL_UNIQUE_PLAYERS("Unique Players"), + LABEL_NEW_PLAYERS("New Players"), + LABEL_REGULAR_PLAYERS("Regular Players"), + LABEL_TOTAL_PLAYERS("Total Players"), + TITLE_NETWORK_AS_NUMBERS("Network as Numbers"), + LABEL_PLAYERS_ONLINE("Players Online"), + LABEL_TOTAL_PLAYTIME("Total Playtime"), + LABEL_PLAYTIME("Playtime"), + LABEL_LAST_PEAK("Last Peak"), + LABEL_BEST_PEAK("Best Peak"), + LABEL_AVG_PLAYTIME("Average Playtime"), + LABEL_PER_PLAYER("/ Player"), + LABEL_AVG_SESSION_LENGTH("Average Session Length"), + TITLE_WEEK_COMPARISON("Week Comparison"), + TITLE_TRENDS("Trends for 30 days"), + TITLE_TREND("Trend"), + COMPARING_7_DAYS("Comparing 7 days"), + // Servers tab + TITLE_ONLINE_ACTIVITY("Online Activity"), + TITLE_30_DAYS("30 days"), + TITLE_AS_NUMBERS("as Numbers"), + LABEL_AVG_TPS("Average TPS"), + LABEL_LOW_TPS("Low TPS Spikes"), + LABEL_DOWNTIME("Downtime"), + // Sessions tab + TITLE_RECENT_SESSIONS("Recent Sessions"), + TITLE_PLAYER("Player"), + TITLE_SESSION_START("Session Started"), + TITLE_LENGTH("Length"), + TITLE_SERVER("Server"), + TITLE_MOST_PLAYED_WORLD("Most played World"), + TEXT_CLICK_TO_EXPAND("Click to expand"), + TITLE_SERVER_PLAYTIME_30("Server Playtime for 30 days"), + TITLE_INSIGHTS("Insights for 30 days"), + LABEL_AFK_TIME("AFK Time"), + LABEL_AFK("AFK"), + // Playerbase overview tab + TITLE_PLAYERBASE_DEVELOPMENT("Playerbase development"), + TITLE_CURRENT_PLAYERBASE("Current Playerbase"), + COMPARING_60_DAYS("Comparing 30d ago to Now"), + TITLE_30_DAYS_AGO("30 days ago"), + TITLE_NOW("Now"), + LABEL_PER_REGULAR_PLAYER("/ Regular Player"), + LABEL_NEW("New"), + LABEL_REGULAR("Regular"), + LABEL_INACTIVE("Inactive"), + SIDE_TO_MAIN_PAGE("to main page"), + // Geolocations tab + TITLE_CONNECTION_INFO("Connection Information"), + TITLE_COUNTRY("Country"), + TITLE_AVG_PING("Average Ping"), + TITLE_WORST_PING("Worst Ping"), + TITLE_BEST_PING("Best Ping"), + TEXT_NO_EXTENSION_DATA("No Extension Data"), + // Server page + LINK_BACK_NETWORK("Network page"), + SIDE_PVP_PVE("PvP & PvE"), + SIDE_PERFORMANCE("Performance"), + LABEL_RETENTION("New Player Retention"), + TITLE_SERVER_AS_NUMBERS("Server as Numbers"), + TITLE_ONLINE_ACTIVITY_AS_NUMBERS("Online Activity as Numbers"), + COMPARING_15_DAYS("Comparing 15 days"), + TITLE_GRAPH_PUNCHCARD("Punchcard for 30 Days"), + LABEL_ONLINE_FIRST_JOIN("Players online on first join"), + LABEL_FIRST_SESSION_LENGTH("First session length"), + LABEL_LONE_JOINS("Lone joins"), + LABEL_LONE_NEW_JOINS("Lone newbie joins"), + LABEL_MOST_ACTIVE_GAMEMODE("Most Active Gamemode"), + LABEL_SERVER_OCCUPIED("Server occupied"), + TITLE_PVP_PVE_NUMBERS("PvP & PvE as Numbers"), + LABEL_1ST_WEAPON("Deadliest PvP Weapon"), + LABEL_2ND_WEAPON("2nd PvP Weapon"), + LABEL_3RD_WEAPON("3rd PvP Weapon"), + LABEL_AVG_KDR("Average KDR"), + LABEL_PLAYER_KILLS("Player Kills"), + LABEL_AVG_MOB_KDR("Average Mob KDR"), + LABEL_MOB_KILLS("Mob Kills"), + LABEL_MOB_DEATHS("Mob Caused Deaths"), + LABEL_DEATHS("Deaths"), + TITLE_RECENT_KILLS("Recent Kills"), + TITLE_ALL("All"), + TITLE_TPS("TPS"), + TITLE_CPU_RAM("CPU & RAM"), + TITLE_WORLD("World Load"), + TITLE_PING("Ping"), + TITLE_DISK("Disk Space"), + LABEL_AVG("Average"), + TITLE_PERFORMANCE_AS_NUMBERS("Performance as Numbers"), + LABEL_SERVER_DOWNTIME("Server Downtime"), + LABEL_DURING_LOW_TPS("During Low TPS Spikes:"), + TEXT_NO_LOW_TPS("No low tps spikes"), + // Player Page + TITLE_SEEN_NICKNAMES("Seen Nicknames"), + LABEL_LAST_SEEN("Last Seen"), + TITLE_LAST_CONNECTED("Last Connected"), + LABEL_PLAYER_DEATHS("Player Caused Deaths"), + TITLE_PVP_KILLS("Recent PvP Kills"), + TITLE_PVP_DEATHS("Recent PvP Deaths"), + TITLE_SERVER_PLAYTIME("Server Playtime"), + LINK_BACK_SERVER("Server page"), + SIDE_SERVERS_TITLE("SERVERS"), + // Were missing + TITLE_SERVER_OVERVIEW("Server Overview"), + TITLE_ONLINE_ACTIVITY_OVERVIEW("Online Activity Overview"), + PER_DAY("/ Day"), + TITLE_WORLD_PLAYTIME("World Playtime"), + TITLE_PLAYER_OVERVIEW("Player Overview"), + LABEL_LONGEST_SESSION("Longest Session"), + LABEL_REGISTERED("Registered"), + TITLE_TITLE_PLAYER_PUNCHCARD("Punchcard"), + TITLE_ALL_TIME("All Time"), + LABEL_NAME("Name"), + // ---------------------------------- + // OLD + // ---------------------------------- + NAV_PLUGINS("Plugins"), + PLAYERS_TEXT("Players"), + TOTAL_PLAYERS("Total Players"), + UNIQUE_CALENDAR("Unique:"), + NEW_CALENDAR("New:"), + SESSION("Session"), + KILLED("Killed"), + LABEL_LOADED_ENTITIES("Loaded Entities"), + LABEL_LOADED_CHUNKS("Loaded Chunks"), + LABEL_ENTITIES("Entities"), + LABEL_FREE_DISK_SPACE("Free Disk Space"), + ONLINE(" Online"), + OFFLINE(" Offline"), + LABEL_TIMES_KICKED("Times Kicked"), + TOTAL_ACTIVE_TEXT("Total Active"), + TOTAL_AFK("Total AFK"), + LABEL_SESSION_MEDIAN("Session Median"), + LABEL_ACTIVITY_INDEX("Activity Index"), + INDEX_ACTIVE("Active"), + INDEX_VERY_ACTIVE("Very Active"), + INDEX_REGULAR("Regular"), + INDEX_IRREGULAR("Irregular"), + INDEX_INACTIVE("Inactive"), + LABEL_FAVORITE_SERVER("Favorite Server"), + LABEL_NICKNAME("Nickname"), + LOCAL_MACHINE("Local Machine"), + TITLE_CALENDAR(" Calendar"), + LABEL_OPERATOR("Operator"), + LABEL_BANNED("Banned"), + LABEL_MOB_KDR("Mob KDR"), + WITH("

    '); + $(element).find('#data_unique_players_7d').text(data.unique_players_7d); + $(element).find('#data_unique_players_24h').text(data.unique_players_24h); + + $(element).find('#data_unique_players_30d_avg').replaceWith(''); + $(element).find('#data_unique_players_7d_avg').text(data.unique_players_7d_avg); + $(element).find('#data_unique_players_24h_avg').text(data.unique_players_24h_avg); + + $(element).find('#data_new_players_30d').replaceWith(''); + $(element).find('#data_new_players_7d').text(data.new_players_7d); + $(element).find('#data_new_players_24h').text(data.new_players_24h); + + $(element).find('#data_new_players_30d_avg').replaceWith(''); + $(element).find('#data_new_players_7d_avg').text(data.new_players_7d_avg); + $(element).find('#data_new_players_24h_avg').text(data.new_players_24h_avg); + + $(element).find('#data_new_players_retention_30d').text('(' + data.new_players_retention_30d + '/' + data.new_players_30d + ') ' + data.new_players_retention_30d_perc); + $(element).find('#data_new_players_retention_7d').text('(' + data.new_players_retention_7d + '/' + data.new_players_7d + ') ' + data.new_players_retention_7d_perc); + $(element).find('#data_new_players_retention_24h').replaceWith(''); + + $(element).find('#data_playtime_30d').replaceWith(''); + $(element).find('#data_playtime_7d').text(data.playtime_7d); + $(element).find('#data_playtime_24h').text(data.playtime_24h); + + $(element).find('#data_playtime_30d_avg').replaceWith(''); + $(element).find('#data_playtime_7d_avg').text(data.playtime_7d_avg); + $(element).find('#data_playtime_24h_avg').text(data.playtime_24h_avg); + + $(element).find('#data_session_length_30d_avg').replaceWith(''); + $(element).find('#data_session_length_7d_avg').text(data.session_length_7d_avg); + $(element).find('#data_session_length_24h_avg').text(data.session_length_24h_avg); + + $(element).find('#data_sessions_30d').replaceWith(''); + $(element).find('#data_sessions_7d').text(data.sessions_7d); + $(element).find('#data_sessions_24h').text(data.sessions_24h); + + // Insights + data = json.insights; + element = $(tab).find('#data_insights'); + + $(element).find('#data_players_first_join_avg').replaceWith(data.players_first_join_avg + smallTrend(data.players_first_join_trend)); + $(element).find('#data_first_session_length_avg').replaceWith(data.first_session_length_avg + smallTrend(data.first_session_length_trend)); + $(element).find('#data_lone_joins').replaceWith(data.lone_joins + smallTrend(data.lone_joins_trend)); + $(element).find('#data_lone_new_joins').replaceWith(data.lone_new_joins + smallTrend(data.lone_new_joins_trend)) +} + +/* This function loads Sessions tab */ +function loadSessionValues(json, error) { + tab = $('#sessions-overview'); + if (error) { + displayError(tab, error); + return; + } + + // Insights + data = json.insights; + element = $(tab).find('#data_insights'); + + $(element).find('#data_most_active_gamemode').text(data.most_active_gamemode); + $(element).find('#data_most_active_gamemode_perc').text(data.most_active_gamemode_perc); + $(element).find('#data_server_occupied').text(data.server_occupied); + $(element).find('#data_server_occupied_perc').text(data.server_occupied_perc); + $(element).find('#data_total_playtime').text(data.total_playtime); + $(element).find('#data_afk_time').text(data.afk_time); + $(element).find('#data_afk_time_perc').text(data.afk_time_perc) +} + +/* This function loads Playerbase Overview tab */ +function loadPlayerbaseOverviewValues(json, error) { + tab = $('#playerbase-overview'); + if (error) { + displayError(tab, error); + return; + } + + // Trends + data = json.trends; + element = $(tab).find('#data_trends'); + + $(element).find('#data_total_players_then').text(data.total_players_then); + $(element).find('#data_total_players_now').text(data.total_players_now); + $(element).find('#data_total_players_trend').replaceWith(trend(data.total_players_trend)); + $(element).find('#data_regular_players_then').text(data.regular_players_then); + $(element).find('#data_regular_players_now').text(data.regular_players_now); + $(element).find('#data_regular_players_trend').replaceWith(trend(data.regular_players_trend)); + $(element).find('#data_playtime_avg_then').text(data.playtime_avg_then); + $(element).find('#data_playtime_avg_now').text(data.playtime_avg_now); + $(element).find('#data_playtime_avg_trend').replaceWith(trend(data.playtime_avg_trend)); + $(element).find('#data_afk_then').text(data.afk_then); + $(element).find('#data_afk_now').text(data.afk_now); + $(element).find('#data_afk_trend').replaceWith(trend(data.afk_trend)); + $(element).find('#data_regular_playtime_avg_then').text(data.regular_playtime_avg_then); + $(element).find('#data_regular_playtime_avg_now').text(data.regular_playtime_avg_now); + $(element).find('#data_regular_playtime_avg_trend').replaceWith(trend(data.regular_playtime_avg_trend)); + $(element).find('#data_regular_session_avg_then').text(data.regular_session_avg_then); + $(element).find('#data_regular_session_avg_now').text(data.regular_session_avg_now); + $(element).find('#data_regular_session_avg_trend').replaceWith(trend(data.regular_session_avg_trend)); + $(element).find('#data_regular_afk_then').text(data.regular_afk_avg_then); + $(element).find('#data_regular_afk_now').text(data.regular_afk_avg_now); + $(element).find('#data_regular_afk_trend').replaceWith(trend(data.regular_afk_avg_trend)); + + // Insights + data = json.insights; + element = $(tab).find('#data_insights'); + + $(element).find('#data_new_to_regular').replaceWith(data.new_to_regular + smallTrend(data.new_to_regular_trend)); + $(element).find('#data_regular_to_inactive').replaceWith(data.regular_to_inactive + smallTrend(data.regular_to_inactive_trend)) +} + +function loadservers(servers, error) { + if (error) { + displayError($('#servers-tab'), error); + return; + } + + if (!servers || !servers.length) { + $('#data_server_list').replaceWith( + '

    No servers found in the database.

    ' + ); + $('#quick_view_players_online').text('No server to display online activity for.'); + return; + } + + var serversHtml = ''; + for (var i = 0; i < servers.length; i++) { + serversHtml += createnetworkserverBox(i, servers[i]); + } + $("#data_server_list").replaceWith(serversHtml); + + for (var i = 0; i < servers.length; i++) { + $('#server_quick_view_' + i).click(onViewserver(i, servers)); + } + onViewserver(0, servers)(); // Open first server. +} + +function createnetworkserverBox(i, server) { + return '
    ' + + '
    ' + + '
    ' + server.name + '
    ' + + '
    ' + + '

    Registered Players' + + '' + server.players + '

    ' + + '

    Players Online' + + '' + server.online + '

    ' + + '
    ' + // /column + '
    ' + // /header + '
    ' + + ' Server Analysis' + + '' + + '
    ' + // /buttons + '
    ' // /card +} + +function onViewserver(i, servers) { + return function () { + setTimeout(function () { + var server = servers[i]; + var playersOnlineSeries = { + name: s.name.playersOnline, + type: s.type.areaSpline, + tooltip: s.tooltip.zeroDecimals, + data: server.playersOnline, + color: v.colors.playersOnline, + yAxis: 0 + }; + $('.data_server_name').text(server.name); + playersChart('quick_view_players_online', playersOnlineSeries, 2); + + var quickView = $('#data_quick_view'); + + quickView.find('#data_last_peak_date').text(server.last_peak_date); + quickView.find('#data_last_peak_players').text(server.last_peak_players); + quickView.find('#data_best_peak_date').text(server.best_peak_date); + quickView.find('#data_best_peak_players').text(server.best_peak_players); + + quickView.find('#data_unique').text(server.unique_players); + quickView.find('#data_new').text(server.new_players); + quickView.find('#data_avg_tps').text(server.avg_tps); + quickView.find('#data_low_tps_spikes').text(server.low_tps_spikes); + quickView.find('#data_downtime').text(server.downtime); + }, 0); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/pingTable.js b/Plan/common/src/main/resources/assets/plan/web/js/pingTable.js new file mode 100644 index 000000000..9ec78e052 --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/pingTable.js @@ -0,0 +1,32 @@ +function loadPingTable(json, error) { + pingTable = $("#geolocations").find("#data_ping_table").find("tbody"); + + if (error) { + pingTable.append(''); + return; + } + + var countries = json; + + if (!countries.length) { + pingTable.append(''); + return; + } + + var tableHtml = ''; + + for (var i = 0; i < countries.length; i++) { + var country = countries[i]; + tableHtml += createPingTableRow(country); + } + + pingTable.append(tableHtml); +} + +function createPingTableRow(entry) { + return '' +} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/player-values.js b/Plan/common/src/main/resources/assets/plan/web/js/player-values.js new file mode 100644 index 000000000..9ac055545 --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/player-values.js @@ -0,0 +1,226 @@ +function loadPlayerOverviewValues(json, error) { + tab = $("#player-overview"); + if (error) { + displayError(tab, error); + return; + } + + // Player information card + data = json.info; + element = $(tab).find("#data_player_info"); + + $(element).find("#data_online").replaceWith(data.online ? ' Online' : ' Offline'); + $(element).find("#data_titles").replaceWith((data.operator ? ' Operator' : '') + (data.banned ? ' Banned' : '')); + $(element).find("#data_kick_count").text(data.kick_count); + $(element).find("#data_player_kills").text(data.player_kill_count); + $(element).find("#data_mob_kills").text(data.mob_kill_count); + $(element).find("#data_deaths").text(data.death_count); + + $(element).find("#data_playtime").text(data.playtime); + $(element).find("#data_active_playtime").text(data.active_playtime); + $(element).find("#data_afk_time").text(data.afk_time); + $(element).find("#data_session_count").text(data.session_count); + $(element).find("#data_longest_session_length").text(data.longest_session_length); + $(element).find("#data_session_median").text(data.session_median); + + $(element).find("#data_activity_index").text(data.activity_index); + $(element).find("#data_activity_index_group").text(data.activity_index_group); + $(element).find("#data_favorite_server").text(data.favorite_server); + $(element).find("#data_average_ping").text(data.average_ping); + $(element).find("#data_best_ping").text(data.best_ping); + $(element).find("#data_worst_ping").text(data.worst_ping); + + $(element).find("#data_registered").text(data.registered); + $(element).find("#data_last_seen").text(data.last_seen); + + $('#data_nicknames').replaceWith(createNicknameTableBody(json.nicknames)); + + $('#data_connections').replaceWith(createConnectionsTableBody(json.connections)); + + // Online activity + data = json.online_activity; + element = $(tab).find("#data_online_activity"); + + $(element).find("#data_playtime_30d").text(data.playtime_30d); + $(element).find("#data_playtime_7d").text(data.playtime_7d); + $(element).find("#data_active_playtime_30d").text(data.active_playtime_30d); + $(element).find("#data_active_playtime_7d").text(data.active_playtime_7d); + $(element).find("#data_afk_time_30d").text(data.afk_time_30d); + $(element).find("#data_afk_time_7d").text(data.afk_time_7d); + $(element).find("#data_average_session_length_30d").text(data.average_session_length_30d); + $(element).find("#data_average_session_length_7d").text(data.average_session_length_7d); + $(element).find("#data_session_count_30d").text(data.session_count_30d); + $(element).find("#data_session_count_7d").text(data.session_count_7d); + $(element).find("#data_player_kills_30d").text(data.player_kill_count_30d); + $(element).find("#data_player_kills_7d").text(data.player_kill_count_7d); + $(element).find("#data_mob_kills_30d").text(data.mob_kill_count_30d); + $(element).find("#data_mob_kills_7d").text(data.mob_kill_count_7d); + $(element).find("#data_deaths_30d").text(data.death_count_30d); + $(element).find("#data_deaths_7d").text(data.death_count_7d) +} + +/* This function loads PvP & PvE tab */ +function loadPvPPvEValues(json, error) { + tab = $('#pvp-pve'); + if (error) { + displayError(tab, error); + return; + } + + // as Numbers + data = json.kill_data; + element = $(tab).find('#data_numbers'); + + $(element).find('#data_player_kills_total').text(data.player_kills_total); + $(element).find('#data_player_kills_30d').text(data.player_kills_30d); + $(element).find('#data_player_kills_7d').text(data.player_kills_7d); + + $(element).find('#data_player_deaths_total').text(data.player_deaths_total); + $(element).find('#data_player_deaths_30d').text(data.player_deaths_30d); + $(element).find('#data_player_deaths_7d').text(data.player_deaths_7d); + + $(element).find('#data_player_kdr_total').text(data.player_kdr_total); + $(element).find('#data_player_kdr_30d').text(data.player_kdr_30d); + $(element).find('#data_player_kdr_7d').text(data.player_kdr_7d); + + $(element).find('#data_mob_kills_total').text(data.mob_kills_total); + $(element).find('#data_mob_kills_30d').text(data.mob_kills_30d); + $(element).find('#data_mob_kills_7d').text(data.mob_kills_7d); + + $(element).find('#data_mob_deaths_total').text(data.mob_deaths_total); + $(element).find('#data_mob_deaths_30d').text(data.mob_deaths_30d); + $(element).find('#data_mob_deaths_7d').text(data.mob_deaths_7d); + + $(element).find('#data_mob_kdr_total').text(data.mob_kdr_total); + $(element).find('#data_mob_kdr_30d').text(data.mob_kdr_30d); + $(element).find('#data_mob_kdr_7d').text(data.mob_kdr_7d); + + $(element).find('#data_deaths_total').text(data.deaths_total); + $(element).find('#data_deaths_30d').text(data.deaths_30d); + $(element).find('#data_deaths_7d').text(data.deaths_7d); + + // Insights + element = $(tab).find('#data_insights'); + + $(element).find('#data_weapon_1st').text(data.weapon_1st); + $(element).find('#data_weapon_2nd').text(data.weapon_2nd); + $(element).find('#data_weapon_3rd').text(data.weapon_3rd); +} + +function createNicknameTableBody(nicknames) { + var table = ''; + + if (nicknames.length === 0) { + table += '' + } + + for (var i = 0; i < nicknames.length; i++) { + var nickname = nicknames[i]; + table += '' + + '' + + '' + } + + table += ''; + return table; +} + +function createConnectionsTableBody(connections) { + var table = ''; + + if (connections.length === 0) { + table += '' + } + + for (var i = 0; i < connections.length; i++) { + var connection = connections[i]; + table += '' + + '' + } + + table += ''; + return table; +} + +function loadserverAccordion(json, error) { + tab = $("#server-overview"); + if (error) { + displayError(tab, error); + return; + } + + serverTable = tab.find("#tableSAccordion").find("tbody"); + + var servers = json.servers; + + if (!servers.length) { + serverTable.append('') + } + + var serversHtml = ''; + for (var i = 0; i < servers.length; i++) { + var server = servers[i]; + var title = createserverAccordionTitle(i, server); + var body = createserverAccordionBody(i, server); + + serversHtml += title + body; + } + + serverTable.append(serversHtml); + + for (var i = 0; i < servers.length; i++) { + $('#server_h_' + i).click(onOpenserver(i, servers)); + } +} + +function onOpenserver(i, servers) { + var opened = false; + return function () { + if (opened) { + return; + } + setTimeout(function () { + var server = servers[i]; + var worldSeries = {name: 'World Playtime', colorByPoint: true, data: server.world_pie_series}; + var gmSeries = server.gm_series; + + worldPie("worldpie_server_" + i, worldSeries, gmSeries, '#3A3B45'); + }, 250); + opened = true; + } +} + +function createserverAccordionTitle(i, server) { + return '' + + '' + + '' + + '' +} + +function createserverAccordionBody(i, server) { + + return '' + + '' +} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.js b/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.js new file mode 100644 index 000000000..bc762acb0 --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.js @@ -0,0 +1,147 @@ +function openTab(i) { + var x = document.getElementById("content"); + var navButtons = document.getElementsByClassName("nav-button"); + var max = navButtons.length; + for (var j = 0; j < max; j++) { + if (navButtons[j].classList.contains('active')) { + navButtons[j].classList.remove('active'); + } + if (j === i) { + navButtons[j].classList.add('active'); + } + } + var percent = -100 / navButtons.length; + slideIndex = i; + if (slideIndex > max) { + slideIndex = 0 + } + if (slideIndex < 0) { + slideIndex = max + } + window.scrollTo(0, 0); + var value = slideIndex * percent; + x.style.transition = "0.5s"; + x.style.transform = "translate3d(" + value + "%,0px,0)"; +} + +function openPage() { + var params = (window.location.hash.substr(5)).split("&"); + + if (!params.length) { + openTab(0); + return; + } + // window.sessionStorage.setItem("server_slide_index", slideIndex); + + var tabID = params[0]; + var button = $('.nav-button[href="#' + tabID + '"]'); + + var tabs = document.getElementsByClassName("tab"); + for (var i = 0; i < tabs.length; i++) { + if (tabs[i].id === tabID) openTab(i); + } + + if (params.length <= 1) { + return; + } + + var graphTabID = params[1]; + $('a[href="#' + graphTabID + '"]').tab('show'); +} + +(function ($) { + "use strict"; // Start of use strict + + var x = document.getElementById("content"); + // Prepare tabs for display + var navButtons = document.getElementsByClassName("nav-button"); + var tabs = document.getElementsByClassName("tab"); + x.style.transform = "translate3d(0px,0px,0)"; + x.style.width = "" + navButtons.length * 100 + "%"; + for (var i = 0; i < navButtons.length; i++) { + tabs[i].style.width = "" + 100 / navButtons.length + "%"; + } + x.style.opacity = "1"; + + window.addEventListener('hashchange', function (e) { + openPage(); + }); + + // Persistent Bootstrap tabs + $('.nav-tabs a.nav-link').click(function (e) { + var params = (window.location.hash).split("&"); + if (!params) return; + window.location.hash = params[0] + '&' + e.target.href.split('#')[1]; + }); + + var oldWidth = null; + + function reduceSidebar() { + var newWidth = $(window).width(); + if (oldWidth && oldWidth === newWidth) { + return; + } + + var $sidebar = $('.sidebar'); + var closeModal = $('.sidebar-close-modal'); + if ($(window).width() < 1350) { + if (!$sidebar.hasClass('hidden')) $sidebar.addClass('hidden'); + if (!closeModal.hasClass('hidden')) closeModal.addClass('hidden'); + + $('.sidebar .collapse').collapse('hide'); + } else if ($(window).width() > 1400 && $sidebar.hasClass('hidden')) { + $sidebar.removeClass('hidden'); + if (!closeModal.hasClass('hidden')) closeModal.addClass('hidden'); + } + oldWidth = newWidth; + } + + reduceSidebar(); + + function toggleSidebar() { + $('.sidebar').toggleClass('hidden'); + $('.sidebar .collapse').collapse('hide'); + + var closeModal = $('.sidebar-close-modal'); + if ($(window).width() < 900) { + closeModal.toggleClass('hidden'); + } else { + if (!closeModal.hasClass('hidden')) closeModal.addClass('hidden'); + } + } + + $('.sidebar-toggler,.sidebar-close-modal').on('click', toggleSidebar); + + // Close any open menu accordions when window is resized below 924px + $(window).resize(reduceSidebar); + + // Prevent the content wrapper from scrolling when the fixed side navigation hovered over + $('body.fixed-nav .sidebar').on('mousewheel DOMMouseScroll wheel', function (e) { + if ($(window).width() > 924) { + var e0 = e.originalEvent, + delta = e0.wheelDelta || -e0.detail; + this.scrollTop += (delta < 0 ? 1 : -1) * 30; + e.preventDefault(); + } + }); + + // Scroll to top button appear + $(document).on('scroll', function () { + var scrollDistance = $(this).scrollTop(); + if (scrollDistance > 100) { + $('.scroll-to-top').fadeIn(); + } else { + $('.scroll-to-top').fadeOut(); + } + }); + + // Smooth scrolling using jQuery easing + $(document).on('click', 'a.scroll-to-top', function (e) { + var $anchor = $(this); + $('html, body').stop().animate({ + scrollTop: ($($anchor.attr('href')).offset().top) + }, 1000, 'easeInOutExpo'); + e.preventDefault(); + }); + +})(jQuery); // End of use strict diff --git a/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.min.js b/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.min.js new file mode 100644 index 000000000..1ea3e4e0c --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/sb-admin-2.min.js @@ -0,0 +1,7 @@ +/*! + * Start Bootstrap - SB Admin 2 v4.0.0 (https://startbootstrap.com/template-overviews/sb-admin-2) + * Copyright 2013-2019 Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-sb-admin-2/blob/master/LICENSE) + */ + +!function(t){"use strict";t("#sidebarToggle, #sidebarToggleTop").on("click",function(o){t("body").toggleClass("sidebar-toggled"),t(".sidebar").toggleClass("toggled"),t(".sidebar").hasClass("toggled")&&t(".sidebar .collapse").collapse("hide")}),t(window).resize(function(){t(window).width()<768&&t(".sidebar .collapse").collapse("hide")}),t("body.fixed-nav .sidebar").on("mousewheel DOMMouseScroll wheel",function(o){if(768 "; +var trend_up_bad = " "; +var trend_down_bad = " "; +var trend_down_good = " "; +var trend_same = " "; + +var trend_end = ""; + +function trend(trend) { + if (!trend) { + return trend_same + '?' + trend_end; + } + switch (trend.direction) { + case '+': + return (trend.reversed ? trend_up_bad : trend_up_good) + trend.text + trend_end; + case '-': + return (trend.reversed ? trend_down_good : trend_down_bad) + trend.text + trend_end; + default: + return trend_same + trend.text + trend_end; + } +} + +function smallTrend(trend) { + if (!trend) { + return ' '; + } + switch (trend.direction) { + case '+': + trend_color = trend.reversed ? 'text-danger' : 'text-success'; + return ' '; + case '-': + trend_color = trend.reversed ? 'text-success' : 'text-danger'; + return ' '; + default: + return ' '; + } +} + +function displayError(element, error) { + element.find('.d-sm-flex').after('') +} + +/* This function loads Server Overview tab */ +function loadserverOverviewValues(json, error) { + tab = $('#server-overview'); + + if (error) { + displayError(tab, error); + return; + } + + // Last 7 days + data = json.last_7_days; + element = $(tab).find('#data_7_days'); + + $(element).find('#data_unique').text(data.unique_players); + $(element).find('#data_unique_day').text(data.unique_players_day); + $(element).find('#data_new').text(data.new_players); + $(element).find('#data_retention').text('(' + data.new_players_retention + '/' + data.new_players + ')'); + $(element).find('#data_retention_perc').text(data.new_players_retention_perc); + + $(element).find('#data_avg_tps').text(data.average_tps); + $(element).find('#data_low_tps_spikes').text(data.low_tps_spikes); + $(element).find('#data_downtime').text(data.downtime); + + // Server As Numbers + data = json.numbers; + element = $(tab).find('#data_numbers'); + + $(element).find('#data_total').text(data.total_players); + $(element).find('#data_regular').text(data.regular_players); + $(element).find('#data_online').text(data.online_players); + + $(element).find('#data_last_peak_date').text(data.last_peak_date); + $(element).find('#data_last_peak_players').text(data.last_peak_players); + $(element).find('#data_best_peak_date').text(data.best_peak_date); + $(element).find('#data_best_peak_players').text(data.best_peak_players); + + $(element).find('#data_playtime').text(data.playtime); + $(element).find('#data_player_playtime').text(data.player_playtime); + $(element).find('#data_sessions').text(data.sessions); + + $(element).find('#data_player_kills').text(data.player_kills); + $(element).find('#data_mob_kills').text(data.mob_kills); + $(element).find('#data_deaths').text(data.deaths); + + // Week Comparison + data = json.weeks; + element = $(tab).find('#data_weeks'); + + $(element).find('#data_start').text(data.start); + $(element).find('#data_midpoint').text(data.midpoint); + $(element).find('#data_midpoint2').text(data.midpoint); + $(element).find('#data_end').text(data.end); + + $(element).find('#data_unique_before').text(data.unique_before); + $(element).find('#data_unique_after').text(data.unique_after); + $(element).find('#data_unique_trend').replaceWith(trend(data.unique_trend)); + $(element).find('#data_new_before').text(data.new_before); + $(element).find('#data_new_after').text(data.new_after); + $(element).find('#data_new_trend').replaceWith(trend(data.new_trend)); + $(element).find('#data_regular_before').text(data.regular_before); + $(element).find('#data_regular_after').text(data.regular_after); + $(element).find('#data_regular_trend').replaceWith(trend(data.regular_trend)); + + $(element).find('#data_average_playtime_before').text(data.average_playtime_before); + $(element).find('#data_average_playtime_after').text(data.average_playtime_after); + $(element).find('#data_average_playtime_trend').replaceWith(trend(data.average_playtime_trend)); + $(element).find('#data_sessions_before').text(data.sessions_before); + $(element).find('#data_sessions_after').text(data.sessions_after); + $(element).find('#data_sessions_trend').replaceWith(trend(data.sessions_trend)); + + $(element).find('#data_player_kills_before').text(data.player_kills_before); + $(element).find('#data_player_kills_after').text(data.player_kills_after); + $(element).find('#data_player_kills_trend').replaceWith(trend(data.player_kills_trend)); + $(element).find('#data_mob_kills_before').text(data.mob_kills_before); + $(element).find('#data_mob_kills_after').text(data.mob_kills_after); + $(element).find('#data_mob_kills_trend').replaceWith(trend(data.mob_kills_trend)); + $(element).find('#data_deaths_before').text(data.deaths_before); + $(element).find('#data_deaths_after').text(data.deaths_after); + $(element).find('#data_deaths_trend').replaceWith(trend(data.deaths_trend)) +} + +/* This function loads Online Activity Overview tab */ +function loadOnlineActivityOverviewValues(json, error) { + tab = $('#online-activity-overview'); + + if (error) { + displayError(tab, error); + return; + } + + // Online Activity as Numbers + data = json.numbers; + element = $(tab).find('#data_numbers'); + + $(element).find('#data_unique_players_30d').replaceWith(''); + $(element).find('#data_unique_players_7d').text(data.unique_players_7d); + $(element).find('#data_unique_players_24h').text(data.unique_players_24h); + + $(element).find('#data_unique_players_30d_avg').replaceWith(''); + $(element).find('#data_unique_players_7d_avg').text(data.unique_players_7d_avg); + $(element).find('#data_unique_players_24h_avg').text(data.unique_players_24h_avg); + + $(element).find('#data_new_players_30d').replaceWith(''); + $(element).find('#data_new_players_7d').text(data.new_players_7d); + $(element).find('#data_new_players_24h').text(data.new_players_24h); + + $(element).find('#data_new_players_30d_avg').replaceWith(''); + $(element).find('#data_new_players_7d_avg').text(data.new_players_7d_avg); + $(element).find('#data_new_players_24h_avg').text(data.new_players_24h_avg); + + $(element).find('#data_new_players_retention_30d').text('(' + data.new_players_retention_30d + '/' + data.new_players_30d + ') ' + data.new_players_retention_30d_perc); + $(element).find('#data_new_players_retention_7d').text('(' + data.new_players_retention_7d + '/' + data.new_players_7d + ') ' + data.new_players_retention_7d_perc); + $(element).find('#data_new_players_retention_24h').replaceWith(''); + + $(element).find('#data_playtime_30d').replaceWith(''); + $(element).find('#data_playtime_7d').text(data.playtime_7d); + $(element).find('#data_playtime_24h').text(data.playtime_24h); + + $(element).find('#data_playtime_30d_avg').replaceWith(''); + $(element).find('#data_playtime_7d_avg').text(data.playtime_7d_avg); + $(element).find('#data_playtime_24h_avg').text(data.playtime_24h_avg); + + $(element).find('#data_session_length_30d_avg').replaceWith(''); + $(element).find('#data_session_length_7d_avg').text(data.session_length_7d_avg); + $(element).find('#data_session_length_24h_avg').text(data.session_length_24h_avg); + + $(element).find('#data_sessions_30d').replaceWith(''); + $(element).find('#data_sessions_7d').text(data.sessions_7d); + $(element).find('#data_sessions_24h').text(data.sessions_24h); + + // Insights + data = json.insights; + element = $(tab).find('#data_insights'); + + $(element).find('#data_players_first_join_avg').replaceWith(data.players_first_join_avg + smallTrend(data.players_first_join_trend)); + $(element).find('#data_first_session_length_avg').replaceWith(data.first_session_length_avg + smallTrend(data.first_session_length_trend)); + $(element).find('#data_lone_joins').replaceWith(data.lone_joins + smallTrend(data.lone_joins_trend)); + $(element).find('#data_lone_new_joins').replaceWith(data.lone_new_joins + smallTrend(data.lone_new_joins_trend)) +} + +/* This function loads Sessions tab */ +function loadSessionValues(json, error) { + tab = $('#sessions-overview'); + + if (error) { + displayError(tab, error); + return; + } + + // Insights + data = json.insights; + element = $(tab).find('#data_insights'); + + $(element).find('#data_most_active_gamemode').text(data.most_active_gamemode); + $(element).find('#data_most_active_gamemode_perc').text(data.most_active_gamemode_perc); + $(element).find('#data_server_occupied').text("~" + data.server_occupied); + $(element).find('#data_server_occupied_perc').text(data.server_occupied_perc); + $(element).find('#data_total_playtime').text(data.total_playtime); + $(element).find('#data_afk_time').text(data.afk_time); + $(element).find('#data_afk_time_perc').text(data.afk_time_perc) +} + +/* This function loads PvP & PvE tab */ +function loadPvPPvEValues(json, error) { + tab = $('#pvp-pve'); + if (error) { + displayError(tab, error); + return; + } + + // as Numbers + data = json.numbers; + element = $(tab).find('#data_numbers'); + + $(element).find('#data_player_kills_total').text(data.player_kills_total); + $(element).find('#data_player_kills_30d').text(data.player_kills_30d); + $(element).find('#data_player_kills_7d').text(data.player_kills_7d); + + $(element).find('#data_player_kdr_avg').text(data.player_kdr_avg); + $(element).find('#data_player_kdr_avg_30d').text(data.player_kdr_avg_30d); + $(element).find('#data_player_kdr_avg_7d').text(data.player_kdr_avg_7d); + + $(element).find('#data_mob_kills_total').text(data.mob_kills_total); + $(element).find('#data_mob_kills_30d').text(data.mob_kills_30d); + $(element).find('#data_mob_kills_7d').text(data.mob_kills_7d); + + $(element).find('#data_mob_deaths_total').text(data.mob_deaths_total); + $(element).find('#data_mob_deaths_30d').text(data.mob_deaths_30d); + $(element).find('#data_mob_deaths_7d').text(data.mob_deaths_7d); + + $(element).find('#data_mob_kdr_total').text(data.mob_kdr_total); + $(element).find('#data_mob_kdr_30d').text(data.mob_kdr_30d); + $(element).find('#data_mob_kdr_7d').text(data.mob_kdr_7d); + + $(element).find('#data_deaths_total').text(data.deaths_total); + $(element).find('#data_deaths_30d').text(data.deaths_30d); + $(element).find('#data_deaths_7d').text(data.deaths_7d); + + // Insights + data = json.insights; + element = $(tab).find('#data_insights'); + + $(element).find('#data_weapon_1st').text(data.weapon_1st); + $(element).find('#data_weapon_2nd').text(data.weapon_2nd); + $(element).find('#data_weapon_3rd').text(data.weapon_3rd); +} + +/* This function loads Playerbase Overview tab */ +function loadPlayerbaseOverviewValues(json, error) { + tab = $('#playerbase-overview'); + if (error) { + displayError(tab, error); + return; + } + + // Trends + data = json.trends; + element = $(tab).find('#data_trends'); + + $(element).find('#data_total_players_then').text(data.total_players_then); + $(element).find('#data_total_players_now').text(data.total_players_now); + $(element).find('#data_total_players_trend').replaceWith(trend(data.total_players_trend)); + $(element).find('#data_regular_players_then').text(data.regular_players_then); + $(element).find('#data_regular_players_now').text(data.regular_players_now); + $(element).find('#data_regular_players_trend').replaceWith(trend(data.regular_players_trend)); + $(element).find('#data_playtime_avg_then').text(data.playtime_avg_then); + $(element).find('#data_playtime_avg_now').text(data.playtime_avg_now); + $(element).find('#data_playtime_avg_trend').replaceWith(trend(data.playtime_avg_trend)); + $(element).find('#data_afk_then').text(data.afk_then); + $(element).find('#data_afk_now').text(data.afk_now); + $(element).find('#data_afk_trend').replaceWith(trend(data.afk_trend)); + $(element).find('#data_regular_playtime_avg_then').text(data.regular_playtime_avg_then); + $(element).find('#data_regular_playtime_avg_now').text(data.regular_playtime_avg_now); + $(element).find('#data_regular_playtime_avg_trend').replaceWith(trend(data.regular_playtime_avg_trend)); + $(element).find('#data_regular_session_avg_then').text(data.regular_session_avg_then); + $(element).find('#data_regular_session_avg_now').text(data.regular_session_avg_now); + $(element).find('#data_regular_session_avg_trend').replaceWith(trend(data.regular_session_avg_trend)); + $(element).find('#data_regular_afk_then').text(data.regular_afk_avg_then); + $(element).find('#data_regular_afk_now').text(data.regular_afk_avg_now); + $(element).find('#data_regular_afk_trend').replaceWith(trend(data.regular_afk_avg_trend)); + + // Insights + data = json.insights; + element = $(tab).find('#data_insights'); + + $(element).find('#data_new_to_regular').replaceWith(data.new_to_regular + smallTrend(data.new_to_regular_trend)); + $(element).find('#data_regular_to_inactive').replaceWith(data.regular_to_inactive + smallTrend(data.regular_to_inactive_trend)) +} + +/* This function loads Performance tab */ +function loadPerformanceValues(json, error) { + tab = $('#performance'); + if (error) { + displayError(tab, error); + return; + } + + // as Numbers + data = json.numbers; + element = $(tab).find('#data_numbers'); + + $(element).find('#data_low_tps_spikes_30d').text(data.low_tps_spikes_30d); + $(element).find('#data_low_tps_spikes_7d').text(data.low_tps_spikes_7d); + $(element).find('#data_low_tps_spikes_24h').text(data.low_tps_spikes_24h); + $(element).find('#data_server_downtime_30d').text(data.server_downtime_30d); + $(element).find('#data_server_downtime_7d').text(data.server_downtime_7d); + $(element).find('#data_server_downtime_24h').text(data.server_downtime_24h); + $(element).find('#data_tps_30d').text(data.tps_30d); + $(element).find('#data_tps_7d').text(data.tps_7d); + $(element).find('#data_tps_24h').text(data.tps_24h); + $(element).find('#data_cpu_30d').text(data.cpu_30d); + $(element).find('#data_cpu_7d').text(data.cpu_7d); + $(element).find('#data_cpu_24h').text(data.cpu_24h); + $(element).find('#data_ram_30d').text(data.ram_30d); + $(element).find('#data_ram_7d').text(data.ram_7d); + $(element).find('#data_ram_24h').text(data.ram_24h); + $(element).find('#data_entities_30d').text(data.entities_30d); + $(element).find('#data_entities_7d').text(data.entities_7d); + $(element).find('#data_entities_24h').text(data.entities_24h); + $(element).find('#data_chunks_30d').text(data.chunks_30d); + $(element).find('#data_chunks_7d').text(data.chunks_7d); + $(element).find('#data_chunks_24h').text(data.chunks_24h); + $(element).find('#data_max_disk_30d').text(data.max_disk_30d); + $(element).find('#data_max_disk_7d').text(data.max_disk_7d); + $(element).find('#data_max_disk_24h').text(data.max_disk_24h); + $(element).find('#data_min_disk_30d').text(data.min_disk_30d); + $(element).find('#data_min_disk_7d').text(data.min_disk_7d); + $(element).find('#data_min_disk_24h').text(data.min_disk_24h); + + // Insights + data = json.insights; + element = $(tab).find('#data_insights'); + + $(element).find('#data_low_tps_players').text(data.low_tps_players); + $(element).find('#data_low_tps_entities').text(data.low_tps_entities); + $(element).find('#data_low_tps_chunks').text(data.low_tps_chunks); + $(element).find('#data_low_tps_cpu').text(data.low_tps_cpu); + + dates = data.low_disk_space_dates; + dateString = ''; + for (i in dates) { + dateString += (dates[i] + '
    ') + } + + $(element).find('#data_low_disk_space_dates').replaceWith( + dateString + ) +} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/sessionAccordion.js b/Plan/common/src/main/resources/assets/plan/web/js/sessionAccordion.js new file mode 100644 index 000000000..9a7e79c6e --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/sessionAccordion.js @@ -0,0 +1,115 @@ +function loadSessionAccordion(json, error) { + sessionTable = $("#sessions-overview").find("#tableAccordion").find("tbody"); + + if (error) { + sessionTable.append(''); + return; + } + + var sessions = json.sessions; + + if (!sessions.length) { + sessionTable.append(''); + return; + } + + // sessions_per_page can be undefined (-> NaN) or higher than amount of sessions. + var limit = json.sessions_per_page ? json.sessions_per_page : sessions.length; + limit = Math.min(limit, sessions.length); + + var sessionsHtml = ''; + for (var i = 0; i < limit; i++) { + var session = sessions[i]; + var title = createAccordionTitle(i, session); + var body = createAccordionBody(i, session); + sessionsHtml += title + body; + } + + sessionTable.append(sessionsHtml); + + for (var i = 0; i < limit; i++) { + $('#session_h_' + i).click(onOpenSession(i, sessions)); + } +} + +function onOpenSession(i, sessions) { + var opened = false; + return function () { + if (opened) { + return; + } + setTimeout(function () { + var session = sessions[i]; + var worldSeries = {name: 'World Playtime', colorByPoint: true, data: session.world_series}; + var gmSeries = session.gm_series; + + worldPie("worldpie_" + i, worldSeries, gmSeries, '#3A3B45'); + }, 250); + opened = true; + } +} + +function loadPlayerKills(json, error) { + if (error) { + $('#playerKillTable').replaceWith('

    Failed to load player kills: ' + error + '

    '); + return; + } + $('#playerKillTable').replaceWith(createKillsTable(json.player_kills)); +} + +function loadPlayerDeaths(json, error) { + if (error) { + $('#playerDeathTable').replaceWith('

    Failed to load player deaths: ' + error + '

    '); + return; + } + $('#playerDeathTable').replaceWith(createKillsTable(json.player_deaths)); +} + +function createAccordionTitle(i, session) { + return '' + + '' + + '' + + '' +} + +function createAccordionBody(i, session) { + return '' + + '' +} + +function createKillsTable(player_kills) { + var table = '
    ${0}${1}${2}${3}
    ${0}${1}
    ${0}${1}${2}
    ${0}${1}${2}${3}
    ${0}${1}${3}${4}${6}${8}${9}
    ${0}${2}${3}${4}${5}${6}
    ${1}${3}${5}
    ${1}${2}${3}
    With"), + NO_KILLS("No Kills"), + LABEL_MAX_FREE_DISK("Max Free Disk"), + LABEL_MIN_FREE_DISK("Min Free Disk") + ; + + private final String defaultValue; + + HtmlLang(String defaultValue) { + this.defaultValue = defaultValue; + } + + @Override + public String getIdentifier() { + return "HTML - " + name(); + } + + @Override + public String getDefault() { + return defaultValue; + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CommonHtmlLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/JSLang.java similarity index 52% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CommonHtmlLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/JSLang.java index f4849379d..2dc403a6a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/CommonHtmlLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/JSLang.java @@ -14,36 +14,34 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** - * {@link Lang} implementation for commonly used .html replacement values. + * Lang enum for all text included in the javascript files. * * @author Rsl1122 */ -public enum CommonHtmlLang implements Lang { - PLEASE_WAIT("Please wait..."), +public enum JSLang implements Lang { - NAV_INFORMATION("Information"), - NAV_SESSIONS("Sessions"), - NAV_OVERVIEW("Overview"), - NAV_PLUGINS("Plugins"), - NAV_ONLINE_ACTIVITY("Online Activity"), - NAV_SEVER_HEALTH("Server Health"), - NAV_PERFORMANCE("Performance"), - NAV_PLAYERS("Players"), - NAV_GEOLOCATIONS("Geolocations"), - NAV_COMMAND_USAGE("Command Usage"), - NAV_NETWORK_PLAYERS("Network Players"), - - AVERAGE_PING("Average Ping"), - BEST_PING("Best Ping"), - WORST_PING("Worst Ping"), - PLAYERS_ONLINE_TEXT("Players Online"); + TEXT_PREDICTED_RETENTION("This value is a prediction based on previous players"), + TEXT_NO_SERVERS("No servers found in the database"), + TEXT_NO_SERVER("No server to display online activity for"), + LABEL_REGISTERED_PLAYERS("Registered Players"), + LINK_SERVER_ANALYSIS("Server Analysis"), + LINK_QUICK_VIEW("Quick view"), + TEXT_FIRST_SESSION("First session"), + LABEL_SESSION_ENDED("Ended"), + LINK_PLAYER_PAGE("Player Page"), + LABEL_NO_SESSION_KILLS("None"), + UNIT_ENTITIES("Entities"), + UNIT_CHUNKS("Chunks"), + LABEL_RELATIVE_JOIN_ACTIVITY("Relative Join Activity"), + LABEL_DAY_OF_WEEK("Day of the Week"), + LABEL_WEEK_DAYS("'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'"); private final String defaultValue; - CommonHtmlLang(String defaultValue) { + JSLang(String defaultValue) { this.defaultValue = defaultValue; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/Lang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/Lang.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/Lang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/Lang.java index 41c9a5dbb..e9ac206cc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/Lang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/Lang.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** * Interface for splitting different Language categories into different classes. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ManageLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ManageLang.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ManageLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ManageLang.java index 7d706ef11..25c730554 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ManageLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/ManageLang.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** * {@link Lang} implementation for Manage command related subcommand language. @@ -31,13 +31,6 @@ public enum ManageLang implements Lang { CONFIRMATION("Manage - Fail, Confirmation", "> §cAdd '-a' argument to confirm execution: ${0}"), IMPORTERS("Manage - List Importers", "Importers: "), - CON_NO_SERVERS("Manage - Fail, No Servers", "§cNo Servers found in the database."), - CON_EXCEPTION("Manage - Fail, Unexpected Exception", "§eOdd Exception: ${0}"), - CON_UNAUTHORIZED("Manage - Fail, Unauthorized", "§eFail reason: Unauthorized. Server might be using different database."), - CON_GENERIC_FAIL("Manage - Fail, Connection Exception", "§eFail reason: "), - CON_EXTERNAL_URL("Manage - Notify External Url", "§eNon-local address, check that port is open"), - CON_OLD_VERSION("Manage - Fail, Old version", "§eFail reason: Older Plan version on receiving server"), - CONFIRM_OVERWRITE("Manage - Confirm Overwrite", "Data in ${0} will be overwritten!"), CONFIRM_REMOVAL("Manage - Confirm Removal", "Data in ${0} will be removed!"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/PluginLang.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/PluginLang.java index 931b80b34..f509f0c3c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PluginLang.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/locale/lang/PluginLang.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.locale.lang; +package com.djrapitops.plan.settings.locale.lang; /** * {@link Lang} implementation for Language that is logged when the plugin enables or disables. @@ -30,8 +30,6 @@ public enum PluginLang implements Lang { ENABLE_NOTIFY_WEB_SERVER_DISABLED("Enable - Notify Webserver disabled", "WebServer was not initialized. (WebServer.DisableWebServer: true)"), ENABLE_NOTIFY_GEOLOCATIONS_INTERNET_REQUIRED("Enable - Notify Geolocations Internet Required", "Plan Requires internet access on first run to download GeoLite2 Geolocation database."), ENABLE_NOTIFY_GEOLOCATIONS_DISABLED("Enable - Notify Geolocations disabled", "Geolocation gathering is not active. (Data.Geolocations: false)"), - ENABLE_NOTIFY_ADDRESS_CONFIRMATION("Enable - Notify Address Confirmation", "Make sure that this address points to THIS Server: ${0}"), - ENABLE_FAIL_DB("Enable FAIL - Database", "${0}-Database Connection failed: ${1}"), ENABLE_FAIL_WRONG_DB("Enable FAIL - Wrong Database Type", "${0} is not a supported Database"), ENABLE_FAIL_DB_PATCH("Enable FAIL - Database Patch", "Database Patching failed, plugin has to be disabled. Please report this issue"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettingManager.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/network/NetworkSettingManager.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettingManager.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/network/NetworkSettingManager.java index 84b5a5c9e..7ebaf7a48 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/NetworkSettingManager.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/network/NetworkSettingManager.java @@ -14,22 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.network; +package com.djrapitops.plan.settings.network; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.NewerConfigQuery; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.transactions.StoreConfigTransaction; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.*; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.utilities.file.FileWatcher; -import com.djrapitops.plan.utilities.file.WatchedFile; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.*; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.upkeep.FileWatcher; +import com.djrapitops.plan.settings.upkeep.WatchedFile; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.NewerConfigQuery; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.transactions.StoreConfigTransaction; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/ServerSettingsManager.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/network/ServerSettingsManager.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/ServerSettingsManager.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/network/ServerSettingsManager.java index c509a2bd3..5e6792767 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/network/ServerSettingsManager.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/network/ServerSettingsManager.java @@ -14,23 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.network; +package com.djrapitops.plan.settings.network; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.NewerConfigQuery; -import com.djrapitops.plan.db.access.transactions.StoreConfigTransaction; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.Config; -import com.djrapitops.plan.system.settings.config.ConfigReader; -import com.djrapitops.plan.system.settings.config.ConfigWriter; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.system.tasks.TaskSystem; -import com.djrapitops.plan.utilities.file.FileWatcher; -import com.djrapitops.plan.utilities.file.WatchedFile; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.TaskSystem; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.Config; +import com.djrapitops.plan.settings.config.ConfigReader; +import com.djrapitops.plan.settings.config.ConfigWriter; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.upkeep.FileWatcher; +import com.djrapitops.plan.settings.upkeep.WatchedFile; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.objects.NewerConfigQuery; +import com.djrapitops.plan.storage.database.transactions.StoreConfigTransaction; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; @@ -42,6 +42,7 @@ import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.TimeUnit; /** @@ -108,10 +109,14 @@ public class ServerSettingsManager implements SubSystem { } Database database = dbSystem.getDatabase(); + Optional serverUUID = serverInfo.getServerUUIDSafe(); + if (!serverUUID.isPresent()) { + return; + } try (ConfigReader reader = new ConfigReader(file.toPath())) { - Config config = reader.read(); - database.executeTransaction(new StoreConfigTransaction(serverInfo.getServerUUID(), config, file.lastModified())); + Config read = reader.read(); + database.executeTransaction(new StoreConfigTransaction(serverUUID.get(), read, file.lastModified())); logger.debug("Server config saved to database."); } catch (IOException e) { throw new UncheckedIOException(e); @@ -132,7 +137,12 @@ public class ServerSettingsManager implements SubSystem { File configFile = files.getConfigFile(); long lastModified = configFile.exists() ? configFile.lastModified() : -1; - Optional foundConfig = database.query(new NewerConfigQuery(serverInfo.getServerUUID(), lastModified)); + Optional serverUUID = serverInfo.getServerUUIDSafe(); + if (!serverUUID.isPresent()) { + return; + } + + Optional foundConfig = database.query(new NewerConfigQuery(serverUUID.get(), lastModified)); if (foundConfig.isPresent()) { try { new ConfigWriter(configFile.toPath()).write(foundConfig.get()); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/PlanColorScheme.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/theme/PlanColorScheme.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/PlanColorScheme.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/theme/PlanColorScheme.java index 034534879..fb62f6dff 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/PlanColorScheme.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/theme/PlanColorScheme.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.theme; +package com.djrapitops.plan.settings.theme; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DisplaySettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; import com.djrapitops.plugin.command.ColorScheme; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/Theme.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/theme/Theme.java similarity index 68% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/Theme.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/theme/Theme.java index 75f694279..883538d8c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/Theme.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/theme/Theme.java @@ -14,20 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.theme; +package com.djrapitops.plan.settings.theme; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.console.PluginLogger; -import com.djrapitops.plugin.utilities.Verify; +import org.apache.commons.lang3.StringUtils; import javax.inject.Inject; import javax.inject.Singleton; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; -import static com.djrapitops.plan.system.settings.theme.ThemeVal.*; +import static com.djrapitops.plan.settings.theme.ThemeVal.*; /** * Enum that contains available themes. @@ -60,6 +63,12 @@ public class Theme implements SubSystem { } } + public String[] getPieColors(ThemeVal variable) { + return Arrays.stream(StringUtils.split(getValue(variable), ',')) + .map(color -> StringUtils.remove(StringUtils.trim(color), '"')) + .toArray(String[]::new); + } + @Override public void enable() throws EnableException { try { @@ -78,13 +87,7 @@ public class Theme implements SubSystem { private String getColor(ThemeVal variable) { String path = variable.getThemePath(); try { - String value = themeConfig.getString(path); - - if (value.contains(".")) { - return "url(\"" + value + "\")"; - } else { - return value; - } + return themeConfig.getString(path); } catch (Exception | NoSuchFieldError e) { logger.error("Something went wrong with getting variable " + variable.name() + " for: " + path); } @@ -92,33 +95,31 @@ public class Theme implements SubSystem { } public String replaceThemeColors(String resourceString) { - String replaced = resourceString; - ThemeVal[] themeVariables = new ThemeVal[]{ + return replaceVariables(resourceString, RED, PINK, PURPLE, DEEP_PURPLE, INDIGO, BLUE, LIGHT_BLUE, CYAN, TEAL, GREEN, LIGHT_GREEN, LIME, YELLOW, AMBER, ORANGE, DEEP_ORANGE, BROWN, GREY, BLUE_GREY, BLACK, WHITE, GRAPH_PUNCHCARD, GRAPH_PLAYERS_ONLINE, GRAPH_TPS_HIGH, GRAPH_TPS_MED, GRAPH_TPS_LOW, - GRAPH_CPU, GRAPH_RAM, GRAPH_CHUNKS, GRAPH_ENTITIES, GRAPH_WORLD_PIE, GRAPH_GM_PIE, - GRAPH_ACTIVITY_PIE, GRAPH_SERVER_PREF_PIE, FONT_STYLESHEET, FONT_FAMILY - }; + GRAPH_CPU, GRAPH_RAM, GRAPH_CHUNKS, GRAPH_ENTITIES, GRAPH_WORLD_PIE, FONT_STYLESHEET, FONT_FAMILY + ); + } + + private String replaceVariables(String resourceString, ThemeVal... themeVariables) { + List replace = new ArrayList<>(); + List with = new ArrayList<>(); for (ThemeVal variable : themeVariables) { String value = getColor(variable); String defaultValue = variable.getDefaultValue(); - if (Verify.equalsOne(value, defaultValue)) { + if (defaultValue.equals(value)) { continue; } - if (value.contains("url")) { - String[] colorAndUrl = value.split(" "); - if (colorAndUrl.length >= 2) { - replaced = replaced.replace("background: " + defaultValue, "background: " + colorAndUrl[1]); - replaced = replaced.replace(defaultValue, colorAndUrl[0]); - return replaced; - } - } else { - replaced = replaced.replace(defaultValue, value); - } + replace.add(defaultValue); + with.add(value); } - return replaced.replace("${defaultTheme}", getValue(ThemeVal.THEME_DEFAULT)); + replace.add("${defaultTheme}"); + with.add(getValue(ThemeVal.THEME_DEFAULT)); + + return StringUtils.replaceEach(resourceString, replace.toArray(new String[0]), with.toArray(new String[0])); } private String getThemeValue(ThemeVal color) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/ThemeConfig.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/theme/ThemeConfig.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/ThemeConfig.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/theme/ThemeConfig.java index 0b7505e27..2a3207864 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/ThemeConfig.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/theme/ThemeConfig.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.theme; +package com.djrapitops.plan.settings.theme; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.settings.config.Config; -import com.djrapitops.plan.system.settings.config.ConfigNode; -import com.djrapitops.plan.system.settings.config.ConfigReader; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DisplaySettings; +import com.djrapitops.plan.settings.config.Config; +import com.djrapitops.plan.settings.config.ConfigNode; +import com.djrapitops.plan.settings.config.ConfigReader; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DisplaySettings; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.console.PluginLogger; import java.io.File; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/ThemeVal.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/theme/ThemeVal.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/ThemeVal.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/theme/ThemeVal.java index bb9f15276..faecc7ed2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/theme/ThemeVal.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/theme/ThemeVal.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.settings.theme; +package com.djrapitops.plan.settings.theme; /** * Enum class used for getting the Html colors that match the config settings. @@ -23,10 +23,10 @@ package com.djrapitops.plan.system.settings.theme; */ public enum ThemeVal { - THEME_DEFAULT("DefaultColor", "light-green"), + THEME_DEFAULT("DefaultColor", "plan"), - FONT_STYLESHEET("Font.FontStyleSheet", "https://fonts.googleapis.com/css?family=Roboto:400,700&subset=latin,cyrillic-ext"), - FONT_FAMILY("Font.FontFamily", "\"Roboto\", sans-serif"), + FONT_STYLESHEET("Font.FontStyleSheet", "https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext"), + FONT_FAMILY("Font.FontFamily", "\"Nunito\""), RED("Colors.red", "#E91E63"), PINK("Colors.pink", "#F44336"), diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/ConfigStoreTask.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/ConfigStoreTask.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/ConfigStoreTask.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/ConfigStoreTask.java index 06bc29db7..b3876d89a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/ConfigStoreTask.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/ConfigStoreTask.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks.server; +package com.djrapitops.plan.settings.upkeep; -import com.djrapitops.plan.db.access.transactions.StoreConfigTransaction; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.transactions.StoreConfigTransaction; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.task.AbsRunnable; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/FileWatcher.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/FileWatcher.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/file/FileWatcher.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/FileWatcher.java index db449a599..94f78bda8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/FileWatcher.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/FileWatcher.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.file; +package com.djrapitops.plan.settings.upkeep; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/proxy/NetworkConfigStoreTask.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/NetworkConfigStoreTask.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/system/tasks/proxy/NetworkConfigStoreTask.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/NetworkConfigStoreTask.java index 664871c99..30aa6dafc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/proxy/NetworkConfigStoreTask.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/NetworkConfigStoreTask.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks.proxy; +package com.djrapitops.plan.settings.upkeep; -import com.djrapitops.plan.system.settings.network.NetworkSettingManager; +import com.djrapitops.plan.settings.network.NetworkSettingManager; import com.djrapitops.plugin.task.AbsRunnable; import javax.inject.Inject; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/WatchedFile.java b/Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/WatchedFile.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/file/WatchedFile.java rename to Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/WatchedFile.java index 208a81580..a5f392a0a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/file/WatchedFile.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/settings/upkeep/WatchedFile.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.file; +package com.djrapitops.plan.settings.upkeep; import com.djrapitops.plan.utilities.java.VoidFunction; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/AbstractDatabase.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/AbstractDatabase.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/db/AbstractDatabase.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/AbstractDatabase.java index 1bbd6086f..bade58419 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/AbstractDatabase.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/AbstractDatabase.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db; +package com.djrapitops.plan.storage.database; /** * Abstract class representing a Database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/DBAccessLock.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBAccessLock.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/DBAccessLock.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBAccessLock.java index 2471e954b..7c57f84a8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/DBAccessLock.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBAccessLock.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.access.transactions.init.OperationCriticalTransaction; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.transactions.Transaction; +import com.djrapitops.plan.storage.database.transactions.init.OperationCriticalTransaction; /** * Database Lock that prevents queries and transactions from taking place before database schema is ready. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/database/DBSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBSystem.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/system/database/DBSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBSystem.java index e180e0061..07f93fe39 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/database/DBSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBSystem.java @@ -14,17 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.database; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.H2DB; -import com.djrapitops.plan.db.SQLiteDB; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plugin.benchmarking.Timings; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/DBType.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBType.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/db/DBType.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBType.java index ba3ec6558..547ba58fa 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/DBType.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/DBType.java @@ -14,7 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db; +package com.djrapitops.plan.storage.database; + +import com.djrapitops.plan.storage.database.sql.parsing.Sql; import java.util.Optional; @@ -25,18 +27,20 @@ import java.util.Optional; */ public enum DBType { - MYSQL("MySQL", true), - SQLITE("SQLite", false), - H2("H2", true); + MYSQL("MySQL", true, new Sql.MySQL()), + SQLITE("SQLite", false, new Sql.SQLite()), + H2("H2", true, new Sql.H2()); private final String name; private final String configName; private final boolean supportingMySQLQueries; + private final Sql sql; - DBType(String name, boolean supportingMySQLQueries) { + DBType(String name, boolean supportingMySQLQueries, Sql sql) { this.name = name; this.configName = name.toLowerCase().trim(); this.supportingMySQLQueries = supportingMySQLQueries; + this.sql = sql; } /** @@ -101,4 +105,8 @@ public enum DBType { return false; } + + public Sql getSql() { + return sql; + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/Database.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/Database.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/db/Database.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/Database.java index b0fb36f7e..cd79df8fd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/Database.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/Database.java @@ -14,11 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.concurrent.Future; @@ -67,6 +68,10 @@ public interface Database { */ DBType getType(); + default Sql getSql() { + return getType().getSql(); + } + State getState(); enum State { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/H2DB.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/H2DB.java index b5f08b0e9..9b34edc3a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/H2DB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/H2DB.java @@ -14,16 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.db.tasks.KeepAliveTask; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DatabaseSettings; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DatabaseSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.storage.upkeep.DBKeepAliveTask; import com.djrapitops.plan.utilities.MiscUtils; import com.djrapitops.plan.utilities.java.ThrowableUtils; import com.djrapitops.plugin.logging.console.PluginLogger; @@ -57,12 +56,11 @@ public class H2DB extends SQLDB { Locale locale, PlanConfig config, Lazy serverInfo, - NetworkContainer.Factory networkContainerFactory, RunnableFactory runnableFactory, PluginLogger logger, ErrorHandler errorHandler ) { - super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, errorHandler); + super(() -> serverInfo.get().getServerUUID(), locale, config, runnableFactory, logger, errorHandler); dbName = databaseFile.getName(); this.databaseFile = databaseFile; } @@ -104,7 +102,7 @@ public class H2DB extends SQLDB { try { // Maintains Connection. connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getType().getName(), - new KeepAliveTask(connection, () -> getNewConnection(databaseFile), logger, errorHandler) + new DBKeepAliveTask(connection, () -> getNewConnection(databaseFile), logger, errorHandler) ).runTaskTimerAsynchronously(60L * 20L, 60L * 20L); } catch (Exception ignored) { // Task failed to register because plugin is being disabled @@ -173,7 +171,6 @@ public class H2DB extends SQLDB { private final Locale locale; private final PlanConfig config; private final Lazy serverInfo; - private final NetworkContainer.Factory networkContainerFactory; private final RunnableFactory runnableFactory; private final PluginLogger logger; private final ErrorHandler errorHandler; @@ -185,7 +182,6 @@ public class H2DB extends SQLDB { PlanConfig config, PlanFiles files, Lazy serverInfo, - NetworkContainer.Factory networkContainerFactory, RunnableFactory runnableFactory, PluginLogger logger, ErrorHandler errorHandler @@ -194,7 +190,6 @@ public class H2DB extends SQLDB { this.config = config; this.files = files; this.serverInfo = serverInfo; - this.networkContainerFactory = networkContainerFactory; this.runnableFactory = runnableFactory; this.logger = logger; this.errorHandler = errorHandler; @@ -211,7 +206,6 @@ public class H2DB extends SQLDB { public H2DB usingFile(File databaseFile) { return new H2DB(databaseFile, locale, config, serverInfo, - networkContainerFactory, runnableFactory, logger, errorHandler ); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/MySQLDB.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/MySQLDB.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/MySQLDB.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/MySQLDB.java index 6d6d17474..b057ec872 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/MySQLDB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/MySQLDB.java @@ -14,16 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DatabaseSettings; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.DatabaseSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.benchmarking.Timings; import com.djrapitops.plugin.logging.L; @@ -40,6 +39,7 @@ import javax.inject.Singleton; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; +import java.sql.Statement; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -58,13 +58,12 @@ public class MySQLDB extends SQLDB { Locale locale, PlanConfig config, Lazy serverInfo, - NetworkContainer.Factory networkContainerFactory, RunnableFactory runnableFactory, PluginLogger pluginLogger, Timings timings, ErrorHandler errorHandler ) { - super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, pluginLogger, errorHandler); + super(() -> serverInfo.get().getServerUUID(), locale, config, runnableFactory, pluginLogger, errorHandler); } private static synchronized void increment() { @@ -142,9 +141,16 @@ public class MySQLDB extends SQLDB { } } if (connection.getAutoCommit()) connection.setAutoCommit(false); + setTimezoneToUTC(connection); return connection; } + private void setTimezoneToUTC(Connection connection) throws SQLException { + try (Statement statement = connection.createStatement()) { + statement.execute("set time_zone = '+00:00'"); + } + } + @Override public void close() { super.close(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/database/ProxyDBSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/ProxyDBSystem.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/database/ProxyDBSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/ProxyDBSystem.java index c1655dce2..b1b978204 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/database/ProxyDBSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/ProxyDBSystem.java @@ -14,12 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.database; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.db.H2DB; -import com.djrapitops.plan.db.MySQLDB; -import com.djrapitops.plan.db.SQLiteDB; -import com.djrapitops.plan.system.locale.Locale; +import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plugin.benchmarking.Timings; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java index f5cde6cf9..fd56194a9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/SQLDB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLDB.java @@ -14,23 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.api.exceptions.database.FatalDBException; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.access.transactions.init.CreateIndexTransaction; -import com.djrapitops.plan.db.access.transactions.init.CreateTablesTransaction; -import com.djrapitops.plan.db.access.transactions.init.OperationCriticalTransaction; -import com.djrapitops.plan.db.patches.*; -import com.djrapitops.plan.system.DebugChannels; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.DebugChannels; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.exceptions.database.FatalDBException; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.transactions.Transaction; +import com.djrapitops.plan.storage.database.transactions.init.CreateIndexTransaction; +import com.djrapitops.plan.storage.database.transactions.init.CreateTablesTransaction; +import com.djrapitops.plan.storage.database.transactions.init.OperationCriticalTransaction; +import com.djrapitops.plan.storage.database.transactions.patches.*; import com.djrapitops.plan.utilities.java.ThrowableUtils; import com.djrapitops.plugin.api.TimeAmount; import com.djrapitops.plugin.logging.L; @@ -38,7 +37,7 @@ import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; import com.djrapitops.plugin.task.AbsRunnable; import com.djrapitops.plugin.task.RunnableFactory; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; import java.sql.Connection; import java.sql.SQLException; @@ -61,7 +60,6 @@ public abstract class SQLDB extends AbstractDatabase { protected final Locale locale; protected final PlanConfig config; - protected final NetworkContainer.Factory networkContainerFactory; protected final RunnableFactory runnableFactory; protected final PluginLogger logger; protected final ErrorHandler errorHandler; @@ -75,21 +73,29 @@ public abstract class SQLDB extends AbstractDatabase { Supplier serverUUIDSupplier, Locale locale, PlanConfig config, - NetworkContainer.Factory networkContainerFactory, RunnableFactory runnableFactory, + RunnableFactory runnableFactory, PluginLogger logger, ErrorHandler errorHandler ) { this.serverUUIDSupplier = serverUUIDSupplier; this.locale = locale; this.config = config; - this.networkContainerFactory = networkContainerFactory; this.runnableFactory = runnableFactory; this.logger = logger; this.errorHandler = errorHandler; devMode = config.get(PluginSettings.DEV_MODE); - this.transactionExecutorServiceProvider = () -> Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Plan " + getClass().getSimpleName() + "-transaction-thread-%d").build()); + this.transactionExecutorServiceProvider = () -> { + String nameFormat = "Plan " + getClass().getSimpleName() + "-transaction-thread-%d"; + return Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder() + .namingPattern(nameFormat) + .uncaughtExceptionHandler((thread, throwable) -> { + if (config.get(PluginSettings.DEV_MODE)) { + errorHandler.log(L.WARN, getClass(), throwable); + } + }).build()); + }; } @Override @@ -158,10 +164,11 @@ public abstract class SQLDB extends AbstractDatabase { new UserInfoOptimizationPatch(), new GeoInfoOptimizationPatch(), new TransferTableRemovalPatch(), - new IPAnonPatch(), new BadAFKThresholdValuePatch(), - new DeleteIPHashesPatch(), - new ExtensionShowInPlayersTablePatch() + new DeleteIPsPatch(), + new ExtensionShowInPlayersTablePatch(), + new ExtensionTableRowValueLengthPatch(), + new CommandUsageTableRemovalPatch() }; } @@ -277,10 +284,6 @@ public abstract class SQLDB extends AbstractDatabase { return serverUUIDSupplier; } - public NetworkContainer.Factory getNetworkContainerFactory() { - return networkContainerFactory; - } - public void setTransactionExecutorServiceProvider(Supplier transactionExecutorServiceProvider) { this.transactionExecutorServiceProvider = transactionExecutorServiceProvider; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/SQLiteDB.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/db/SQLiteDB.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java index 2c4f7d4f5..edcfbaecb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/SQLiteDB.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/SQLiteDB.java @@ -14,16 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db; +package com.djrapitops.plan.storage.database; -import com.djrapitops.plan.api.exceptions.database.DBInitException; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.db.tasks.KeepAliveTask; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.exceptions.database.DBInitException; +import com.djrapitops.plan.identification.ServerInfo; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; +import com.djrapitops.plan.storage.file.PlanFiles; +import com.djrapitops.plan.storage.upkeep.DBKeepAliveTask; import com.djrapitops.plan.utilities.MiscUtils; import com.djrapitops.plan.utilities.java.ThrowableUtils; import com.djrapitops.plugin.logging.L; @@ -56,12 +55,11 @@ public class SQLiteDB extends SQLDB { Locale locale, PlanConfig config, Lazy serverInfo, - NetworkContainer.Factory networkContainerFactory, RunnableFactory runnableFactory, PluginLogger logger, ErrorHandler errorHandler ) { - super(() -> serverInfo.get().getServerUUID(), locale, config, networkContainerFactory, runnableFactory, logger, errorHandler); + super(() -> serverInfo.get().getServerUUID(), locale, config, runnableFactory, logger, errorHandler); dbName = databaseFile.getName(); this.databaseFile = databaseFile; } @@ -108,7 +106,7 @@ public class SQLiteDB extends SQLDB { try { // Maintains Connection. connectionPingTask = runnableFactory.create("DBConnectionPingTask " + getType().getName(), - new KeepAliveTask(connection, () -> getNewConnection(databaseFile), logger, errorHandler) + new DBKeepAliveTask(connection, () -> getNewConnection(databaseFile), logger, errorHandler) ).runTaskTimerAsynchronously(60L * 20L, 60L * 20L); } catch (Exception ignored) { // Task failed to register because plugin is being disabled @@ -177,7 +175,6 @@ public class SQLiteDB extends SQLDB { private final Locale locale; private final PlanConfig config; private final Lazy serverInfo; - private final NetworkContainer.Factory networkContainerFactory; private final RunnableFactory runnableFactory; private final PluginLogger logger; private final ErrorHandler errorHandler; @@ -189,7 +186,6 @@ public class SQLiteDB extends SQLDB { PlanConfig config, PlanFiles files, Lazy serverInfo, - NetworkContainer.Factory networkContainerFactory, RunnableFactory runnableFactory, PluginLogger logger, ErrorHandler errorHandler @@ -198,7 +194,6 @@ public class SQLiteDB extends SQLDB { this.config = config; this.files = files; this.serverInfo = serverInfo; - this.networkContainerFactory = networkContainerFactory; this.runnableFactory = runnableFactory; this.logger = logger; this.errorHandler = errorHandler; @@ -215,7 +210,6 @@ public class SQLiteDB extends SQLDB { public SQLiteDB usingFile(File databaseFile) { return new SQLiteDB(databaseFile, locale, config, serverInfo, - networkContainerFactory, runnableFactory, logger, errorHandler ); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/DataStoreQueries.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/DataStoreQueries.java index abb47f70d..5cbcb7e6b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/DataStoreQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/DataStoreQueries.java @@ -14,26 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.data.time.GMTimes; -import com.djrapitops.plan.db.access.ExecBatchStatement; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; import com.djrapitops.plugin.utilities.Verify; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; /** * Static method class for single item store queries. @@ -46,48 +42,11 @@ public class DataStoreQueries { /* static method class */ } - /** - * Store the used command in the database. - * - * @param serverUUID UUID of the Plan server. - * @param commandName Name of the command that was used. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} - */ - public static Executable storeUsedCommandInformation(UUID serverUUID, String commandName) { - return connection -> { - if (!updateCommandUsage(serverUUID, commandName).execute(connection)) { - insertNewCommandUsage(serverUUID, commandName).execute(connection); - } - return false; - }; - } - - private static Executable updateCommandUsage(UUID serverUUID, String commandName) { - return new ExecStatement(CommandUseTable.UPDATE_STATEMENT) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, serverUUID.toString()); - statement.setString(2, commandName); - } - }; - } - - private static Executable insertNewCommandUsage(UUID serverUUID, String commandName) { - return new ExecStatement(CommandUseTable.INSERT_STATEMENT) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, commandName); - statement.setInt(2, 1); - statement.setString(3, serverUUID.toString()); - } - }; - } - /** * Store a finished session in the database. * * @param session Session, of which {@link Session#endSession(long)} has been called. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} * @throws IllegalArgumentException If {@link Session#endSession(long)} has not yet been called. */ public static Executable storeSession(Session session) { @@ -150,7 +109,7 @@ public class DataStoreQueries { * * @param playerUUID UUID of the player. * @param geoInfo GeoInfo of the player. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeGeoInfo(UUID playerUUID, GeoInfo geoInfo) { return connection -> { @@ -177,9 +136,8 @@ public class DataStoreQueries { @Override public void prepare(PreparedStatement statement) throws SQLException { statement.setString(1, playerUUID.toString()); - statement.setString(2, geoInfo.getIp()); - statement.setString(3, geoInfo.getGeolocation()); - statement.setLong(4, geoInfo.getDate()); + statement.setString(2, geoInfo.getGeolocation()); + statement.setLong(3, geoInfo.getDate()); } }; } @@ -190,7 +148,7 @@ public class DataStoreQueries { * @param playerUUID UUID of the player. * @param registered Time the player registered on the server for the first time. * @param playerName Name of the player. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable registerBaseUser(UUID playerUUID, long registered, String playerName) { return new ExecStatement(UsersTable.INSERT_STATEMENT) { @@ -209,7 +167,7 @@ public class DataStoreQueries { * * @param playerUUID UUID of the player. * @param playerName Name of the player. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable updatePlayerName(UUID playerUUID, String playerName) { String sql = "UPDATE " + UsersTable.TABLE_NAME + " SET " + UsersTable.USER_NAME + "=?" + @@ -229,7 +187,7 @@ public class DataStoreQueries { * @param playerUUID UUID of the player. * @param registered Time the player registered on the server. * @param serverUUID UUID of the Plan server. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable registerUserInfo(UUID playerUUID, long registered, UUID serverUUID) { return new ExecStatement(UserInfoTable.INSERT_STATEMENT) { @@ -250,7 +208,7 @@ public class DataStoreQueries { * @param playerUUID UUID of the player. * @param serverUUID UUID of the Plan server. * @param ping Ping data entry - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storePing(UUID playerUUID, UUID serverUUID, Ping ping) { return new ExecStatement(PingTable.INSERT_STATEMENT) { @@ -271,7 +229,7 @@ public class DataStoreQueries { * * @param serverUUID UUID of the Plan server. * @param tps TPS data entry - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeTPS(UUID serverUUID, TPS tps) { return new ExecStatement(TPSTable.INSERT_STATEMENT) { @@ -295,7 +253,7 @@ public class DataStoreQueries { * * @param playerUUID UUID of the player. * @param nickname Nickname information. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storePlayerNickname(UUID playerUUID, Nickname nickname) { return connection -> { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/HasMoreThanZeroQueryStatement.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/HasMoreThanZeroQueryStatement.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/HasMoreThanZeroQueryStatement.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/HasMoreThanZeroQueryStatement.java index 7115ba6dd..fd9077dec 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/HasMoreThanZeroQueryStatement.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/HasMoreThanZeroQueryStatement.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.queries; import java.sql.ResultSet; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeFetchQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeFetchQueries.java similarity index 66% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeFetchQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeFetchQueries.java index 4a89de2a7..4608d4485 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeFetchQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeFetchQueries.java @@ -14,22 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.builders.TPSBuilder; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.sql.tables.CommandUseTable; -import com.djrapitops.plan.db.sql.tables.ServerTable; -import com.djrapitops.plan.db.sql.tables.TPSTable; -import com.djrapitops.plan.db.sql.tables.WorldTable; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.gathering.domain.builders.TPSBuilder; +import com.djrapitops.plan.storage.database.sql.tables.ServerTable; +import com.djrapitops.plan.storage.database.sql.tables.TPSTable; +import com.djrapitops.plan.storage.database.sql.tables.WorldTable; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Static method class for queries that use large amount of memory. @@ -42,41 +39,6 @@ public class LargeFetchQueries { /* Static method class */ } - /** - * Query database for all command usage data. - * - * @return Multi map: Server UUID - (Command name - Usage count) - */ - public static Query>> fetchAllCommandUsageData() { - String serverIDColumn = ServerTable.TABLE_NAME + '.' + ServerTable.SERVER_ID; - String serverUUIDColumn = ServerTable.TABLE_NAME + '.' + ServerTable.SERVER_UUID + " as s_uuid"; - String sql = SELECT + - CommandUseTable.COMMAND + ',' + - CommandUseTable.TIMES_USED + ',' + - serverUUIDColumn + - FROM + CommandUseTable.TABLE_NAME + - INNER_JOIN + ServerTable.TABLE_NAME + " on " + serverIDColumn + "=" + CommandUseTable.SERVER_ID; - - return new QueryAllStatement>>(sql, 10000) { - @Override - public Map> processResults(ResultSet set) throws SQLException { - Map> map = new HashMap<>(); - while (set.next()) { - UUID serverUUID = UUID.fromString(set.getString("s_uuid")); - - Map serverMap = map.getOrDefault(serverUUID, new HashMap<>()); - - String command = set.getString(CommandUseTable.COMMAND); - int timesUsed = set.getInt(CommandUseTable.TIMES_USED); - - serverMap.put(command, timesUsed); - map.put(serverUUID, serverMap); - } - return map; - } - }; - } - /** * Query database for TPS data. * diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java index 6014fc86e..d8fff9524 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/LargeStoreQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/LargeStoreQueries.java @@ -14,17 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.data.container.*; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.data.time.GMTimes; -import com.djrapitops.plan.db.access.ExecBatchStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.sql.tables.*; -import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; import com.djrapitops.plugin.utilities.Verify; import java.sql.PreparedStatement; @@ -45,43 +44,11 @@ public class LargeStoreQueries { /* Static method class */ } - /** - * Execute a big batch of command use insert statements. - * - * @param ofServers Multi map: Server UUID - (Command name - Usage count) - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} - */ - public static Executable storeAllCommandUsageData(Map> ofServers) { - if (ofServers.isEmpty()) { - return Executable.empty(); - } - - return new ExecBatchStatement(CommandUseTable.INSERT_STATEMENT) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - // Every Server - for (Map.Entry> serverEntry : ofServers.entrySet()) { - UUID serverUUID = serverEntry.getKey(); - // Every Command - for (Map.Entry entry : serverEntry.getValue().entrySet()) { - String command = entry.getKey(); - int timesUsed = entry.getValue(); - - statement.setString(1, command); - statement.setInt(2, timesUsed); - statement.setString(3, serverUUID.toString()); - statement.addBatch(); - } - } - } - }; - } - /** * Execute a big batch of GeoInfo insert statements. * * @param ofUsers Map: Player UUID - List of GeoInfo - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeAllGeoInformation(Map> ofUsers) { if (Verify.isEmpty(ofUsers)) { @@ -96,14 +63,12 @@ public class LargeStoreQueries { UUID playerUUID = playerEntry.getKey(); // Every GeoInfo for (GeoInfo info : playerEntry.getValue()) { - String ip = info.getIp(); String geoLocation = info.getGeolocation(); long lastUsed = info.getDate(); statement.setString(1, playerUUID.toString()); - statement.setString(2, ip); - statement.setString(3, geoLocation); - statement.setLong(4, lastUsed); + statement.setString(2, geoLocation); + statement.setLong(3, lastUsed); statement.addBatch(); } @@ -116,7 +81,7 @@ public class LargeStoreQueries { * Execute a big batch of nickname insert statements. * * @param ofServersAndUsers Multimap: Server UUID - (Player UUID - List of nicknames) - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeAllNicknameData(Map>> ofServersAndUsers) { if (Verify.isEmpty(ofServersAndUsers)) { @@ -151,7 +116,7 @@ public class LargeStoreQueries { * Execute a big batch of web user insert statements. * * @param users Collection of Plan WebUsers. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeAllPlanWebUsers(Collection users) { if (Verify.isEmpty(users)) { @@ -179,7 +144,7 @@ public class LargeStoreQueries { * Execute a big batch of server infromation insert statements. * * @param servers Collection of Plan Servers. - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeAllPlanServerInformation(Collection servers) { if (Verify.isEmpty(servers)) { @@ -213,7 +178,7 @@ public class LargeStoreQueries { * Execute a big batch of TPS insert statements. * * @param ofServers Map: Server UUID - List of TPS data - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeAllTPSData(Map> ofServers) { if (Verify.isEmpty(ofServers)) { @@ -249,7 +214,7 @@ public class LargeStoreQueries { * Execute a big batch of Per server UserInfo insert statements. * * @param ofServers Map: Server UUID - List of user information - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storePerServerUserInformation(Map> ofServers) { if (Verify.isEmpty(ofServers)) { @@ -280,7 +245,7 @@ public class LargeStoreQueries { * Execute a big batch of world name insert statements. * * @param ofServers Map: Server UUID - Collection of world names - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeAllWorldNames(Map> ofServers) { if (Verify.isEmpty(ofServers)) { @@ -306,7 +271,7 @@ public class LargeStoreQueries { * Execute a big batch of user information insert statements. * * @param ofUsers Collection of BaseUsers - * @return Executable, use inside a {@link com.djrapitops.plan.db.access.transactions.Transaction} + * @return Executable, use inside a {@link com.djrapitops.plan.storage.database.transactions.Transaction} */ public static Executable storeAllCommonUserInformation(Collection ofUsers) { if (Verify.isEmpty(ofUsers)) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/PerServerAggregateQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/PerServerAggregateQueries.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/PerServerAggregateQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/PerServerAggregateQueries.java index 796e2dbe0..25231e86e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/PerServerAggregateQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/PerServerAggregateQueries.java @@ -14,12 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.KillsTable; -import com.djrapitops.plan.db.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.KillsTable; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -28,7 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Static method class for queries that count together counts for a player on a per server basis. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/PlayerFetchQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/PlayerFetchQueries.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/PlayerFetchQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/PlayerFetchQueries.java index f5d602031..496a6a1b7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/PlayerFetchQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/PlayerFetchQueries.java @@ -14,13 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; -import com.djrapitops.plan.db.sql.tables.UsersTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -28,7 +25,7 @@ import java.sql.SQLException; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Static method class for queries that return information related to a single player. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/Query.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/Query.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/Query.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/Query.java index 321c9166a..2639972f1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/Query.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/Query.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.db.SQLDB; +import com.djrapitops.plan.storage.database.SQLDB; /** * Interface for everything that returns results from the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAPIExecutable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAPIExecutable.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAPIExecutable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAPIExecutable.java index 1e86f3d89..32ac19577 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAPIExecutable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAPIExecutable.java @@ -14,10 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.api.exceptions.database.DBOpException; +import com.djrapitops.plan.exceptions.database.DBOpException; import com.djrapitops.plan.query.QueryService; +import com.djrapitops.plan.storage.database.transactions.Executable; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAPIQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAPIQuery.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAPIQuery.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAPIQuery.java index bb882a672..8276278f9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAPIQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAPIQuery.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.SQLDB; +import com.djrapitops.plan.exceptions.database.DBOpException; import com.djrapitops.plan.query.QueryService; +import com.djrapitops.plan.storage.database.SQLDB; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAllStatement.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAllStatement.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAllStatement.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAllStatement.java index 9c2e935de..c0d639d18 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryAllStatement.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryAllStatement.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.queries; import java.sql.PreparedStatement; import java.sql.ResultSet; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryStatement.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryStatement.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryStatement.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryStatement.java index 24082935e..21489ef90 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/QueryStatement.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/QueryStatement.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.SQLDB; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.SQLDB; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/ServerAggregateQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/ServerAggregateQueries.java similarity index 55% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/ServerAggregateQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/ServerAggregateQueries.java index 0a88369ce..240bec5f5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/ServerAggregateQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/ServerAggregateQueries.java @@ -14,12 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries; +package com.djrapitops.plan.storage.database.queries; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -28,7 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Static method class for queries that count how many entries of particular kinds there are for a server. @@ -103,61 +101,4 @@ public class ServerAggregateQueries { }; } - /** - * Count how many times commands have been used on a server. - * - * @param serverUUID Server UUID of the Plan server. - * @return Map: Lowercase used command - Count of use times. - */ - public static Query> commandUsageCounts(UUID serverUUID) { - String sql = SELECT + CommandUseTable.COMMAND + ',' + CommandUseTable.TIMES_USED + FROM + CommandUseTable.TABLE_NAME + - WHERE + CommandUseTable.SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID; - - return new QueryStatement>(sql, 5000) { - @Override - public void prepare(PreparedStatement statement) throws SQLException { - statement.setString(1, serverUUID.toString()); - } - - @Override - public Map processResults(ResultSet set) throws SQLException { - Map commandUse = new HashMap<>(); - while (set.next()) { - String cmd = set.getString(CommandUseTable.COMMAND).toLowerCase(); - int amountUsed = set.getInt(CommandUseTable.TIMES_USED); - commandUse.put(cmd, amountUsed); - } - return commandUse; - } - }; - } - - public static Query> networkGeolocationCounts() { - String subQuery1 = SELECT + - GeoInfoTable.USER_UUID + ',' + - GeoInfoTable.GEOLOCATION + ',' + - GeoInfoTable.LAST_USED + - FROM + GeoInfoTable.TABLE_NAME; - String subQuery2 = SELECT + - GeoInfoTable.USER_UUID + ',' + - "MAX(" + GeoInfoTable.LAST_USED + ") as m" + - FROM + GeoInfoTable.TABLE_NAME + - GROUP_BY + GeoInfoTable.USER_UUID; - String sql = SELECT + GeoInfoTable.GEOLOCATION + ", COUNT(1) as c FROM (" + - '(' + subQuery1 + ") AS q1" + - " INNER JOIN (" + subQuery2 + ") AS q2 ON q1.uuid = q2.uuid)" + - WHERE + GeoInfoTable.LAST_USED + "=m" + - GROUP_BY + GeoInfoTable.GEOLOCATION; - - return new QueryAllStatement>(sql) { - @Override - public Map processResults(ResultSet set) throws SQLException { - Map geolocationCounts = new HashMap<>(); - while (set.next()) { - geolocationCounts.put(set.getString(GeoInfoTable.GEOLOCATION), set.getInt("c")); - } - return geolocationCounts; - } - }; - } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/ActivityIndexQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/ActivityIndexQueries.java new file mode 100644 index 000000000..60c27570d --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/ActivityIndexQueries.java @@ -0,0 +1,505 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.analysis; + +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Queries for Activity Index that attempts to gain insight into player activity levels. + *

    + * Old formula for activity index was not linear and difficult to turn into a query due to conditional multipliers. + * Thus a new formula was written. + *

    + * {@code T} - Time played after someone is considered active on a particular week + * {@code t1, t2, t3} - Time played that week + *

    + * Activity index takes into account last 3 weeks. + *

    + * Activity for a single week is calculated using {@code A(t) = (1 / (pi/2 * (t/T) + 1))}. + * A(t) is based on function f(x) = 1 / (x + 1), which has property f(0) = 1, decreasing from there, but not in a straight line. + * You can see the function plotted here https://www.wolframalpha.com/input/?i=1+%2F+(x%2B1)+from+-1+to+2 + *

    + * To fine tune the curve pi/2 is used since it felt like a good curve. + *

    + * Activity index A is calculated by using the formula: + * {@code A = 5 - 5 * [A(t1) + A(t2) + A(t3)] / 3} + *

    + * Plot for A and limits + * https://www.wolframalpha.com/input/?i=plot+y+%3D+5+-+5+*+(1+%2F+(pi%2F2+*+x%2B1))+and+y+%3D1+and+y+%3D+2+and+y+%3D+3+and+y+%3D+3.75+from+-0.5+to+3 + *

    + * New Limits for A would thus be + * {@code < 1: Inactive} + * {@code > 1: Irregular} + * {@code > 2: Regular} + * {@code > 3: Active} + * {@code > 3.75: Very Active} + * + * @author Rsl1122 + */ +public class ActivityIndexQueries { + + private ActivityIndexQueries() { + // Static method class + } + + public static Query fetchRegularPlayerCount(long date, UUID serverUUID, long playtimeThreshold) { + return fetchActivityGroupCount(date, serverUUID, playtimeThreshold, ActivityIndex.REGULAR, 5.1); + } + + public static String selectActivityIndexSQL() { + String selectActivePlaytimeSQL = SELECT + + SessionsTable.USER_UUID + + ",SUM(" + + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + '-' + SessionsTable.AFK_TIME + + ") as active_playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?" + + GROUP_BY + SessionsTable.USER_UUID; + + String selectThreeWeeks = selectActivePlaytimeSQL + UNION + selectActivePlaytimeSQL + UNION + selectActivePlaytimeSQL; + + return SELECT + + "5.0 - 5.0 * AVG(1 / (?/2 * (q1.active_playtime/?) +1)) as activity_index," + + "q1." + SessionsTable.USER_UUID + + FROM + '(' + selectThreeWeeks + ") q1" + + GROUP_BY + "q1." + SessionsTable.USER_UUID; + } + + public static void setSelectActivityIndexSQLParameters(PreparedStatement statement, int index, long playtimeThreshold, UUID serverUUID, long date) throws SQLException { + statement.setDouble(index, Math.PI); + statement.setLong(index + 1, playtimeThreshold); + + statement.setString(index + 2, serverUUID.toString()); + statement.setLong(index + 3, date - TimeUnit.DAYS.toMillis(7L)); + statement.setLong(index + 4, date); + statement.setString(index + 5, serverUUID.toString()); + statement.setLong(index + 6, date - TimeUnit.DAYS.toMillis(14L)); + statement.setLong(index + 7, date - TimeUnit.DAYS.toMillis(7L)); + statement.setString(index + 8, serverUUID.toString()); + statement.setLong(index + 9, date - TimeUnit.DAYS.toMillis(21L)); + statement.setLong(index + 10, date - TimeUnit.DAYS.toMillis(14L)); + } + + public static Query fetchActivityGroupCount(long date, UUID serverUUID, long playtimeThreshold, double above, double below) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectIndexes = SELECT + "COALESCE(activity_index, 0) as activity_index" + + FROM + UserInfoTable.TABLE_NAME + " u" + + LEFT_JOIN + '(' + selectActivityIndex + ") q2 on q2." + SessionsTable.USER_UUID + "=u." + UserInfoTable.USER_UUID + + WHERE + "u." + UserInfoTable.SERVER_UUID + "=?" + + AND + "u." + UserInfoTable.REGISTERED + "<=?"; + + String selectCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectIndexes + ") i" + + WHERE + "i.activity_index>=?" + + AND + "i.activity_index(selectCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, playtimeThreshold, serverUUID, date); + statement.setString(12, serverUUID.toString()); + statement.setLong(13, date); + statement.setDouble(14, above); + statement.setDouble(15, below); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + public static Query> fetchActivityIndexGroupingsOn(long date, UUID serverUUID, long threshold) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectIndexes = SELECT + "? as activity_group, activity_index" + + FROM + UserInfoTable.TABLE_NAME + " u" + + LEFT_JOIN + '(' + selectActivityIndex + ") s on s." + SessionsTable.USER_UUID + "=u." + UserInfoTable.USER_UUID + + WHERE + "u." + UserInfoTable.SERVER_UUID + "=?" + + AND + "u." + UserInfoTable.REGISTERED + "<=?"; + + String selectCount = SELECT + "activity_group, COUNT(1) as count" + FROM + + '(' + selectIndexes + ") indexes" + + WHERE + "COALESCE(indexes.activity_index,0)>=?" + + AND + "COALESCE(indexes.activity_index,0)>(selectMultipleCounts) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + set16ValuesForQuery(statement, 1, "Very Active", ActivityIndex.VERY_ACTIVE, 5.1); + set16ValuesForQuery(statement, 17, "Active", ActivityIndex.ACTIVE, ActivityIndex.VERY_ACTIVE); + set16ValuesForQuery(statement, 33, "Regular", ActivityIndex.REGULAR, ActivityIndex.ACTIVE); + set16ValuesForQuery(statement, 49, "Irregular", ActivityIndex.IRREGULAR, ActivityIndex.REGULAR); + set16ValuesForQuery(statement, 65, "Inactive", -1, ActivityIndex.IRREGULAR); + } + + private void set16ValuesForQuery( + PreparedStatement statement, int index, + String group, double above, double below + ) throws SQLException { + statement.setString(index, group); + setSelectActivityIndexSQLParameters(statement, index + 1, threshold, serverUUID, date); + statement.setString(index + 12, serverUUID.toString()); + statement.setLong(index + 13, date); + statement.setDouble(index + 14, above); + statement.setDouble(index + 15, below); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map groups = new HashMap<>(); + while (set.next()) { + groups.put(set.getString("activity_group"), set.getInt("count")); + } + return groups; + } + }; + } + + public static Query countNewPlayersTurnedRegular(long after, long before, UUID serverUUID, Long threshold) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectActivePlayerCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectActivityIndex + ") q2" + + INNER_JOIN + UserInfoTable.TABLE_NAME + " u on u." + UserInfoTable.USER_UUID + "=q2." + SessionsTable.USER_UUID + + WHERE + "u." + UserInfoTable.SERVER_UUID + "=?" + + AND + "u." + UserInfoTable.REGISTERED + ">=?" + + AND + "u." + UserInfoTable.REGISTERED + "<=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectActivePlayerCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, serverUUID, before); + statement.setString(12, serverUUID.toString()); + statement.setLong(13, after); + statement.setLong(14, before); + statement.setDouble(15, ActivityIndex.REGULAR); + statement.setDouble(16, 5.1); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + /** + * @param start Start of the tracking, those regular will be counted here. + * @param end End of the tracking, those inactive will be count here. + * @param serverUUID UUID of the server. + * @param threshold Playtime threshold + * @return Query how many players went from regular to inactive in a span of time. + */ + public static Query countRegularPlayersTurnedInactive(long start, long end, UUID serverUUID, Long threshold) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectActivePlayerCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectActivityIndex + ") q2" + + // Join two select activity index queries together to query Regular and Inactive players + INNER_JOIN + '(' + selectActivityIndex.replace("q1", "q3") + ") q4" + + " on q2." + SessionsTable.USER_UUID + "=q4." + SessionsTable.USER_UUID + + WHERE + "q2.activity_index>=?" + + AND + "q2.activity_index=?" + + AND + "q4.activity_index(selectActivePlayerCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, serverUUID, end); + setSelectActivityIndexSQLParameters(statement, 12, threshold, serverUUID, start); + statement.setDouble(23, ActivityIndex.REGULAR); + statement.setDouble(24, 5.1); + statement.setDouble(25, -0.1); + statement.setDouble(26, ActivityIndex.IRREGULAR); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + public static Query averagePlaytimePerRegularPlayer(long after, long before, UUID serverUUID, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectPlaytimePerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "SUM(p." + SessionsTable.SESSION_END + "-p." + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "p." + SessionsTable.SERVER_UUID + "=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, serverUUID, before); + statement.setLong(12, before); + statement.setLong(13, after); + statement.setString(14, serverUUID.toString()); + statement.setDouble(15, ActivityIndex.REGULAR); + statement.setDouble(16, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query averageSessionLengthPerRegularPlayer(long after, long before, UUID serverUUID, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectSessionLengthPerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "p." + SessionsTable.SESSION_END + "-p." + SessionsTable.SESSION_START + " as length" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "p." + SessionsTable.SERVER_UUID + "=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, serverUUID, before); + statement.setLong(12, before); + statement.setLong(13, after); + statement.setString(14, serverUUID.toString()); + statement.setDouble(15, ActivityIndex.REGULAR); + statement.setDouble(16, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query averageAFKPerRegularPlayer(long after, long before, UUID serverUUID, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectPlaytimePerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "SUM(p." + SessionsTable.AFK_TIME + ") as afk" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "p." + SessionsTable.SERVER_UUID + "=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, serverUUID, before); + statement.setLong(12, before); + statement.setLong(13, after); + statement.setString(14, serverUUID.toString()); + statement.setDouble(15, ActivityIndex.REGULAR); + statement.setDouble(16, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query> activityIndexForNewPlayers(long after, long before, UUID serverUUID, Long threshold) { + String selectNewUUIDs = SELECT + UserInfoTable.USER_UUID + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.SERVER_UUID + "=?"; + + String sql = SELECT + "activity_index" + + FROM + '(' + selectNewUUIDs + ") n" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID; + + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + statement.setString(3, serverUUID.toString()); + setSelectActivityIndexSQLParameters(statement, 4, threshold, serverUUID, before); + } + + @Override + public Collection processResults(ResultSet set) throws SQLException { + Collection indexes = new ArrayList<>(); + while (set.next()) { + indexes.add(new ActivityIndex(set.getDouble("activity_index"), before)); + } + return indexes; + } + }; + } + + public static Query averageActivityIndexForRetainedPlayers(long after, long before, UUID serverUUID, Long threshold) { + String selectNewUUIDs = SELECT + UserInfoTable.USER_UUID + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.SERVER_UUID + "=?"; + + String selectUniqueUUIDs = SELECT + "DISTINCT " + SessionsTable.USER_UUID + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SERVER_UUID + "=?"; + + String sql = SELECT + "AVG(activity_index) as average" + + FROM + '(' + selectNewUUIDs + ") n" + + INNER_JOIN + '(' + selectUniqueUUIDs + ") u on n." + SessionsTable.USER_UUID + "=u." + SessionsTable.USER_UUID + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + statement.setString(3, serverUUID.toString()); + + // Have played in the last half of the time frame + long half = before - (before - after) / 2; + statement.setLong(4, half); + statement.setLong(5, before); + statement.setString(6, serverUUID.toString()); + setSelectActivityIndexSQLParameters(statement, 7, threshold, serverUUID, before); + } + + @Override + public ActivityIndex processResults(ResultSet set) throws SQLException { + return set.next() ? new ActivityIndex(set.getDouble("average"), before) : new ActivityIndex(0.0, before); + } + }; + } + + public static Query averageActivityIndexForNonRetainedPlayers(long after, long before, UUID serverUUID, Long threshold) { + String selectNewUUIDs = SELECT + UserInfoTable.USER_UUID + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.SERVER_UUID + "=?"; + + String selectUniqueUUIDs = SELECT + "DISTINCT " + SessionsTable.USER_UUID + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SERVER_UUID + "=?"; + + String sql = SELECT + "AVG(activity_index) as average" + + FROM + '(' + selectNewUUIDs + ") n" + + LEFT_JOIN + '(' + selectUniqueUUIDs + ") u on n." + SessionsTable.USER_UUID + "=u." + SessionsTable.USER_UUID + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID + + WHERE + "n." + SessionsTable.USER_UUID + IS_NULL; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + statement.setString(3, serverUUID.toString()); + + // Have played in the last half of the time frame + long half = before - (before - after) / 2; + statement.setLong(4, half); + statement.setLong(5, before); + statement.setString(6, serverUUID.toString()); + setSelectActivityIndexSQLParameters(statement, 7, threshold, serverUUID, before); + } + + @Override + public ActivityIndex processResults(ResultSet set) throws SQLException { + return set.next() ? new ActivityIndex(set.getDouble("average"), before) : new ActivityIndex(0.0, before); + } + }; + } + + public static Query activityIndexOnServerToMap(UUID serverUUID, long date, long threshold, BiConsumer consumer) { + String sql = SELECT + "activity_index,n." + UserInfoTable.USER_UUID + + FROM + UserInfoTable.TABLE_NAME + " n" + + LEFT_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + UserInfoTable.USER_UUID + + WHERE + UserInfoTable.SERVER_UUID + "=?"; + + return new QueryStatement(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, serverUUID, date); + statement.setString(2, serverUUID.toString()); + } + + @Override + public Object processResults(ResultSet set) throws SQLException { + while (set.next()) { + double activityIndex = set.getDouble("activity_index"); // Returns 0.0 if missing + consumer.accept(UUID.fromString(set.getString(UserInfoTable.USER_UUID)), activityIndex); + } + return null; + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/NetworkActivityIndexQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/NetworkActivityIndexQueries.java new file mode 100644 index 000000000..a149c9730 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/NetworkActivityIndexQueries.java @@ -0,0 +1,457 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.analysis; + +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Queries for Activity Index that attempts to gain insight into player activity levels. + *

    + * Old formula for activity index was not linear and difficult to turn into a query due to conditional multipliers. + * Thus a new formula was written. + *

    + * {@code T} - Time played after someone is considered active on a particular week + * {@code t1, t2, t3} - Time played that week + *

    + * Activity index takes into account last 3 weeks. + *

    + * Activity for a single week is calculated using {@code A(t) = (1 / (pi/2 * (t/T) + 1))}. + * A(t) is based on function f(x) = 1 / (x + 1), which has property f(0) = 1, decreasing from there, but not in a straight line. + * You can see the function plotted here https://www.wolframalpha.com/input/?i=1+%2F+(x%2B1)+from+-1+to+2 + *

    + * To fine tune the curve pi/2 is used since it felt like a good curve. + *

    + * Activity index A is calculated by using the formula: + * {@code A = 5 - 5 * [A(t1) + A(t2) + A(t3)] / 3} + *

    + * Plot for A and limits + * https://www.wolframalpha.com/input/?i=plot+y+%3D+5+-+5+*+(1+%2F+(pi%2F2+*+x%2B1))+and+y+%3D1+and+y+%3D+2+and+y+%3D+3+and+y+%3D+3.75+from+-0.5+to+3 + *

    + * New Limits for A would thus be + * {@code < 1: Inactive} + * {@code > 1: Irregular} + * {@code > 2: Regular} + * {@code > 3: Active} + * {@code > 3.75: Very Active} + * + * @author Rsl1122 + */ +public class NetworkActivityIndexQueries { + + private NetworkActivityIndexQueries() { + // Static method class + } + + public static Query fetchRegularPlayerCount(long date, long playtimeThreshold) { + return fetchActivityGroupCount(date, playtimeThreshold, ActivityIndex.REGULAR, 5.1); + } + + public static String selectActivityIndexSQL() { + String selectActivePlaytimeSQL = SELECT + + SessionsTable.USER_UUID + + ",SUM(" + + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + '-' + SessionsTable.AFK_TIME + + ") as active_playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?" + + GROUP_BY + SessionsTable.USER_UUID; + + String selectThreeWeeks = selectActivePlaytimeSQL + UNION + selectActivePlaytimeSQL + UNION + selectActivePlaytimeSQL; + + return SELECT + + "5.0 - 5.0 * AVG(1 / (?/2 * (q1.active_playtime/?) +1)) as activity_index," + + "q1." + SessionsTable.USER_UUID + + FROM + '(' + selectThreeWeeks + ") q1" + + GROUP_BY + "q1." + SessionsTable.USER_UUID; + } + + public static void setSelectActivityIndexSQLParameters(PreparedStatement statement, int index, long playtimeThreshold, long date) throws SQLException { + statement.setDouble(index, Math.PI); + statement.setLong(index + 1, playtimeThreshold); + + statement.setLong(index + 2, date - TimeUnit.DAYS.toMillis(7L)); + statement.setLong(index + 3, date); + statement.setLong(index + 4, date - TimeUnit.DAYS.toMillis(14L)); + statement.setLong(index + 5, date - TimeUnit.DAYS.toMillis(7L)); + statement.setLong(index + 6, date - TimeUnit.DAYS.toMillis(21L)); + statement.setLong(index + 7, date - TimeUnit.DAYS.toMillis(14L)); + } + + public static Query fetchActivityGroupCount(long date, long playtimeThreshold, double above, double below) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectIndexes = SELECT + "COALESCE(activity_index, 0) as activity_index" + + FROM + UsersTable.TABLE_NAME + " u" + + LEFT_JOIN + '(' + selectActivityIndex + ") q2 on q2." + SessionsTable.USER_UUID + "=u." + UsersTable.USER_UUID + + WHERE + "u." + UsersTable.REGISTERED + "<=?"; + + String selectCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectIndexes + ") i" + + WHERE + "i.activity_index>=?" + + AND + "i.activity_index(selectCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, playtimeThreshold, date); + statement.setLong(9, date); + statement.setDouble(10, above); + statement.setDouble(11, below); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + public static Query> fetchActivityIndexGroupingsOn(long date, long threshold) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectIndexes = SELECT + "? as activity_group, activity_index" + + FROM + UserInfoTable.TABLE_NAME + " u" + + LEFT_JOIN + '(' + selectActivityIndex + ") s on s." + SessionsTable.USER_UUID + "=u." + UserInfoTable.USER_UUID + + AND + "u." + UserInfoTable.REGISTERED + "<=?"; + + String selectCount = SELECT + "activity_group, COUNT(1) as count" + FROM + + '(' + selectIndexes + ") indexes" + + WHERE + "COALESCE(indexes.activity_index,0)>=?" + + AND + "COALESCE(indexes.activity_index,0)>(selectMultipleCounts) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + set12ValuesForQuery(statement, 1, "Very Active", ActivityIndex.VERY_ACTIVE, 5.1); + set12ValuesForQuery(statement, 13, "Active", ActivityIndex.ACTIVE, ActivityIndex.VERY_ACTIVE); + set12ValuesForQuery(statement, 25, "Regular", ActivityIndex.REGULAR, ActivityIndex.ACTIVE); + set12ValuesForQuery(statement, 37, "Irregular", ActivityIndex.IRREGULAR, ActivityIndex.REGULAR); + set12ValuesForQuery(statement, 49, "Inactive", -1, ActivityIndex.IRREGULAR); + } + + private void set12ValuesForQuery( + PreparedStatement statement, int index, + String group, double above, double below + ) throws SQLException { + statement.setString(index, group); + setSelectActivityIndexSQLParameters(statement, index + 1, threshold, date); + statement.setLong(index + 9, date); + statement.setDouble(index + 10, above); + statement.setDouble(index + 11, below); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map groups = new HashMap<>(); + while (set.next()) { + groups.put(set.getString("activity_group"), set.getInt("count")); + } + return groups; + } + }; + } + + public static Query countNewPlayersTurnedRegular(long after, long before, Long threshold) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectActivePlayerCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectActivityIndex + ") q2" + + INNER_JOIN + UsersTable.TABLE_NAME + " u on u." + UsersTable.USER_UUID + "=q2." + SessionsTable.USER_UUID + + WHERE + "u." + UsersTable.REGISTERED + ">=?" + + AND + "u." + UsersTable.REGISTERED + "<=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectActivePlayerCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, before); + statement.setLong(9, after); + statement.setLong(10, before); + statement.setDouble(11, ActivityIndex.REGULAR); + statement.setDouble(12, 5.1); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + /** + * @param start Start of the tracking, those regular will be counted here. + * @param end End of the tracking, those inactive will be count here. + * @param threshold Playtime threshold + * @return Query how many players went from regular to inactive in a span of time. + */ + public static Query countRegularPlayersTurnedInactive(long start, long end, Long threshold) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectActivePlayerCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectActivityIndex + ") q2" + + // Join two select activity index queries together to query Regular and Inactive players + INNER_JOIN + '(' + selectActivityIndex.replace("q1", "q3") + ") q4" + + " on q2." + SessionsTable.USER_UUID + "=q4." + SessionsTable.USER_UUID + + WHERE + "q2.activity_index>=?" + + AND + "q2.activity_index=?" + + AND + "q4.activity_index(selectActivePlayerCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, end); + setSelectActivityIndexSQLParameters(statement, 9, threshold, start); + statement.setDouble(17, ActivityIndex.REGULAR); + statement.setDouble(18, 5.1); + statement.setDouble(19, -0.1); + statement.setDouble(20, ActivityIndex.IRREGULAR); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + public static Query averagePlaytimePerRegularPlayer(long after, long before, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectPlaytimePerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "SUM(p." + SessionsTable.SESSION_END + "-p." + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, before); + statement.setLong(9, before); + statement.setLong(10, after); + statement.setDouble(11, ActivityIndex.REGULAR); + statement.setDouble(12, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query averageSessionLengthPerRegularPlayer(long after, long before, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectSessionLengthPerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "p." + SessionsTable.SESSION_END + "-p." + SessionsTable.SESSION_START + " as length" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, before); + statement.setLong(9, before); + statement.setLong(10, after); + statement.setDouble(11, ActivityIndex.REGULAR); + statement.setDouble(12, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query averageAFKPerRegularPlayer(long after, long before, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectPlaytimePerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "SUM(p." + SessionsTable.AFK_TIME + ") as afk" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, before); + statement.setLong(9, before); + statement.setLong(10, after); + statement.setDouble(11, ActivityIndex.REGULAR); + statement.setDouble(12, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query> activityIndexForNewPlayers(long after, long before, Long threshold) { + String selectNewUUIDs = SELECT + UsersTable.USER_UUID + + FROM + UsersTable.TABLE_NAME + + WHERE + UsersTable.REGISTERED + "<=?" + + AND + UsersTable.REGISTERED + ">=?"; + + String sql = SELECT + "activity_index" + + FROM + '(' + selectNewUUIDs + ") n" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID; + + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + setSelectActivityIndexSQLParameters(statement, 3, threshold, before); + } + + @Override + public Collection processResults(ResultSet set) throws SQLException { + Collection indexes = new ArrayList<>(); + while (set.next()) { + indexes.add(new ActivityIndex(set.getDouble("activity_index"), before)); + } + return indexes; + } + }; + } + + public static Query averageActivityIndexForRetainedPlayers(long after, long before, Long threshold) { + String selectNewUUIDs = SELECT + UsersTable.USER_UUID + + FROM + UsersTable.TABLE_NAME + + WHERE + UsersTable.REGISTERED + "<=?" + + AND + UsersTable.REGISTERED + ">=?"; + + String selectUniqueUUIDs = SELECT + "DISTINCT " + SessionsTable.USER_UUID + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?"; + + String sql = SELECT + "AVG(activity_index) as average" + + FROM + '(' + selectNewUUIDs + ") n" + + INNER_JOIN + '(' + selectUniqueUUIDs + ") u on n." + SessionsTable.USER_UUID + "=u." + SessionsTable.USER_UUID + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + + // Have played in the last half of the time frame + long half = before - (before - after) / 2; + statement.setLong(3, half); + statement.setLong(4, before); + setSelectActivityIndexSQLParameters(statement, 5, threshold, before); + } + + @Override + public ActivityIndex processResults(ResultSet set) throws SQLException { + return set.next() ? new ActivityIndex(set.getDouble("average"), before) : new ActivityIndex(0.0, before); + } + }; + } + + public static Query averageActivityIndexForNonRetainedPlayers(long after, long before, Long threshold) { + String selectNewUUIDs = SELECT + UsersTable.USER_UUID + + FROM + UsersTable.TABLE_NAME + + WHERE + UsersTable.REGISTERED + "<=?" + + AND + UsersTable.REGISTERED + ">=?"; + + String selectUniqueUUIDs = SELECT + "DISTINCT " + SessionsTable.USER_UUID + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?"; + + String sql = SELECT + "AVG(activity_index) as average" + + FROM + '(' + selectNewUUIDs + ") n" + + LEFT_JOIN + '(' + selectUniqueUUIDs + ") u on n." + SessionsTable.USER_UUID + "=u." + SessionsTable.USER_UUID + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID + + WHERE + "n." + SessionsTable.USER_UUID + IS_NULL; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + + // Have played in the last half of the time frame + long half = before - (before - after) / 2; + statement.setLong(3, half); + statement.setLong(4, before); + setSelectActivityIndexSQLParameters(statement, 5, threshold, before); + } + + @Override + public ActivityIndex processResults(ResultSet set) throws SQLException { + return set.next() ? new ActivityIndex(set.getDouble("average"), before) : new ActivityIndex(0.0, before); + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/PlayerCountQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/PlayerCountQueries.java new file mode 100644 index 000000000..a22eeebda --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/PlayerCountQueries.java @@ -0,0 +1,435 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.analysis; + +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Queries for server overview tab data. + * + * @author Rsl1122 + */ +public class PlayerCountQueries { + + private PlayerCountQueries() { + // Static method class + } + + private static QueryStatement queryPlayerCount(String sql, long after, long before, UUID serverUUID) { + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + statement.setString(3, serverUUID.toString()); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("player_count") : 0; + } + }; + } + + private static QueryStatement queryPlayerCount(String sql, long after, long before) { + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("player_count") : 0; + } + }; + } + + public static Query uniquePlayerCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?"; + + return queryPlayerCount(sql, after, before, serverUUID); + } + + /** + * Fetch uniquePlayer count for ALL servers. + * + * @param after After epoch ms + * @param before Before epoch ms + * @return Unique player count (players who played within time frame) + */ + public static Query uniquePlayerCount(long after, long before) { + String sql = SELECT + "COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?"; + + return queryPlayerCount(sql, after, before); + } + + public static Query> uniquePlayerCounts(long after, long before) { + String sql = SELECT + SessionsTable.SERVER_UUID + ",COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + GROUP_BY + UserInfoTable.SERVER_UUID; + + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map byServer = new HashMap<>(); + while (set.next()) { + byServer.put(UUID.fromString(set.getString(UserInfoTable.SERVER_UUID)), set.getInt("player_count")); + } + return byServer; + } + }; + } + + /** + * Fetch a EpochMs - Count map of unique players on a server. + * + * @param after After epoch ms + * @param before Before epoch ms + * @param timeZoneOffset Offset from {@link java.util.TimeZone#getOffset(long)}, applied to the dates before grouping. + * @param serverUUID UUID of the Plan server + * @return Map: Epoch ms (Start of day at 0 AM, no offset) - How many unique players played that day + */ + public static Query> uniquePlayerCounts(long after, long before, long timeZoneOffset, UUID serverUUID) { + return database -> { + Sql sql = database.getSql(); + String selectUniquePlayersPerDay = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) + + "*1000 as date," + + "COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + "date"; + + return database.query(new QueryStatement>(selectUniquePlayersPerDay, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + statement.setString(4, serverUUID.toString()); + } + + @Override + public NavigableMap processResults(ResultSet set) throws SQLException { + NavigableMap uniquePerDay = new TreeMap<>(); + while (set.next()) { + uniquePerDay.put(set.getLong("date"), set.getInt("player_count")); + } + return uniquePerDay; + } + }); + }; + } + + /** + * Fetch a EpochMs - Count map of unique players on ALL servers. + * + * @param after After epoch ms + * @param before Before epoch ms + * @param timeZoneOffset Offset from {@link java.util.TimeZone#getOffset(long)}, applied to the dates before grouping. + * @return Map: Epoch ms (Start of day at 0 AM, no offset) - How many unique players played that day + */ + public static Query> uniquePlayerCounts(long after, long before, long timeZoneOffset) { + return database -> { + Sql sql = database.getSql(); + String selectUniquePlayersPerDay = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) + + "*1000 as date," + + "COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + GROUP_BY + "date"; + + return database.query(new QueryStatement>(selectUniquePlayersPerDay, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + } + + @Override + public NavigableMap processResults(ResultSet set) throws SQLException { + NavigableMap uniquePerDay = new TreeMap<>(); + while (set.next()) { + uniquePerDay.put(set.getLong("date"), set.getInt("player_count")); + } + return uniquePerDay; + } + }); + }; + } + + public static Query averageUniquePlayerCount(long after, long before, long timeZoneOffset, UUID serverUUID) { + return database -> { + Sql sql = database.getSql(); + String selectUniquePlayersPerDay = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) + + "*1000 as date," + + "COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + "date"; + String selectAverage = SELECT + "AVG(player_count) as average" + FROM + '(' + selectUniquePlayersPerDay + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + statement.setString(4, serverUUID.toString()); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("average") : 0; + } + }); + }; + } + + public static Query newPlayerCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "COUNT(1) as player_count" + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.SERVER_UUID + "=?"; + + return queryPlayerCount(sql, after, before, serverUUID); + } + + public static Query newPlayerCount(long after, long before) { + String sql = SELECT + "COUNT(1) as player_count" + + FROM + UsersTable.TABLE_NAME + + WHERE + UsersTable.REGISTERED + "<=?" + + AND + UsersTable.REGISTERED + ">=?"; + + return queryPlayerCount(sql, after, before); + } + + public static Query> newPlayerCounts(long after, long before) { + String sql = SELECT + UserInfoTable.SERVER_UUID + ",COUNT(1) as player_count" + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + GROUP_BY + UserInfoTable.SERVER_UUID; + + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map byServer = new HashMap<>(); + while (set.next()) { + byServer.put(UUID.fromString(set.getString(UserInfoTable.SERVER_UUID)), set.getInt("player_count")); + } + return byServer; + } + }; + } + + /** + * Fetch a EpochMs - Count map of new players on a server. + * + * @param after After epoch ms + * @param before Before epoch ms + * @param timeZoneOffset Offset from {@link java.util.TimeZone#getOffset(long)}, applied to the dates before grouping. + * @param serverUUID UUID of the Plan server + * @return Map: Epoch ms (Start of day at 0 AM, no offset) - How many new players joined that day + */ + public static Query> newPlayerCounts(long after, long before, long timeZoneOffset, UUID serverUUID) { + return database -> { + Sql sql = database.getSql(); + String selectNewPlayersQuery = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + UserInfoTable.REGISTERED + "+?)/1000"))) + + "*1000 as date," + + "COUNT(1) as player_count" + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.SERVER_UUID + "=?" + + GROUP_BY + "date"; + + return database.query(new QueryStatement>(selectNewPlayersQuery, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + statement.setString(4, serverUUID.toString()); + } + + @Override + public NavigableMap processResults(ResultSet set) throws SQLException { + NavigableMap newPerDay = new TreeMap<>(); + while (set.next()) { + newPerDay.put(set.getLong("date"), set.getInt("player_count")); + } + return newPerDay; + } + }); + }; + } + + /** + * Fetch a EpochMs - Count map of new players on a server. + * + * @param after After epoch ms + * @param before Before epoch ms + * @param timeZoneOffset Offset from {@link java.util.TimeZone#getOffset(long)}, applied to the dates before grouping. + * @return Map: Epoch ms (Start of day at 0 AM, no offset) - How many new players joined that day + */ + public static Query> newPlayerCounts(long after, long before, long timeZoneOffset) { + return database -> { + Sql sql = database.getSql(); + String selectNewPlayersQuery = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + UserInfoTable.REGISTERED + "+?)/1000"))) + + "*1000 as date," + + "COUNT(1) as player_count" + + FROM + UsersTable.TABLE_NAME + + WHERE + UsersTable.REGISTERED + "<=?" + + AND + UsersTable.REGISTERED + ">=?" + + GROUP_BY + "date"; + + return database.query(new QueryStatement>(selectNewPlayersQuery, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + } + + @Override + public NavigableMap processResults(ResultSet set) throws SQLException { + NavigableMap newPerDay = new TreeMap<>(); + while (set.next()) { + newPerDay.put(set.getLong("date"), set.getInt("player_count")); + } + return newPerDay; + } + }); + }; + } + + public static Query averageNewPlayerCount(long after, long before, long timeZoneOffset, UUID serverUUID) { + return database -> { + Sql sql = database.getSql(); + String selectNewPlayersQuery = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + UserInfoTable.REGISTERED + "+?)/1000"))) + + "*1000 as date," + + "COUNT(1) as player_count" + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.SERVER_UUID + "=?" + + GROUP_BY + "date"; + String selectAverage = SELECT + "AVG(player_count) as average" + FROM + '(' + selectNewPlayersQuery + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + statement.setString(4, serverUUID.toString()); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("average") : 0; + } + }); + }; + } + + public static Query retainedPlayerCount(long after, long before, UUID serverUUID) { + String selectNewUUIDs = SELECT + UserInfoTable.USER_UUID + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.SERVER_UUID + "=?"; + + String selectUniqueUUIDs = SELECT + DISTINCT + SessionsTable.USER_UUID + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SERVER_UUID + "=?"; + + String sql = SELECT + "COUNT(1) as player_count" + + FROM + '(' + selectNewUUIDs + ") q1" + + INNER_JOIN + '(' + selectUniqueUUIDs + ") q2 on q1." + UserInfoTable.USER_UUID + "=q2." + SessionsTable.USER_UUID; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, after); + statement.setLong(2, before); + statement.setString(3, serverUUID.toString()); + + // Have played in the last half of the time frame + long half = before - (before - after) / 2; + statement.setLong(4, half); + statement.setLong(5, before); + statement.setString(6, serverUUID.toString()); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("player_count") : 0; + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ContainerFetchQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ContainerFetchQueries.java similarity index 56% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ContainerFetchQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ContainerFetchQueries.java index dc18b6a5d..599e493f8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ContainerFetchQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ContainerFetchQueries.java @@ -14,18 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.containers; +package com.djrapitops.plan.storage.database.queries.containers; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.containers.ServerContainer; -import com.djrapitops.plan.db.access.Query; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.container.ServerContainer; +import com.djrapitops.plan.storage.database.queries.Query; -import java.util.List; import java.util.UUID; /** - * Static method class for queries that return some kind of {@link com.djrapitops.plan.data.store.containers.DataContainer}. + * Static method class for queries that return some kind of {@link DataContainer}. * * @author Rsl1122 */ @@ -35,16 +34,6 @@ public class ContainerFetchQueries { /* Static method class */ } - /** - * Used to get a NetworkContainer, some limitations apply to values returned by DataContainer keys. - * - * @return a new NetworkContainer. - * @see NetworkContainerQuery - */ - public static Query fetchNetworkContainer() { - return new NetworkContainerQuery(); - } - /** * Used to get a ServerContainer, some limitations apply to values returned by DataContainer keys. * @@ -69,19 +58,4 @@ public class ContainerFetchQueries { return new PlayerContainerQuery(playerUUID); } - /** - * Used to get PlayerContainers of all players on the network, some limitations apply to DataContainer keys. - *

    - * Limitations: - * - PlayerContainers do not support: PlayerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_KILL_COUNT - * - PlayerContainers PlayerKeys.PER_SERVER does not support: PerServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_KILL_COUNT - *

    - * Blocking methods are not called until DataContainer getter methods are called. - * - * @return a list of PlayerContainers in Plan database. - */ - public static Query> fetchAllPlayerContainers() { - return new AllPlayerContainersQuery(); - } - } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/PerServerContainerQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PerServerContainerQuery.java similarity index 66% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/PerServerContainerQuery.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PerServerContainerQuery.java index 2322319e6..cba106dc3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/PerServerContainerQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PerServerContainerQuery.java @@ -14,22 +14,21 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.containers; +package com.djrapitops.plan.storage.database.queries.containers; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.container.UserInfo; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.store.containers.SupplierDataContainer; -import com.djrapitops.plan.data.store.keys.PerServerKeys; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.PerServerAggregateQueries; -import com.djrapitops.plan.db.access.queries.objects.SessionQueries; -import com.djrapitops.plan.db.access.queries.objects.UserInfoQueries; -import com.djrapitops.plan.db.access.queries.objects.WorldTimesQueries; +import com.djrapitops.plan.delivery.domain.container.DataContainer; +import com.djrapitops.plan.delivery.domain.container.PerServerContainer; +import com.djrapitops.plan.delivery.domain.container.SupplierDataContainer; +import com.djrapitops.plan.delivery.domain.keys.Key; +import com.djrapitops.plan.delivery.domain.keys.PerServerKeys; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.UserInfo; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.PerServerAggregateQueries; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.objects.SessionQueries; +import com.djrapitops.plan.storage.database.queries.objects.UserInfoQueries; +import com.djrapitops.plan.storage.database.queries.objects.WorldTimesQueries; import java.util.List; import java.util.Map; @@ -55,18 +54,10 @@ public class PerServerContainerQuery implements Query { userInformation(db, perServerContainer); lastSeen(db, perServerContainer); playerKillCount(db, perServerContainer); - playerDeathCount(db, perServerContainer); mobKillCount(db, perServerContainer); totalDeathCount(db, perServerContainer); worldTimes(db, perServerContainer); - // After-values that can be calculated without database. - for (DataContainer serverContainer : perServerContainer.values()) { - serverContainer.putSupplier(PerServerKeys.MOB_DEATH_COUNT, () -> - serverContainer.getValue(PerServerKeys.DEATH_COUNT).orElse(0) - serverContainer.getValue(PerServerKeys.PLAYER_DEATH_COUNT).orElse(0) - ); - } - Map> sessions = db.query(SessionQueries.fetchSessionsOfPlayer(playerUUID)); for (Map.Entry> entry : sessions.entrySet()) { UUID serverUUID = entry.getKey(); @@ -75,9 +66,6 @@ public class PerServerContainerQuery implements Query { DataContainer serverContainer = perServerContainer.getOrDefault(serverUUID, new SupplierDataContainer()); serverContainer.putRawData(PerServerKeys.SESSIONS, serverSessions); - serverContainer.putSupplier(PerServerKeys.PLAYER_KILLS, () -> SessionsMutator.forContainer(serverContainer).toPlayerKillList()); - serverContainer.putSupplier(PerServerKeys.PLAYER_DEATHS, () -> SessionsMutator.forContainer(serverContainer).toPlayerDeathList()); - perServerContainer.put(serverUUID, serverContainer); } @@ -92,10 +80,6 @@ public class PerServerContainerQuery implements Query { matchingEntrySet(PerServerKeys.WORLD_TIMES, WorldTimesQueries.fetchPlayerWorldTimesOnServers(playerUUID), db, container); } - private void playerDeathCount(SQLDB db, PerServerContainer container) { - matchingEntrySet(PerServerKeys.PLAYER_DEATH_COUNT, PerServerAggregateQueries.playerDeathCountOnServers(playerUUID), db, container); - } - private void mobKillCount(SQLDB db, PerServerContainer container) { matchingEntrySet(PerServerKeys.MOB_KILL_COUNT, PerServerAggregateQueries.mobKillCountOnServers(playerUUID), db, container); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/PlayerContainerQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PlayerContainerQuery.java similarity index 78% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/PlayerContainerQuery.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PlayerContainerQuery.java index 415272f45..50bc35bd6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/PlayerContainerQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/PlayerContainerQuery.java @@ -14,21 +14,21 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.containers; +package com.djrapitops.plan.storage.database.queries.containers; -import com.djrapitops.plan.data.container.BaseUser; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.Key; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.mutators.PerServerMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.objects.*; +import com.djrapitops.plan.delivery.domain.container.PerServerContainer; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.Key; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.domain.mutators.PerServerMutator; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.gathering.domain.BaseUser; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.WorldTimes; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.objects.*; import java.util.Collection; import java.util.List; @@ -84,9 +84,8 @@ public class PlayerContainerQuery implements Query { }); container.putSupplier(PlayerKeys.LAST_SEEN, () -> SessionsMutator.forContainer(container).toLastSeen()); - - container.putSupplier(PlayerKeys.PLAYER_KILLS, () -> SessionsMutator.forContainer(container).toPlayerKillList()); - container.putSupplier(PlayerKeys.PLAYER_DEATHS, () -> SessionsMutator.forContainer(container).toPlayerDeathList()); + container.putSupplier(PlayerKeys.PLAYER_KILLS, () -> db.query(KillQueries.fetchPlayerKillsOfPlayer(uuid))); + container.putSupplier(PlayerKeys.PLAYER_DEATHS_KILLS, () -> db.query(KillQueries.fetchPlayerDeathsOfPlayer(uuid))); container.putSupplier(PlayerKeys.PLAYER_KILL_COUNT, () -> container.getValue(PlayerKeys.PLAYER_KILLS).map(Collection::size).orElse(0)); container.putSupplier(PlayerKeys.MOB_KILL_COUNT, () -> SessionsMutator.forContainer(container).toMobKillCount()); container.putSupplier(PlayerKeys.DEATH_COUNT, () -> SessionsMutator.forContainer(container).toDeathCount()); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerContainerQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ServerContainerQuery.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerContainerQuery.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ServerContainerQuery.java index 6c7411769..54dd3373e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerContainerQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ServerContainerQuery.java @@ -14,22 +14,21 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.containers; +package com.djrapitops.plan.storage.database.queries.containers; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.ServerContainer; -import com.djrapitops.plan.data.store.keys.ServerKeys; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.ServerAggregateQueries; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.queries.objects.TPSQueries; -import com.djrapitops.plan.db.access.queries.objects.WorldTimesQueries; +import com.djrapitops.plan.delivery.domain.container.ServerContainer; +import com.djrapitops.plan.delivery.domain.keys.ServerKeys; +import com.djrapitops.plan.delivery.domain.mutators.PlayersMutator; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerDataQuery; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.queries.objects.TPSQueries; +import com.djrapitops.plan.storage.database.queries.objects.WorldTimesQueries; import java.util.Collection; import java.util.List; @@ -79,7 +78,6 @@ public class ServerContainerQuery implements Query { return db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo)).orElse(null); }); - container.putCachingSupplier(ServerKeys.COMMAND_USAGE, () -> db.query(ServerAggregateQueries.commandUsageCounts(serverUUID))); container.putCachingSupplier(ServerKeys.WORLD_TIMES, () -> db.query(WorldTimesQueries.fetchServerTotalWorldTimes(serverUUID))); // Calculating getters diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayerContainersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ServerPlayerContainersQuery.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayerContainersQuery.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ServerPlayerContainersQuery.java index d5b757e77..51ab5c8ed 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayerContainersQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/containers/ServerPlayerContainersQuery.java @@ -14,20 +14,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.containers; +package com.djrapitops.plan.storage.database.queries.containers; -import com.djrapitops.plan.data.container.*; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.mutators.PerServerMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.objects.*; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.delivery.domain.container.PerServerContainer; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.domain.mutators.PerServerMutator; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.gathering.domain.*; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.objects.*; import java.util.*; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/BaseUserQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/BaseUserQueries.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/BaseUserQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/BaseUserQueries.java index 75e887310..cae952bce 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/BaseUserQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/BaseUserQueries.java @@ -14,15 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.data.container.BaseUser; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.parsing.Select; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; -import com.djrapitops.plan.db.sql.tables.UsersTable; +import com.djrapitops.plan.gathering.domain.BaseUser; +import com.djrapitops.plan.gathering.domain.UserInfo; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.parsing.Select; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -32,7 +33,7 @@ import java.util.HashSet; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Queries for {@link BaseUser} objects. @@ -48,7 +49,7 @@ public class BaseUserQueries { /** * Query database for common user information. *

    - * Only one {@link BaseUser} per player exists unlike {@link com.djrapitops.plan.data.container.UserInfo} which is available per server. + * Only one {@link BaseUser} per player exists unlike {@link UserInfo} which is available per server. * * @return Map: Player UUID - BaseUser */ @@ -79,7 +80,7 @@ public class BaseUserQueries { /** * Query database for common user information of a player. *

    - * Only one {@link BaseUser} per player exists unlike {@link com.djrapitops.plan.data.container.UserInfo} which is available per server. + * Only one {@link BaseUser} per player exists unlike {@link UserInfo} which is available per server. * * @param playerUUID UUID of the Player. * @return Optional: BaseUser if found, empty if not. @@ -111,7 +112,7 @@ public class BaseUserQueries { /** * Query database for common user information for players that have played on a specific server. *

    - * Only one {@link BaseUser} per player exists unlike {@link com.djrapitops.plan.data.container.UserInfo} which is available per server. + * Only one {@link BaseUser} per player exists unlike {@link UserInfo} which is available per server. *

    * This will fetch BaseUsers for which UserInfo object also exists on the server. * diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/GeoInfoQueries.java similarity index 50% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/GeoInfoQueries.java index 14f6dbaf6..b698a88f3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/GeoInfoQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/GeoInfoQueries.java @@ -14,24 +14,24 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.GeoInfoTable; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; +import com.djrapitops.plan.gathering.domain.GeoInfo; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** - * Queries for {@link com.djrapitops.plan.data.container.GeoInfo} objects. + * Queries for {@link GeoInfo} objects. * * @author Rsl1122 */ @@ -48,7 +48,6 @@ public class GeoInfoQueries { */ public static Query>> fetchAllGeoInformation() { String sql = SELECT + - GeoInfoTable.IP + ',' + GeoInfoTable.GEOLOCATION + ',' + GeoInfoTable.LAST_USED + ',' + GeoInfoTable.USER_UUID + @@ -69,10 +68,9 @@ public class GeoInfoQueries { List userGeoInfo = geoInformation.getOrDefault(uuid, new ArrayList<>()); - String ip = set.getString(GeoInfoTable.IP); String geolocation = set.getString(GeoInfoTable.GEOLOCATION); long lastUsed = set.getLong(GeoInfoTable.LAST_USED); - userGeoInfo.add(new GeoInfo(ip, geolocation, lastUsed)); + userGeoInfo.add(new GeoInfo(geolocation, lastUsed)); geoInformation.put(uuid, userGeoInfo); } @@ -86,8 +84,12 @@ public class GeoInfoQueries { * @return List of {@link GeoInfo}, empty if none are found. */ public static Query> fetchPlayerGeoInformation(UUID playerUUID) { - String sql = SELECT + DISTINCT + '*' + FROM + GeoInfoTable.TABLE_NAME + - WHERE + GeoInfoTable.USER_UUID + "=?"; + String sql = SELECT + + GeoInfoTable.GEOLOCATION + + ",MAX(" + GeoInfoTable.LAST_USED + ") as " + GeoInfoTable.LAST_USED + + FROM + GeoInfoTable.TABLE_NAME + + WHERE + GeoInfoTable.USER_UUID + "=?" + + GROUP_BY + GeoInfoTable.GEOLOCATION; return new QueryStatement>(sql, 100) { @Override @@ -99,10 +101,9 @@ public class GeoInfoQueries { public List processResults(ResultSet set) throws SQLException { List geoInfo = new ArrayList<>(); while (set.next()) { - String ip = set.getString(GeoInfoTable.IP); String geolocation = set.getString(GeoInfoTable.GEOLOCATION); long lastUsed = set.getLong(GeoInfoTable.LAST_USED); - geoInfo.add(new GeoInfo(ip, geolocation, lastUsed)); + geoInfo.add(new GeoInfo(geolocation, lastUsed)); } return geoInfo; } @@ -112,8 +113,7 @@ public class GeoInfoQueries { public static Query>> fetchServerGeoInformation(UUID serverUUID) { String sql = SELECT + GeoInfoTable.TABLE_NAME + '.' + GeoInfoTable.USER_UUID + ',' + GeoInfoTable.GEOLOCATION + ',' + - GeoInfoTable.LAST_USED + ',' + - GeoInfoTable.IP + + GeoInfoTable.LAST_USED + FROM + GeoInfoTable.TABLE_NAME + INNER_JOIN + UserInfoTable.TABLE_NAME + " on " + GeoInfoTable.TABLE_NAME + '.' + GeoInfoTable.USER_UUID + "=" + UserInfoTable.TABLE_NAME + '.' + UserInfoTable.USER_UUID + @@ -130,4 +130,69 @@ public class GeoInfoQueries { } }; } + + public static Query> networkGeolocationCounts() { + String subQuery1 = SELECT + + GeoInfoTable.USER_UUID + ", " + + GeoInfoTable.GEOLOCATION + ", " + + GeoInfoTable.LAST_USED + + FROM + GeoInfoTable.TABLE_NAME; + String subQuery2 = SELECT + + GeoInfoTable.USER_UUID + ", " + + "MAX(" + GeoInfoTable.LAST_USED + ") as m" + + FROM + GeoInfoTable.TABLE_NAME + + GROUP_BY + GeoInfoTable.USER_UUID; + String sql = SELECT + GeoInfoTable.GEOLOCATION + ", COUNT(1) as c FROM (" + + "(" + subQuery1 + ") AS q1" + + INNER_JOIN + "(" + subQuery2 + ") AS q2 ON q1.uuid = q2.uuid)" + + WHERE + GeoInfoTable.LAST_USED + "=m" + + GROUP_BY + GeoInfoTable.GEOLOCATION; + + return new QueryAllStatement>(sql) { + @Override + public Map processResults(ResultSet set) throws SQLException { + Map geolocationCounts = new HashMap<>(); + while (set.next()) { + geolocationCounts.put(set.getString(GeoInfoTable.GEOLOCATION), set.getInt("c")); + } + return geolocationCounts; + } + }; + } + + public static Query> serverGeolocationCounts(UUID serverUUID) { + String selectGeolocations = SELECT + + GeoInfoTable.USER_UUID + ", " + + GeoInfoTable.GEOLOCATION + ", " + + GeoInfoTable.LAST_USED + + FROM + GeoInfoTable.TABLE_NAME; + String selectLatestGeolocationDate = SELECT + + GeoInfoTable.USER_UUID + ", " + + "MAX(" + GeoInfoTable.LAST_USED + ") as m" + + FROM + GeoInfoTable.TABLE_NAME + + GROUP_BY + GeoInfoTable.USER_UUID; + String sql = SELECT + GeoInfoTable.GEOLOCATION + ", COUNT(1) as c FROM (" + + "(" + selectGeolocations + ") AS q1" + + INNER_JOIN + "(" + selectLatestGeolocationDate + ") AS q2 ON q1.uuid = q2.uuid" + + INNER_JOIN + UserInfoTable.TABLE_NAME + " u on u." + UserInfoTable.USER_UUID + "=q1.uuid)" + + WHERE + GeoInfoTable.LAST_USED + "=m" + + AND + "u." + UserInfoTable.SERVER_UUID + "=?" + + GROUP_BY + GeoInfoTable.GEOLOCATION; + + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map geolocationCounts = new HashMap<>(); + while (set.next()) { + geolocationCounts.put(set.getString(GeoInfoTable.GEOLOCATION), set.getInt("c")); + } + return geolocationCounts; + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/KillQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/KillQueries.java new file mode 100644 index 000000000..23f978871 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/KillQueries.java @@ -0,0 +1,315 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.objects; + +import com.djrapitops.plan.gathering.domain.PlayerDeath; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.KillsTable; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Queries for {@link PlayerKill} and {@link PlayerDeath} objects. + * + * @author Rsl1122 + */ +public class KillQueries { + + private KillQueries() { + // Static method class + } + + public static Query> fetchPlayerKillsOnServer(UUID serverUUID, int limit) { + String sql = SELECT + KillsTable.VICTIM_UUID + ", " + + "v." + UsersTable.USER_NAME + " as victim_name, " + + "k." + UsersTable.USER_NAME + " as killer_name," + + KillsTable.DATE + ", " + + KillsTable.WEAPON + + FROM + KillsTable.TABLE_NAME + + INNER_JOIN + UsersTable.TABLE_NAME + " v on v." + UsersTable.USER_UUID + "=" + KillsTable.VICTIM_UUID + + INNER_JOIN + UsersTable.TABLE_NAME + " k on k." + UsersTable.USER_UUID + "=" + KillsTable.KILLER_UUID + + WHERE + KillsTable.TABLE_NAME + '.' + KillsTable.SERVER_UUID + "=?" + + ORDER_BY + KillsTable.DATE + " DESC LIMIT ?"; + + return new QueryStatement>(sql, limit) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setInt(2, limit); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List kills = new ArrayList<>(); + while (set.next()) { + extractKillFromResults(set).ifPresent(kills::add); + } + return kills; + } + }; + } + + public static Query> fetchPlayerKillsOfPlayer(UUID playerUUID) { + String sql = SELECT + KillsTable.VICTIM_UUID + ", " + + "v." + UsersTable.USER_NAME + " as victim_name, " + + "k." + UsersTable.USER_NAME + " as killer_name," + + KillsTable.DATE + ", " + + KillsTable.WEAPON + + FROM + KillsTable.TABLE_NAME + + INNER_JOIN + UsersTable.TABLE_NAME + " v on v." + UsersTable.USER_UUID + "=" + KillsTable.VICTIM_UUID + + INNER_JOIN + UsersTable.TABLE_NAME + " k on k." + UsersTable.USER_UUID + "=" + KillsTable.KILLER_UUID + + WHERE + KillsTable.TABLE_NAME + '.' + KillsTable.KILLER_UUID + "=?" + + ORDER_BY + KillsTable.DATE + " DESC"; + + return new QueryStatement>(sql, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List kills = new ArrayList<>(); + while (set.next()) { + extractKillFromResults(set).ifPresent(kills::add); + } + return kills; + } + }; + } + + public static Query> fetchPlayerDeathsOfPlayer(UUID playerUUID) { + String sql = SELECT + KillsTable.VICTIM_UUID + ", " + + "v." + UsersTable.USER_NAME + " as victim_name, " + + "k." + UsersTable.USER_NAME + " as killer_name," + + KillsTable.DATE + ", " + + KillsTable.WEAPON + + FROM + KillsTable.TABLE_NAME + + INNER_JOIN + UsersTable.TABLE_NAME + " v on v." + UsersTable.USER_UUID + "=" + KillsTable.VICTIM_UUID + + INNER_JOIN + UsersTable.TABLE_NAME + " k on k." + UsersTable.USER_UUID + "=" + KillsTable.KILLER_UUID + + WHERE + KillsTable.TABLE_NAME + '.' + KillsTable.VICTIM_UUID + "=?" + + ORDER_BY + KillsTable.DATE + " DESC"; + + return new QueryStatement>(sql, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List kills = new ArrayList<>(); + while (set.next()) { + extractKillFromResults(set).ifPresent(kills::add); + } + return kills; + } + }; + } + + private static Optional extractKillFromResults(ResultSet set) throws SQLException { + String victimName = set.getString("victim_name"); + String killerName = set.getString("killer_name"); + if (victimName != null && killerName != null) { + UUID victim = UUID.fromString(set.getString(KillsTable.VICTIM_UUID)); + long date = set.getLong(KillsTable.DATE); + String weapon = set.getString(KillsTable.WEAPON); + return Optional.of(new PlayerKill(victim, weapon, date, victimName, killerName)); + } + return Optional.empty(); + } + + public static Query playerKillCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "COUNT(1) as count" + + FROM + KillsTable.TABLE_NAME + + WHERE + KillsTable.SERVER_UUID + "=?" + + AND + KillsTable.DATE + ">=?" + + AND + KillsTable.DATE + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + public static Query averageKDR(long after, long before, UUID serverUUID) { + String selectKillCounts = SELECT + "COUNT(1) as kills," + KillsTable.KILLER_UUID + + FROM + KillsTable.TABLE_NAME + + WHERE + KillsTable.SERVER_UUID + "=?" + + AND + KillsTable.DATE + ">=?" + + AND + KillsTable.DATE + "<=?" + + GROUP_BY + KillsTable.KILLER_UUID; + String selectDeathCounts = SELECT + "COUNT(1) as deaths," + KillsTable.VICTIM_UUID + + FROM + KillsTable.TABLE_NAME + + WHERE + KillsTable.SERVER_UUID + "=?" + + AND + KillsTable.DATE + ">=?" + + AND + KillsTable.DATE + "<=?" + + GROUP_BY + KillsTable.VICTIM_UUID; + String sql = SELECT + "u." + UserInfoTable.USER_UUID + ",kills, deaths" + + FROM + UserInfoTable.TABLE_NAME + " u" + + LEFT_JOIN + '(' + selectKillCounts + ") q1 on q1." + KillsTable.KILLER_UUID + "=u." + UserInfoTable.USER_UUID + + LEFT_JOIN + '(' + selectDeathCounts + ") q2 on q2." + KillsTable.VICTIM_UUID + "=u." + UserInfoTable.USER_UUID + + WHERE + "u." + UserInfoTable.SERVER_UUID + "=?"; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + statement.setString(4, serverUUID.toString()); + statement.setLong(5, after); + statement.setLong(6, before); + statement.setString(7, serverUUID.toString()); + } + + @Override + public Double processResults(ResultSet set) throws SQLException { + double totalKDR = 0.0; + int playerCount = 0; + while (set.next()) { + int kills = set.getInt("kills"); + int deaths = set.getInt("deaths"); + totalKDR += (double) kills / (deaths > 0 ? deaths : 1); + playerCount++; + } + return totalKDR / (playerCount > 0 ? playerCount : 1); + } + }; + } + + public static Query mobKillCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "SUM(" + SessionsTable.MOB_KILLS + ") as count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + public static Query deathCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "SUM(" + SessionsTable.DEATHS + ") as count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + public static Query> topWeaponsOfServer(long after, long before, UUID serverUUID, int limit) { + String innerSQL = SELECT + KillsTable.WEAPON + ", COUNT(1) as kills" + + FROM + KillsTable.TABLE_NAME + + WHERE + KillsTable.SERVER_UUID + "=?" + + AND + KillsTable.DATE + ">=?" + + AND + KillsTable.DATE + "<=?" + + GROUP_BY + KillsTable.WEAPON; + String sql = SELECT + KillsTable.WEAPON + + FROM + '(' + innerSQL + ") q1" + + ORDER_BY + "kills DESC LIMIT ?"; + + return new QueryStatement>(sql, limit) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + statement.setInt(4, limit); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List weapons = new ArrayList<>(); + while (set.next()) weapons.add(set.getString(KillsTable.WEAPON)); + return weapons; + } + }; + } + + public static Query> topWeaponsOfPlayer(long after, long before, UUID playerUUID, int limit) { + String innerSQL = SELECT + KillsTable.WEAPON + ", COUNT(1) as kills" + + FROM + KillsTable.TABLE_NAME + + WHERE + KillsTable.KILLER_UUID + "=?" + + AND + KillsTable.DATE + ">=?" + + AND + KillsTable.DATE + "<=?" + + GROUP_BY + KillsTable.WEAPON; + String sql = SELECT + KillsTable.WEAPON + + FROM + '(' + innerSQL + ") q1" + + ORDER_BY + "kills DESC LIMIT ?"; + + return new QueryStatement>(sql, limit) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + statement.setInt(4, limit); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List weapons = new ArrayList<>(); + while (set.next()) weapons.add(set.getString(KillsTable.WEAPON)); + return weapons; + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NetworkTablePlayersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NetworkTablePlayersQuery.java new file mode 100644 index 000000000..cb1e55d1e --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NetworkTablePlayersQuery.java @@ -0,0 +1,133 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.objects; + +import com.djrapitops.plan.delivery.domain.TablePlayer; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries; +import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Query for displaying players on /players page. + * + * @author Rsl1122 + */ +public class NetworkTablePlayersQuery implements Query> { + + private final long date; + private final long activeMsThreshold; + private final int xMostRecentPlayers; + + public NetworkTablePlayersQuery(long date, long activeMsThreshold, int xMostRecentPlayers) { + this.date = date; + this.activeMsThreshold = activeMsThreshold; + this.xMostRecentPlayers = xMostRecentPlayers; + } + + @Override + public List executeQuery(SQLDB db) { + String selectGeolocations = SELECT + DISTINCT + + GeoInfoTable.USER_UUID + ", " + + GeoInfoTable.GEOLOCATION + ", " + + GeoInfoTable.LAST_USED + + FROM + GeoInfoTable.TABLE_NAME; + String selectLatestGeolocationDate = SELECT + + GeoInfoTable.USER_UUID + ", " + + "MAX(" + GeoInfoTable.LAST_USED + ") as last_used_g" + + FROM + GeoInfoTable.TABLE_NAME + + GROUP_BY + GeoInfoTable.USER_UUID; + String selectLatestGeolocations = SELECT + + "g1." + GeoInfoTable.GEOLOCATION + ',' + + "g1." + GeoInfoTable.USER_UUID + + FROM + "(" + selectGeolocations + ") AS g1" + + INNER_JOIN + "(" + selectLatestGeolocationDate + ") AS g2 ON g1.uuid = g2.uuid" + + WHERE + GeoInfoTable.LAST_USED + "=last_used_g"; + + String selectSessionData = SELECT + "s." + SessionsTable.USER_UUID + ',' + + "MAX(" + SessionsTable.SESSION_END + ") as last_seen," + + "COUNT(1) as count," + + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + " s" + + GROUP_BY + "s." + SessionsTable.USER_UUID; + + String selectBanned = SELECT + DISTINCT + "ub." + UserInfoTable.USER_UUID + + FROM + UserInfoTable.TABLE_NAME + " ub" + + WHERE + UserInfoTable.BANNED + "=?"; + + String selectBaseUsers = SELECT + + "u." + UsersTable.USER_UUID + ',' + + "u." + UsersTable.USER_NAME + ',' + + "u." + UsersTable.REGISTERED + ',' + + "ban." + UserInfoTable.USER_UUID + " as banned," + + "geoloc." + GeoInfoTable.GEOLOCATION + ',' + + "ses.last_seen," + + "ses.count," + + "ses.playtime," + + "act.activity_index" + + FROM + UsersTable.TABLE_NAME + " u" + + LEFT_JOIN + '(' + selectBanned + ") ban on ban." + UserInfoTable.USER_UUID + "=u." + UsersTable.USER_UUID + + LEFT_JOIN + '(' + selectLatestGeolocations + ") geoloc on geoloc." + GeoInfoTable.USER_UUID + "=u." + UsersTable.USER_UUID + + LEFT_JOIN + '(' + selectSessionData + ") ses on ses." + SessionsTable.USER_UUID + "=u." + UsersTable.USER_UUID + + LEFT_JOIN + '(' + NetworkActivityIndexQueries.selectActivityIndexSQL() + ") act on u." + UsersTable.USER_UUID + "=act." + UserInfoTable.USER_UUID + + ORDER_BY + "ses.last_seen DESC LIMIT ?"; + + return db.query(new QueryStatement>(selectBaseUsers, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setBoolean(1, true); + NetworkActivityIndexQueries.setSelectActivityIndexSQLParameters(statement, 2, activeMsThreshold, date); + statement.setInt(10, xMostRecentPlayers); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List players = new ArrayList<>(); + while (set.next()) { + TablePlayer.Builder player = TablePlayer.builder() + .uuid(UUID.fromString(set.getString(UsersTable.USER_UUID))) + .name(set.getString(UsersTable.USER_NAME)) + .geolocation(set.getString(GeoInfoTable.GEOLOCATION)) + .registered(set.getLong(UsersTable.REGISTERED)) + .lastSeen(set.getLong("last_seen")) + .sessionCount(set.getInt("count")) + .playtime(set.getLong("playtime")) + .activityIndex(new ActivityIndex(set.getDouble("activity_index"), date)); + if (set.getString("banned") != null) { + player.banned(); + } + players.add(player.build()); + } + return players; + } + }); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/NewerConfigQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NewerConfigQuery.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/NewerConfigQuery.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NewerConfigQuery.java index 0c45c61f4..9c1b3adec 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/NewerConfigQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NewerConfigQuery.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.system.settings.config.Config; -import com.djrapitops.plan.system.settings.config.ConfigReader; +import com.djrapitops.plan.settings.config.Config; +import com.djrapitops.plan.settings.config.ConfigReader; +import com.djrapitops.plan.storage.database.queries.QueryStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -27,8 +27,8 @@ import java.util.Optional; import java.util.Scanner; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; -import static com.djrapitops.plan.db.sql.tables.SettingsTable.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.tables.SettingsTable.*; /** * Query to fetch a newer config from the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/NicknameQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NicknameQueries.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/NicknameQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NicknameQueries.java index a9b903088..5245690f6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/NicknameQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/NicknameQueries.java @@ -14,23 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.NicknamesTable; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.NicknamesTable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** - * Queries for {@link com.djrapitops.plan.data.store.objects.Nickname} objects. + * Queries for {@link Nickname} objects. * * @author Rsl1122 */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/PingQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/PingQueries.java new file mode 100644 index 000000000..0a820fe68 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/PingQueries.java @@ -0,0 +1,296 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.objects; + +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.gathering.domain.Ping; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.PingTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Queries for {@link WebUser} objects. + * + * @author Rsl1122 + */ +public class PingQueries { + + private PingQueries() { + /* Static method class */ + } + + /** + * Query database for all Ping data. + * + * @return Map: Player UUID - List of ping data. + */ + public static Query>> fetchAllPingData() { + String sql = SELECT + + PingTable.DATE + ',' + + PingTable.MAX_PING + ',' + + PingTable.MIN_PING + ',' + + PingTable.AVG_PING + ',' + + PingTable.USER_UUID + ',' + + PingTable.SERVER_UUID + + FROM + PingTable.TABLE_NAME; + return new QueryAllStatement>>(sql, 100000) { + @Override + public Map> processResults(ResultSet set) throws SQLException { + return extractUserPings(set); + } + }; + } + + private static Map> extractUserPings(ResultSet set) throws SQLException { + Map> userPings = new HashMap<>(); + + while (set.next()) { + UUID uuid = UUID.fromString(set.getString(PingTable.USER_UUID)); + UUID serverUUID = UUID.fromString(set.getString(PingTable.SERVER_UUID)); + long date = set.getLong(PingTable.DATE); + double avgPing = set.getDouble(PingTable.AVG_PING); + int minPing = set.getInt(PingTable.MIN_PING); + int maxPing = set.getInt(PingTable.MAX_PING); + + List pings = userPings.getOrDefault(uuid, new ArrayList<>()); + pings.add(new Ping(date, serverUUID, + minPing, + maxPing, + avgPing)); + userPings.put(uuid, pings); + } + + return userPings; + } + + /** + * Query database for Ping data of a specific player. + * + * @param playerUUID UUID of the player. + * @return List of Ping entries for this player. + */ + public static Query> fetchPingDataOfPlayer(UUID playerUUID) { + String sql = SELECT + '*' + FROM + PingTable.TABLE_NAME + + WHERE + PingTable.USER_UUID + "=?"; + + return new QueryStatement>(sql, 10000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List pings = new ArrayList<>(); + + while (set.next()) { + pings.add(new Ping( + set.getLong(PingTable.DATE), + UUID.fromString(set.getString(PingTable.SERVER_UUID)), + set.getInt(PingTable.MIN_PING), + set.getInt(PingTable.MAX_PING), + set.getDouble(PingTable.AVG_PING) + ) + ); + } + + return pings; + } + }; + } + + public static Query>> fetchPingDataOfServer(UUID serverUUID) { + String sql = SELECT + + PingTable.DATE + ',' + + PingTable.MAX_PING + ',' + + PingTable.MIN_PING + ',' + + PingTable.AVG_PING + ',' + + PingTable.USER_UUID + ',' + + PingTable.SERVER_UUID + + FROM + PingTable.TABLE_NAME + + WHERE + PingTable.SERVER_UUID + "=?"; + return new QueryStatement>>(sql, 100000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Map> processResults(ResultSet set) throws SQLException { + return extractUserPings(set); + } + }; + } + + public static Query> fetchPingDataOfServer(long after, long before, UUID serverUUID) { + String sql = SELECT + + PingTable.DATE + ", " + + PingTable.MAX_PING + ", " + + PingTable.MIN_PING + ", " + + PingTable.AVG_PING + ", " + + PingTable.SERVER_UUID + + FROM + PingTable.TABLE_NAME + + WHERE + PingTable.SERVER_UUID + "=?" + + AND + PingTable.DATE + ">=?" + + AND + PingTable.DATE + "<=?"; + return new QueryStatement>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List pings = new ArrayList<>(); + + while (set.next()) { + UUID serverUUID = UUID.fromString(set.getString(PingTable.SERVER_UUID)); + long date = set.getLong(PingTable.DATE); + double avgPing = set.getDouble(PingTable.AVG_PING); + int minPing = set.getInt(PingTable.MIN_PING); + int maxPing = set.getInt(PingTable.MAX_PING); + + pings.add(new Ping(date, serverUUID, + minPing, + maxPing, + avgPing)); + } + + return pings; + } + }; + } + + public static Query> fetchPingDataOfServerByGeolocation(UUID serverUUID) { + String selectPingOfServer = SELECT + + PingTable.MAX_PING + ", " + + PingTable.MIN_PING + ", " + + PingTable.AVG_PING + ", " + + PingTable.USER_UUID + ", " + + PingTable.SERVER_UUID + + FROM + PingTable.TABLE_NAME; + + String selectGeolocations = SELECT + + GeoInfoTable.USER_UUID + ", " + + GeoInfoTable.GEOLOCATION + ", " + + GeoInfoTable.LAST_USED + + FROM + GeoInfoTable.TABLE_NAME; + String selectLatestGeolocationDate = SELECT + + GeoInfoTable.USER_UUID + ", " + + "MAX(" + GeoInfoTable.LAST_USED + ") as m" + + FROM + GeoInfoTable.TABLE_NAME + + GROUP_BY + GeoInfoTable.USER_UUID; + + String selectPingByGeolocation = SELECT + GeoInfoTable.GEOLOCATION + + ", MIN(" + PingTable.MIN_PING + ") as minPing" + + ", MAX(" + PingTable.MAX_PING + ") as maxPing" + + ", AVG(" + PingTable.AVG_PING + ") as avgPing" + + FROM + "(" + + "(" + selectGeolocations + ") AS q1" + + INNER_JOIN + "(" + selectLatestGeolocationDate + ") AS q2 ON q1.uuid = q2.uuid" + + INNER_JOIN + '(' + selectPingOfServer + ") sp on sp." + PingTable.USER_UUID + "=q1.uuid)" + + WHERE + GeoInfoTable.LAST_USED + "=m" + + AND + "sp." + PingTable.SERVER_UUID + "=?" + + GROUP_BY + GeoInfoTable.GEOLOCATION; + + return new QueryStatement>(selectPingByGeolocation) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + // TreeMap to sort alphabetically + Map pingByGeolocation = new TreeMap<>(); + while (set.next()) { + Ping ping = new Ping( + 0L, + serverUUID, + set.getInt("minPing"), + set.getInt("maxPing"), + set.getInt("avgPing") + ); + pingByGeolocation.put(set.getString(GeoInfoTable.GEOLOCATION), ping); + } + return pingByGeolocation; + } + }; + } + + public static Query> fetchPingDataOfNetworkByGeolocation() { + String selectPingOfServer = SELECT + + PingTable.MAX_PING + ", " + + PingTable.MIN_PING + ", " + + PingTable.AVG_PING + ", " + + PingTable.USER_UUID + ", " + + PingTable.SERVER_UUID + + FROM + PingTable.TABLE_NAME; + + String selectGeolocations = SELECT + + GeoInfoTable.USER_UUID + ", " + + GeoInfoTable.GEOLOCATION + ", " + + GeoInfoTable.LAST_USED + + FROM + GeoInfoTable.TABLE_NAME; + String selectLatestGeolocationDate = SELECT + + GeoInfoTable.USER_UUID + ", " + + "MAX(" + GeoInfoTable.LAST_USED + ") as m" + + FROM + GeoInfoTable.TABLE_NAME + + GROUP_BY + GeoInfoTable.USER_UUID; + + String selectPingByGeolocation = SELECT + GeoInfoTable.GEOLOCATION + + ", MIN(" + PingTable.MIN_PING + ") as minPing" + + ", MAX(" + PingTable.MAX_PING + ") as maxPing" + + ", AVG(" + PingTable.AVG_PING + ") as avgPing" + + FROM + "(" + + "(" + selectGeolocations + ") AS q1" + + INNER_JOIN + "(" + selectLatestGeolocationDate + ") AS q2 ON q1.uuid = q2.uuid" + + INNER_JOIN + '(' + selectPingOfServer + ") sp on sp." + PingTable.USER_UUID + "=q1.uuid)" + + WHERE + GeoInfoTable.LAST_USED + "=m" + + GROUP_BY + GeoInfoTable.GEOLOCATION; + + return new QueryAllStatement>(selectPingByGeolocation) { + @Override + public Map processResults(ResultSet set) throws SQLException { + // TreeMap to sort alphabetically + Map pingByGeolocation = new TreeMap<>(); + while (set.next()) { + Ping ping = new Ping( + 0L, + null, + set.getInt("minPing"), + set.getInt("maxPing"), + set.getInt("avgPing") + ); + pingByGeolocation.put(set.getString(GeoInfoTable.GEOLOCATION), ping); + } + return pingByGeolocation; + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/ServerQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/ServerQueries.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/ServerQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/ServerQueries.java index 70b0457b2..8af4747d2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/ServerQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/ServerQueries.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.parsing.Select; -import com.djrapitops.plan.db.sql.tables.ServerTable; -import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.parsing.Select; +import com.djrapitops.plan.storage.database.sql.tables.ServerTable; import org.apache.commons.lang3.math.NumberUtils; import java.sql.PreparedStatement; @@ -29,10 +29,10 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** - * Queries for {@link com.djrapitops.plan.system.info.server.Server} objects. + * Queries for {@link Server} objects. * * @author Rsl1122 */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/ServerTablePlayersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/ServerTablePlayersQuery.java new file mode 100644 index 000000000..a2a13d804 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/ServerTablePlayersQuery.java @@ -0,0 +1,134 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.objects; + +import com.djrapitops.plan.delivery.domain.TablePlayer; +import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.queries.analysis.ActivityIndexQueries; +import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Query for displaying players on /server page players tab. + * + * @author Rsl1122 + */ +public class ServerTablePlayersQuery implements Query> { + + private final UUID serverUUID; + private final long date; + private final long activeMsThreshold; + private final int xMostRecentPlayers; + + public ServerTablePlayersQuery(UUID serverUUID, long date, long activeMsThreshold, int xMostRecentPlayers) { + this.serverUUID = serverUUID; + this.date = date; + this.activeMsThreshold = activeMsThreshold; + this.xMostRecentPlayers = xMostRecentPlayers; + } + + @Override + public List executeQuery(SQLDB db) { + String selectGeolocations = SELECT + DISTINCT + + GeoInfoTable.USER_UUID + ", " + + GeoInfoTable.GEOLOCATION + ", " + + GeoInfoTable.LAST_USED + + FROM + GeoInfoTable.TABLE_NAME; + String selectLatestGeolocationDate = SELECT + + GeoInfoTable.USER_UUID + ", " + + "MAX(" + GeoInfoTable.LAST_USED + ") as last_used_g" + + FROM + GeoInfoTable.TABLE_NAME + + GROUP_BY + GeoInfoTable.USER_UUID; + String selectLatestGeolocations = SELECT + + "g1." + GeoInfoTable.GEOLOCATION + ',' + + "g1." + GeoInfoTable.USER_UUID + + FROM + "(" + selectGeolocations + ") AS g1" + + INNER_JOIN + "(" + selectLatestGeolocationDate + ") AS g2 ON g1.uuid = g2.uuid" + + WHERE + GeoInfoTable.LAST_USED + "=last_used_g"; + + String selectSessionData = SELECT + "s." + SessionsTable.USER_UUID + ',' + + "MAX(" + SessionsTable.SESSION_END + ") as last_seen," + + "COUNT(1) as count," + + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + " s" + + WHERE + "s." + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + "s." + SessionsTable.USER_UUID; + + String selectBaseUsers = SELECT + + "u." + UsersTable.USER_UUID + ',' + + "u." + UsersTable.USER_NAME + ',' + + "u." + UsersTable.REGISTERED + ',' + + UserInfoTable.BANNED + ',' + + "geoloc." + GeoInfoTable.GEOLOCATION + ',' + + "ses.last_seen," + + "ses.count," + + "ses.playtime," + + "act.activity_index" + + FROM + UsersTable.TABLE_NAME + " u" + + INNER_JOIN + UserInfoTable.TABLE_NAME + " on u." + UsersTable.USER_UUID + "=" + UserInfoTable.TABLE_NAME + '.' + UserInfoTable.USER_UUID + + LEFT_JOIN + '(' + selectLatestGeolocations + ") geoloc on geoloc." + GeoInfoTable.USER_UUID + "=u." + UsersTable.USER_UUID + + LEFT_JOIN + '(' + selectSessionData + ") ses on ses." + SessionsTable.USER_UUID + "=u." + UsersTable.USER_UUID + + LEFT_JOIN + '(' + ActivityIndexQueries.selectActivityIndexSQL() + ") act on u." + SessionsTable.USER_UUID + "=act." + UserInfoTable.USER_UUID + + WHERE + UserInfoTable.SERVER_UUID + "=?" + + ORDER_BY + "ses.last_seen DESC LIMIT ?"; + + return db.query(new QueryStatement>(selectBaseUsers, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); // Session query + ActivityIndexQueries.setSelectActivityIndexSQLParameters(statement, 2, activeMsThreshold, serverUUID, date); + statement.setString(13, serverUUID.toString()); // Session query + statement.setInt(14, xMostRecentPlayers); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List players = new ArrayList<>(); + while (set.next()) { + TablePlayer.Builder player = TablePlayer.builder() + .uuid(UUID.fromString(set.getString(UsersTable.USER_UUID))) + .name(set.getString(UsersTable.USER_NAME)) + .geolocation(set.getString(GeoInfoTable.GEOLOCATION)) + .registered(set.getLong(UsersTable.REGISTERED)) + .lastSeen(set.getLong("last_seen")) + .sessionCount(set.getInt("count")) + .playtime(set.getLong("playtime")) + .activityIndex(new ActivityIndex(set.getDouble("activity_index"), date)); + if (set.getBoolean(UserInfoTable.BANNED)) { + player.banned(); + } + players.add(player.build()); + } + return players; + } + }); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java new file mode 100644 index 000000000..7cd034b1b --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java @@ -0,0 +1,832 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.objects; + +import com.djrapitops.plan.delivery.domain.DateHolder; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; +import com.djrapitops.plan.gathering.domain.GMTimes; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.gathering.domain.WorldTimes; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Queries for {@link Session} objects. + * + * @author Rsl1122 + */ +public class SessionQueries { + + private SessionQueries() { + /* Static method class */ + } + + private static final String SELECT_SESSIONS_STATEMENT = SELECT + + "s." + SessionsTable.ID + ',' + + "s." + SessionsTable.USER_UUID + ',' + + "s." + SessionsTable.SERVER_UUID + ',' + + "u." + UsersTable.USER_NAME + " as name," + + "u_info." + UserInfoTable.REGISTERED + " as registered," + + "server." + ServerTable.NAME + " as server_name," + + SessionsTable.SESSION_START + ',' + + SessionsTable.SESSION_END + ',' + + SessionsTable.MOB_KILLS + ',' + + SessionsTable.DEATHS + ',' + + SessionsTable.AFK_TIME + ',' + + WorldTimesTable.SURVIVAL + ',' + + WorldTimesTable.CREATIVE + ',' + + WorldTimesTable.ADVENTURE + ',' + + WorldTimesTable.SPECTATOR + ',' + + WorldTable.NAME + ',' + + KillsTable.VICTIM_UUID + ',' + + "v." + UsersTable.USER_NAME + " as victim_name, " + + KillsTable.DATE + ',' + + KillsTable.WEAPON + + FROM + SessionsTable.TABLE_NAME + " s" + + INNER_JOIN + UsersTable.TABLE_NAME + " u on u." + UsersTable.USER_UUID + "=s." + SessionsTable.USER_UUID + + INNER_JOIN + ServerTable.TABLE_NAME + " server on server." + ServerTable.SERVER_UUID + "=s." + SessionsTable.SERVER_UUID + + LEFT_JOIN + UserInfoTable.TABLE_NAME + " u_info on (u_info." + UserInfoTable.USER_UUID + "=s." + SessionsTable.USER_UUID + AND + "u_info." + UserInfoTable.SERVER_UUID + "=s." + SessionsTable.SERVER_UUID + ')' + + LEFT_JOIN + KillsTable.TABLE_NAME + " ON " + "s." + SessionsTable.ID + '=' + KillsTable.TABLE_NAME + '.' + KillsTable.SESSION_ID + + LEFT_JOIN + UsersTable.TABLE_NAME + " v on v." + UsersTable.USER_UUID + '=' + KillsTable.VICTIM_UUID + + INNER_JOIN + WorldTimesTable.TABLE_NAME + " ON s." + SessionsTable.ID + '=' + WorldTimesTable.TABLE_NAME + '.' + WorldTimesTable.SESSION_ID + + INNER_JOIN + WorldTable.TABLE_NAME + " ON " + WorldTimesTable.TABLE_NAME + '.' + WorldTimesTable.WORLD_ID + '=' + WorldTable.TABLE_NAME + '.' + WorldTable.ID; + + private static final String ORDER_BY_SESSION_START_DESC = ORDER_BY + SessionsTable.SESSION_START + " DESC"; + + /** + * Query the database for Session data without kill, death or world data. + * + * @return Multimap: Server UUID - (Player UUID - List of sessions) + */ + public static Query>>> fetchAllSessionsWithoutKillOrWorldData() { + String sql = SELECT + + SessionsTable.ID + ',' + + SessionsTable.USER_UUID + ',' + + SessionsTable.SERVER_UUID + ',' + + SessionsTable.SESSION_START + ',' + + SessionsTable.SESSION_END + ',' + + SessionsTable.DEATHS + ',' + + SessionsTable.MOB_KILLS + ',' + + SessionsTable.AFK_TIME + + FROM + SessionsTable.TABLE_NAME; + + return new QueryAllStatement>>>(sql, 20000) { + @Override + public Map>> processResults(ResultSet set) throws SQLException { + Map>> map = new HashMap<>(); + while (set.next()) { + UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID)); + UUID uuid = UUID.fromString(set.getString(SessionsTable.USER_UUID)); + + Map> sessionsByUser = map.getOrDefault(serverUUID, new HashMap<>()); + List sessions = sessionsByUser.getOrDefault(uuid, new ArrayList<>()); + + long start = set.getLong(SessionsTable.SESSION_START); + long end = set.getLong(SessionsTable.SESSION_END); + + int deaths = set.getInt(SessionsTable.DEATHS); + int mobKills = set.getInt(SessionsTable.MOB_KILLS); + int id = set.getInt(SessionsTable.ID); + + long timeAFK = set.getLong(SessionsTable.AFK_TIME); + + sessions.add(new Session(id, uuid, serverUUID, start, end, mobKills, deaths, timeAFK)); + + sessionsByUser.put(uuid, sessions); + map.put(serverUUID, sessionsByUser); + } + return map; + } + }; + } + + /** + * Query the database for Session data with kill, death or world data. + * + * @return List of sessions + */ + public static Query> fetchAllSessions() { + String sql = SELECT_SESSIONS_STATEMENT + + ORDER_BY_SESSION_START_DESC; + return new QueryAllStatement>(sql, 50000) { + @Override + public List processResults(ResultSet set) throws SQLException { + return extractDataFromSessionSelectStatement(set); + } + }; + } + + /** + * Query the database for Session data of a server with kill and world data. + * + * @param serverUUID UUID of the Plan server. + * @return Map: Player UUID - List of sessions on the server. + */ + public static Query>> fetchSessionsOfServer(UUID serverUUID) { + return db -> SessionsMutator.sortByPlayers(db.query(fetchSessionsOfServerFlat(serverUUID))); + } + + public static QueryStatement> fetchSessionsOfServerFlat(UUID serverUUID) { + String sql = SELECT_SESSIONS_STATEMENT + + WHERE + "s." + SessionsTable.SERVER_UUID + "=?" + + ORDER_BY_SESSION_START_DESC; + return new QueryStatement>(sql, 50000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + return extractDataFromSessionSelectStatement(set); + } + }; + } + + /** + * Query the database for Session data of a player with kill and world data. + * + * @param playerUUID UUID of the Player. + * @return Map: Server UUID - List of sessions on the server. + */ + public static Query>> fetchSessionsOfPlayer(UUID playerUUID) { + String sql = SELECT_SESSIONS_STATEMENT + + WHERE + "s." + SessionsTable.USER_UUID + "=?" + + ORDER_BY_SESSION_START_DESC; + return new QueryStatement>>(sql, 50000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + } + + @Override + public Map> processResults(ResultSet set) throws SQLException { + List sessions = extractDataFromSessionSelectStatement(set); + return SessionsMutator.sortByServers(sessions); + } + }; + } + + private static List extractDataFromSessionSelectStatement(ResultSet set) throws SQLException { + // Server UUID - Player UUID - Session Start - Session + Map>> tempSessionMap = new HashMap<>(); + + // Utilities + String[] gms = GMTimes.getGMKeyArray(); + Comparator dateColderRecentComparator = new DateHolderRecentComparator(); + Comparator longRecentComparator = (one, two) -> Long.compare(two, one); // Descending order, most recent first. + + while (set.next()) { + UUID serverUUID = UUID.fromString(set.getString(SessionsTable.SERVER_UUID)); + Map> serverSessions = tempSessionMap.getOrDefault(serverUUID, new HashMap<>()); + + UUID playerUUID = UUID.fromString(set.getString(SessionsTable.USER_UUID)); + SortedMap playerSessions = serverSessions.getOrDefault(playerUUID, new TreeMap<>(longRecentComparator)); + + long sessionStart = set.getLong(SessionsTable.SESSION_START); + // id, uuid, serverUUID, sessionStart, sessionEnd, mobKills, deaths, afkTime + Session session = playerSessions.getOrDefault(sessionStart, new Session( + set.getInt(SessionsTable.ID), + playerUUID, + serverUUID, + sessionStart, + set.getLong(SessionsTable.SESSION_END), + set.getInt(SessionsTable.MOB_KILLS), + set.getInt(SessionsTable.DEATHS), + set.getLong(SessionsTable.AFK_TIME) + )); + + WorldTimes worldTimes = session.getValue(SessionKeys.WORLD_TIMES).orElse(new WorldTimes()); + String worldName = set.getString(WorldTable.NAME); + + if (!worldTimes.contains(worldName)) { + Map gmMap = new HashMap<>(); + gmMap.put(gms[0], set.getLong(WorldTimesTable.SURVIVAL)); + gmMap.put(gms[1], set.getLong(WorldTimesTable.CREATIVE)); + gmMap.put(gms[2], set.getLong(WorldTimesTable.ADVENTURE)); + gmMap.put(gms[3], set.getLong(WorldTimesTable.SPECTATOR)); + GMTimes gmTimes = new GMTimes(gmMap); + worldTimes.setGMTimesForWorld(worldName, gmTimes); + } + + String victimName = set.getString("victim_name"); + if (victimName != null) { + UUID victim = UUID.fromString(set.getString(KillsTable.VICTIM_UUID)); + long date = set.getLong(KillsTable.DATE); + String weapon = set.getString(KillsTable.WEAPON); + List playerKills = session.getPlayerKills(); + playerKills.add(new PlayerKill(victim, weapon, date, victimName)); + playerKills.sort(dateColderRecentComparator); + } + + session.putRawData(SessionKeys.NAME, set.getString("name")); + session.putRawData(SessionKeys.SERVER_NAME, set.getString("server_name")); + + session.setAsFirstSessionIfMatches(set.getLong("registered")); + + playerSessions.put(sessionStart, session); + serverSessions.put(playerUUID, playerSessions); + tempSessionMap.put(serverUUID, serverSessions); + } + + return tempSessionMap.values().stream() + .map(Map::values) + .flatMap(Collection::stream) + .map(SortedMap::values) + .flatMap(Collection::stream) + .sorted(dateColderRecentComparator) // Disorder arises + .collect(Collectors.toList()); + } + + public static Query> fetchServerSessionsWithoutKillOrWorldData(long after, long before, UUID serverUUID) { + String sql = SELECT + + SessionsTable.ID + ',' + + SessionsTable.USER_UUID + ',' + + SessionsTable.SESSION_START + ',' + + SessionsTable.SESSION_END + ',' + + SessionsTable.DEATHS + ',' + + SessionsTable.MOB_KILLS + ',' + + SessionsTable.AFK_TIME + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + + return new QueryStatement>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List sessions = new ArrayList<>(); + while (set.next()) { + UUID uuid = UUID.fromString(set.getString(SessionsTable.USER_UUID)); + long start = set.getLong(SessionsTable.SESSION_START); + long end = set.getLong(SessionsTable.SESSION_END); + + int deaths = set.getInt(SessionsTable.DEATHS); + int mobKills = set.getInt(SessionsTable.MOB_KILLS); + int id = set.getInt(SessionsTable.ID); + + long timeAFK = set.getLong(SessionsTable.AFK_TIME); + + sessions.add(new Session(id, uuid, serverUUID, start, end, mobKills, deaths, timeAFK)); + } + return sessions; + } + }; + } + + private static Query fetchLatestSessionStartLimitForServer(UUID serverUUID, int limit) { + String sql = SELECT + SessionsTable.SESSION_START + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + ORDER_BY_SESSION_START_DESC + " LIMIT ?"; + + return new QueryStatement(sql, limit) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setInt(2, limit); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + Long last = null; + while (set.next()) { + last = set.getLong(SessionsTable.SESSION_START); + } + return last; + } + }; + } + + private static Query fetchLatestSessionStartLimit(int limit) { + String sql = SELECT + SessionsTable.SESSION_START + FROM + SessionsTable.TABLE_NAME + + ORDER_BY_SESSION_START_DESC + " LIMIT ?"; + + return new QueryStatement(sql, limit) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, limit); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + Long last = null; + while (set.next()) { + last = set.getLong(SessionsTable.SESSION_START); + } + return last; + } + }; + } + + public static Query> fetchLatestSessionsOfServer(UUID serverUUID, int limit) { + String sql = SELECT_SESSIONS_STATEMENT + + WHERE + "s." + SessionsTable.SERVER_UUID + "=?" + + AND + "s." + SessionsTable.SESSION_START + ">=?" + + ORDER_BY_SESSION_START_DESC; + + return db -> { + Long start = db.query(fetchLatestSessionStartLimitForServer(serverUUID, limit)); + return db.query(new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, start != null ? start : 0L); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + return extractDataFromSessionSelectStatement(set); + } + }); + }; + } + + public static Query> fetchLatestSessions(int limit) { + String sql = SELECT_SESSIONS_STATEMENT + // Fix for "First Session" icons in the Most recent sessions on network page + .replace(LEFT_JOIN + UserInfoTable.TABLE_NAME + " u_info on (u_info." + UserInfoTable.USER_UUID + "=s." + SessionsTable.USER_UUID + AND + "u_info." + UserInfoTable.SERVER_UUID + "=s." + SessionsTable.SERVER_UUID + ')', "") + .replace("u_info", "u") + + WHERE + "s." + SessionsTable.SESSION_START + ">=?" + + ORDER_BY_SESSION_START_DESC; + return db -> { + Long start = db.query(fetchLatestSessionStartLimit(limit)); + return db.query(new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, start != null ? start : 0L); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + return extractDataFromSessionSelectStatement(set); + } + }); + }; + } + + public static Query sessionCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "COUNT(1) as count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + public static Query sessionCount(long after, long before) { + String sql = SELECT + "COUNT(1) as count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, after); + statement.setLong(2, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + /** + * Query session count for each day within range on a server. + * + * @param after After epoch ms + * @param before Before epoch ms + * @param timeZoneOffset Offset in ms to determine start of day. + * @param serverUUID UUID of the Plan server. + * @return Map - Epoch ms (Start of day at 0 AM, no offset) : Session count of that day + */ + public static Query> sessionCountPerDay(long after, long before, long timeZoneOffset, UUID serverUUID) { + return database -> { + Sql sql = database.getSql(); + String selectSessionsPerDay = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) + + "*1000 as date," + + "COUNT(1) as session_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + "date"; + + return database.query(new QueryStatement>(selectSessionsPerDay, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + statement.setString(4, serverUUID.toString()); + } + + @Override + public NavigableMap processResults(ResultSet set) throws SQLException { + NavigableMap uniquePerDay = new TreeMap<>(); + while (set.next()) { + uniquePerDay.put(set.getLong("date"), set.getInt("session_count")); + } + return uniquePerDay; + } + }); + }; + } + + public static Query playtime(long after, long before, UUID serverUUID) { + String sql = SELECT + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("playtime") : 0L; + } + }; + } + + public static Query> playtimeOfPlayer(long after, long before, UUID playerUUID) { + String sql = SELECT + SessionsTable.SERVER_UUID + ",SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.USER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?" + + GROUP_BY + SessionsTable.SERVER_UUID; + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map playtimeOfPlayer = new HashMap<>(); + while (set.next()) { + playtimeOfPlayer.put(UUID.fromString(set.getString(SessionsTable.SERVER_UUID)), set.getLong("playtime")); + } + return playtimeOfPlayer; + } + }; + } + + public static Query playtime(long after, long before) { + String sql = SELECT + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, after); + statement.setLong(2, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("playtime") : 0L; + } + }; + } + + /** + * Query playtime for each day within range on a server. + * + * @param after After epoch ms + * @param before Before epoch ms + * @param timeZoneOffset Offset in ms to determine start of day. + * @param serverUUID UUID of the Plan server. + * @return Map - Epoch ms (Start of day at 0 AM, no offset) : Playtime of that day + */ + public static Query> playtimePerDay(long after, long before, long timeZoneOffset, UUID serverUUID) { + return database -> { + Sql sql = database.getSql(); + String selectPlaytimePerDay = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) + + "*1000 as date," + + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + "date"; + + return database.query(new QueryStatement>(selectPlaytimePerDay, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + statement.setString(4, serverUUID.toString()); + } + + @Override + public NavigableMap processResults(ResultSet set) throws SQLException { + NavigableMap uniquePerDay = new TreeMap<>(); + while (set.next()) { + uniquePerDay.put(set.getLong("date"), set.getLong("playtime")); + } + return uniquePerDay; + } + }); + }; + } + + public static Query averagePlaytimePerDay(long after, long before, long timeZoneOffset, UUID serverUUID) { + return database -> { + Sql sql = database.getSql(); + String selectPlaytimePerDay = SELECT + + sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) + + "*1000 as date," + + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + "date"; + String selectAverage = SELECT + "AVG(playtime) as average" + FROM + '(' + selectPlaytimePerDay + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, timeZoneOffset); + statement.setLong(2, before); + statement.setLong(3, after); + statement.setString(4, serverUUID.toString()); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query averagePlaytimePerPlayer(long after, long before, UUID serverUUID) { + return database -> { + String selectPlaytimePerPlayer = SELECT + + SessionsTable.USER_UUID + "," + + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + SessionsTable.USER_UUID; + String selectAverage = SELECT + "AVG(playtime) as average" + FROM + '(' + selectPlaytimePerPlayer + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + statement.setString(3, serverUUID.toString()); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + /** + * Fetch average playtime per ALL players. + * + * @param after After epoch ms + * @param before Before epoch ms + * @return Average ms played / player, calculated with grouped sums from sessions table. + */ + public static Query averagePlaytimePerPlayer(long after, long before) { + return database -> { + String selectPlaytimePerPlayer = SELECT + + SessionsTable.USER_UUID + "," + + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + GROUP_BY + SessionsTable.USER_UUID; + String selectAverage = SELECT + "AVG(playtime) as average" + FROM + '(' + selectPlaytimePerPlayer + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query averageAfkPerPlayer(long after, long before, UUID serverUUID) { + return database -> { + String selectAfkPerPlayer = SELECT + + SessionsTable.USER_UUID + "," + + "SUM(" + SessionsTable.AFK_TIME + ") as afk" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + SessionsTable.USER_UUID; + String selectAverage = SELECT + "AVG(afk) as average" + FROM + '(' + selectAfkPerPlayer + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + statement.setString(3, serverUUID.toString()); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + /** + * Fetch average Afk per ALL players. + * + * @param after After epoch ms + * @param before Before epoch ms + * @return Average ms afk / player, calculated with grouped sums from sessions table. + */ + public static Query averageAfkPerPlayer(long after, long before) { + return database -> { + String selectAfkPerPlayer = SELECT + + SessionsTable.USER_UUID + "," + + "SUM(" + SessionsTable.AFK_TIME + ") as afk" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + GROUP_BY + SessionsTable.USER_UUID; + String selectAverage = SELECT + "AVG(afk) as average" + FROM + '(' + selectAfkPerPlayer + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query afkTime(long after, long before, UUID serverUUID) { + String sql = SELECT + "SUM(" + SessionsTable.AFK_TIME + ") as afk_time" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("afk_time") : 0L; + } + }; + } + + public static Query afkTime(long after, long before) { + String sql = SELECT + "SUM(" + SessionsTable.AFK_TIME + ") as afk_time" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, after); + statement.setLong(2, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("afk_time") : 0L; + } + }; + } + + public static Query> playtimePerServer(long after, long before) { + String sql = SELECT + + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime," + + ServerTable.NAME + + FROM + SessionsTable.TABLE_NAME + + INNER_JOIN + ServerTable.TABLE_NAME + " s on s." + ServerTable.SERVER_UUID + '=' + SessionsTable.TABLE_NAME + '.' + SessionsTable.SERVER_UUID + + WHERE + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?" + + GROUP_BY + ServerTable.NAME; + return new QueryStatement>(sql, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, after); + statement.setLong(2, before); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map playtimePerServer = new HashMap<>(); + while (set.next()) { + playtimePerServer.put(set.getString(ServerTable.NAME), set.getLong("playtime")); + } + return playtimePerServer; + } + }; + } + + public static Query lastSeen(UUID playerUUID, UUID serverUUID) { + String sql = SELECT + "MAX(" + SessionsTable.SESSION_END + ") as last_seen" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.USER_UUID + "=?" + + AND + SessionsTable.SERVER_UUID + "=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, playerUUID.toString()); + statement.setString(2, serverUUID.toString()); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("last_seen") : 0; + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/TPSQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/TPSQueries.java new file mode 100644 index 000000000..4361223d8 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/TPSQueries.java @@ -0,0 +1,280 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.queries.objects; + +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.gathering.domain.builders.TPSBuilder; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.parsing.Select; +import com.djrapitops.plan.storage.database.sql.tables.ServerTable; +import com.djrapitops.plugin.api.TimeAmount; +import org.apache.commons.text.TextStringBuilder; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.tables.TPSTable.*; + +/** + * Queries for {@link com.djrapitops.plan.gathering.domain.TPS} objects. + * + * @author Rsl1122 + */ +public class TPSQueries { + + private TPSQueries() { + /* Static method class */ + } + + public static Query> fetchTPSDataOfServer(UUID serverUUID) { + String sql = Select.all(TABLE_NAME) + .where(SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID) + .toString(); + + return new QueryStatement>(sql, 50000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List data = new ArrayList<>(); + while (set.next()) { + + com.djrapitops.plan.gathering.domain.TPS tps = extractTPS(set); + + data.add(tps); + } + return data; + } + }; + } + + public static TPS extractTPS(ResultSet set) throws SQLException { + return TPSBuilder.get() + .date(set.getLong(DATE)) + .tps(set.getDouble(TPS)) + .playersOnline(set.getInt(PLAYERS_ONLINE)) + .usedCPU(set.getDouble(CPU_USAGE)) + .usedMemory(set.getLong(RAM_USAGE)) + .entities(set.getInt(ENTITIES)) + .chunksLoaded(set.getInt(CHUNKS)) + .freeDiskSpace(set.getLong(FREE_DISK)) + .toTPS(); + } + + public static Query> fetchTPSDataOfServer(long after, long before, UUID serverUUID) { + String sql = Select.all(TABLE_NAME) + .where(SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID) + .and(DATE + ">=?").and(DATE + "<=?") + .toString(); + + return new QueryStatement>(sql, 50000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List data = new ArrayList<>(); + while (set.next()) { + TPS tps = extractTPS(set); + data.add(tps); + } + return data; + } + }; + } + + public static Query>> fetchPlayersOnlineOfServer(long after, long before, UUID serverUUID) { + String sql = SELECT + ServerTable.SERVER_UUID + ',' + DATE + ',' + PLAYERS_ONLINE + + FROM + TABLE_NAME + + INNER_JOIN + ServerTable.TABLE_NAME + " on " + ServerTable.TABLE_NAME + '.' + ServerTable.SERVER_ID + '=' + SERVER_ID + + WHERE + ServerTable.SERVER_UUID + "=?" + + AND + DATE + "?"; + return new QueryStatement>>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, before); + statement.setLong(3, after); + } + + @Override + public List> processResults(ResultSet set) throws SQLException { + List> ofServer = new ArrayList<>(); + + while (set.next()) { + ofServer.add(new DateObj<>(set.getLong(DATE), set.getInt(PLAYERS_ONLINE))); + } + + return ofServer; + } + }; + } + + public static Query>> fetchTPSDataOfAllServersBut(long after, long before, UUID leaveOut) { + String sql = SELECT + '*' + + FROM + TABLE_NAME + + INNER_JOIN + ServerTable.TABLE_NAME + " on " + ServerTable.TABLE_NAME + '.' + ServerTable.SERVER_ID + '=' + SERVER_ID + + WHERE + ServerTable.SERVER_UUID + "!=?" + + AND + ServerTable.INSTALLED + "=?" + + AND + DATE + "?"; + return new QueryStatement>>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, leaveOut.toString()); + statement.setBoolean(2, true); + statement.setLong(3, before); + statement.setLong(4, after); + } + + @Override + public Map> processResults(ResultSet set) throws SQLException { + Map> byServer = new HashMap<>(); + while (set.next()) { + UUID serverUUID = UUID.fromString(set.getString(ServerTable.SERVER_UUID)); + List ofServer = byServer.getOrDefault(serverUUID, new ArrayList<>()); + + ofServer.add(extractTPS(set)); + + byServer.put(serverUUID, ofServer); + } + return byServer; + } + }; + } + + public static Query>> fetchPlayerOnlineDataOfServers(Collection servers) { + if (servers.isEmpty()) { + return db -> new HashMap<>(); + } + + TextStringBuilder sql = new TextStringBuilder(SELECT); + sql.append(SERVER_ID).append(',') + .append(DATE).append(',') + .append(PLAYERS_ONLINE) + .append(FROM).append(TABLE_NAME) + .append(WHERE).append(DATE).append(">").append(System.currentTimeMillis() - TimeAmount.WEEK.toMillis(2L)) + .append(AND).append('('); + sql.appendWithSeparators(servers.stream().map(server -> SERVER_ID + "=" + server.getId()).iterator(), OR); + sql.append(')'); + + return new QueryAllStatement>>(sql.toString(), 10000) { + @Override + public Map> processResults(ResultSet set) throws SQLException { + Map> map = new HashMap<>(); + while (set.next()) { + int serverID = set.getInt(SERVER_ID); + int playersOnline = set.getInt(PLAYERS_ONLINE); + long date = set.getLong(DATE); + + List tpsList = map.getOrDefault(serverID, new ArrayList<>()); + + TPS tps = TPSBuilder.get().date(date) + .playersOnline(playersOnline) + .toTPS(); + tpsList.add(tps); + + map.put(serverID, tpsList); + } + return map; + } + }; + } + + public static Query>> fetchPeakPlayerCount(UUID serverUUID, long afterDate) { + String subQuery = '(' + SELECT + "MAX(" + PLAYERS_ONLINE + ')' + FROM + TABLE_NAME + WHERE + SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID + + AND + DATE + ">= ?)"; + String sql = SELECT + + DATE + ',' + PLAYERS_ONLINE + + FROM + TABLE_NAME + + WHERE + SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID + + AND + DATE + ">= ?" + + AND + PLAYERS_ONLINE + "=" + subQuery + + ORDER_BY + DATE + " DESC LIMIT 1"; + + return new QueryStatement>>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, afterDate); + statement.setString(3, serverUUID.toString()); + statement.setLong(4, afterDate); + } + + @Override + public Optional> processResults(ResultSet set) throws SQLException { + if (set.next()) { + return Optional.of(new DateObj<>( + set.getLong(DATE), + set.getInt(PLAYERS_ONLINE) + )); + } + return Optional.empty(); + } + }; + } + + public static Query>> fetchAllTimePeakPlayerCount(UUID serverUUID) { + return fetchPeakPlayerCount(serverUUID, 0); + } + + public static Query> fetchLatestTPSEntryForServer(UUID serverUUID) { + String sql = SELECT + "*" + + FROM + TABLE_NAME + + WHERE + SERVER_ID + '=' + ServerTable.STATEMENT_SELECT_SERVER_ID + + ORDER_BY + DATE + " DESC LIMIT 1"; + + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Optional processResults(ResultSet set) throws SQLException { + if (set.next()) { + return Optional.of(TPSBuilder.get() + .date(set.getLong(DATE)) + .tps(set.getDouble(TPS)) + .playersOnline(set.getInt(PLAYERS_ONLINE)) + .usedCPU(set.getDouble(CPU_USAGE)) + .usedMemory(set.getLong(RAM_USAGE)) + .entities(set.getInt(ENTITIES)) + .chunksLoaded(set.getInt(CHUNKS)) + .freeDiskSpace(set.getLong(FREE_DISK)) + .toTPS()); + } + return Optional.empty(); + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserIdentifierQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserIdentifierQueries.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserIdentifierQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserIdentifierQueries.java index 1987c7c0e..57aff8abe 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserIdentifierQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserIdentifierQueries.java @@ -14,22 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.parsing.Select; -import com.djrapitops.plan.db.sql.tables.NicknamesTable; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; -import com.djrapitops.plan.db.sql.tables.UsersTable; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.parsing.Select; +import com.djrapitops.plan.storage.database.sql.tables.NicknamesTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Queries for fetching different user identifiers in the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserInfoQueries.java similarity index 79% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserInfoQueries.java index 9a49643de..74dd84c76 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/UserInfoQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/UserInfoQueries.java @@ -14,23 +14,23 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.data.container.UserInfo; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; +import com.djrapitops.plan.gathering.domain.UserInfo; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** - * Queries for {@link com.djrapitops.plan.data.container.UserInfo} objects. + * Queries for {@link UserInfo} objects. * * @author Rsl1122 */ @@ -182,4 +182,35 @@ public class UserInfoQueries { } }; } + + public static Query> fetchRegisterDates(long after, long before, UUID serverUUID) { + String sql = SELECT + + UserInfoTable.USER_UUID + ',' + + UserInfoTable.REGISTERED + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.SERVER_UUID + "=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.REGISTERED + "<=?"; + + return new QueryStatement>(sql, 1000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map registerDates = new HashMap<>(); + while (set.next()) { + registerDates.put( + UUID.fromString(set.getString(UserInfoTable.USER_UUID)), + set.getLong(UserInfoTable.REGISTERED) + ); + } + return registerDates; + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/WebUserQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WebUserQueries.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/WebUserQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WebUserQueries.java index 797a2b96b..96ab18974 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/WebUserQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WebUserQueries.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.SecurityTable; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.SecurityTable; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -29,10 +29,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** - * Queries for {@link com.djrapitops.plan.data.WebUser} objects. + * Queries for {@link WebUser} objects. * * @author Rsl1122 */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/WorldTimesQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WorldTimesQueries.java similarity index 76% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/WorldTimesQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WorldTimesQueries.java index 53dd344fb..2b37febb5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/WorldTimesQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/WorldTimesQueries.java @@ -14,14 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.objects; +package com.djrapitops.plan.storage.database.queries.objects; -import com.djrapitops.plan.data.time.GMTimes; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.sql.tables.WorldTable; -import com.djrapitops.plan.db.sql.tables.WorldTimesTable; +import com.djrapitops.plan.gathering.domain.GMTimes; +import com.djrapitops.plan.gathering.domain.WorldTimes; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.WorldTable; +import com.djrapitops.plan.storage.database.sql.tables.WorldTimesTable; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -30,10 +31,10 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** - * Queries for {@link com.djrapitops.plan.data.time.WorldTimes} objects. + * Queries for {@link WorldTimes} objects. * * @author Rsl1122 */ @@ -169,4 +170,31 @@ public class WorldTimesQueries { } return new GMTimes(gmMap); } + + public static Query fetchGMTimes(long after, long before, UUID serverUUID) { + String sql = SELECT + + "SUM(" + WorldTimesTable.SURVIVAL + ") as SURVIVAL," + + "SUM(" + WorldTimesTable.CREATIVE + ") as CREATIVE," + + "SUM(" + WorldTimesTable.ADVENTURE + ") as ADVENTURE," + + "SUM(" + WorldTimesTable.SPECTATOR + ") as SPECTATOR" + + FROM + WorldTimesTable.TABLE_NAME + " w1" + + INNER_JOIN + SessionsTable.TABLE_NAME + " s1 on s1." + SessionsTable.ID + '=' + WorldTimesTable.SESSION_ID + + WHERE + "w1." + WorldTimesTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?"; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public GMTimes processResults(ResultSet set) throws SQLException { + return set.next() ? extractGMTimes(set, GMTimes.getGMKeyArray()) : new GMTimes(); + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/H2SchemaQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/H2SchemaQueries.java similarity index 63% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/H2SchemaQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/H2SchemaQueries.java index ab42de693..d07bd7247 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/H2SchemaQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/H2SchemaQueries.java @@ -14,15 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.schema; +package com.djrapitops.plan.storage.database.queries.schema; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.access.Query; +import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import static com.djrapitops.plan.db.sql.parsing.Sql.SELECT; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Static method class for H2 Schema related queries. @@ -56,4 +58,23 @@ public class H2SchemaQueries { } }; } + + public static Query columnVarcharLength(String table, String column) { + String sql = SELECT + "CHARACTER_MAXIMUM_LENGTH" + + FROM + "INFORMATION_SCHEMA.COLUMNS " + + WHERE + "TABLE_NAME=? AND COLUMN_NAME=?"; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, table); + statement.setString(2, column); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("CHARACTER_MAXIMUM_LENGTH") : Integer.MAX_VALUE; + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/MySQLSchemaQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/MySQLSchemaQueries.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/MySQLSchemaQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/MySQLSchemaQueries.java index ae41e6fc8..4978f0166 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/MySQLSchemaQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/MySQLSchemaQueries.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.schema; +package com.djrapitops.plan.storage.database.queries.schema; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; +import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -26,7 +26,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Static method class for MySQL Schema related queries. @@ -109,6 +109,25 @@ public class MySQLSchemaQueries { }; } + public static Query columnVarcharLength(String table, String column) { + String sql = SELECT + "CHARACTER_MAXIMUM_LENGTH" + + FROM + "information_schema.COLUMNS" + + WHERE + "TABLE_NAME=? AND COLUMN_NAME=? AND TABLE_SCHEMA=DATABASE()"; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, table); + statement.setString(2, column); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("CHARACTER_MAXIMUM_LENGTH") : Integer.MAX_VALUE; + } + }; + } + /** * Represents a FOREIGN KEY constraint in a MySQL database. * diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/SQLiteSchemaQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/SQLiteSchemaQueries.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/SQLiteSchemaQueries.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/SQLiteSchemaQueries.java index 617174966..c252b3f4e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/SQLiteSchemaQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/SQLiteSchemaQueries.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.schema; +package com.djrapitops.plan.storage.database.queries.schema; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Static method class for SQLite Schema related queries. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/SessionIDServerIDRelationQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/SessionIDServerIDRelationQuery.java similarity index 75% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/SessionIDServerIDRelationQuery.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/SessionIDServerIDRelationQuery.java index 37f99a29e..361c37510 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/schema/SessionIDServerIDRelationQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/schema/SessionIDServerIDRelationQuery.java @@ -14,25 +14,25 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.queries.schema; +package com.djrapitops.plan.storage.database.queries.schema; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; -import static com.djrapitops.plan.db.sql.parsing.Sql.SELECT; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.SELECT; /** * Query to fetch server id for each session, used by 2 patches. * * @author Rsl1122 - * @see com.djrapitops.plan.db.patches.KillsServerIDPatch - * @see com.djrapitops.plan.db.patches.WorldTimesSeverIDPatch + * @see com.djrapitops.plan.storage.database.transactions.patches.KillsServerIDPatch + * @see com.djrapitops.plan.storage.database.transactions.patches.WorldTimesSeverIDPatch */ public class SessionIDServerIDRelationQuery extends QueryAllStatement> { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/CreateTableParser.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/CreateTableParser.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/CreateTableParser.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/CreateTableParser.java index 9eaca9b9b..3536b4d47 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/CreateTableParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/CreateTableParser.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.parsing; +package com.djrapitops.plan.storage.database.sql.parsing; -import com.djrapitops.plan.db.DBType; +import com.djrapitops.plan.storage.database.DBType; import com.djrapitops.plugin.utilities.Verify; /** diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Insert.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Insert.java similarity index 96% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Insert.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Insert.java index 85ae66629..eae98d4e4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Insert.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Insert.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.parsing; +package com.djrapitops.plan.storage.database.sql.parsing; public class Insert extends SqlParser { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Select.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Select.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Select.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Select.java index e14e43cae..91f29b242 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Select.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Select.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.parsing; +package com.djrapitops.plan.storage.database.sql.parsing; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; -import static com.djrapitops.plan.db.sql.parsing.Sql.SELECT; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.SELECT; public class Select extends WhereParser { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Sql.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Sql.java new file mode 100644 index 000000000..7177c22e2 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Sql.java @@ -0,0 +1,155 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.sql.parsing; + +import java.util.concurrent.TimeUnit; + +/** + * Duplicate String reducing utility class for SQL language Strings. + */ +public abstract class Sql { + public static final String ID = "id"; + public static final String P_UUID = "uuid"; + + public static final String INT = "integer"; + public static final String DOUBLE = "double"; + public static final String LONG = "bigint"; + public static final String BOOL = "boolean"; + + public static final String SELECT = "SELECT "; + public static final String DISTINCT = "DISTINCT "; + public static final String FROM = " FROM "; + public static final String DELETE_FROM = "DELETE" + FROM; + public static final String WHERE = " WHERE "; + public static final String GROUP_BY = " GROUP BY "; + public static final String ORDER_BY = " ORDER BY "; + public static final String INNER_JOIN = " JOIN "; + public static final String LEFT_JOIN = " LEFT JOIN "; + public static final String UNION = " UNION "; + public static final String AND = " AND "; + public static final String OR = " OR "; + public static final String IS_NULL = " IS NULL"; + public static final String IS_NOT_NULL = " IS NOT NULL"; + + public static String varchar(int length) { + return "varchar(" + length + ')'; + } + + /** + * Parse day of week to epoch ms. + *

    + * 1st of January 1970 (Epoch) is Thursday (-2). + * + * @param day 1 = Sunday, 2 = Monday etc.. 7 = Saturday + * @return Milliseconds since epoch for this day to be given by {@link java.text.SimpleDateFormat} "EEEE" + */ + public static long getDayEpochMs(int day) { + return TimeUnit.DAYS.toMillis(day + 2L); + } + + public abstract String epochSecondToDate(String sql); + + public abstract String dateToEpochSecond(String sql); + + public abstract String dateToDayStamp(String sql); + + public abstract String dateToDayOfWeek(String sql); + + public abstract String dateToHour(String sql); + + // https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html + public static class MySQL extends Sql { + + @Override + public String epochSecondToDate(String sql) { + return "FROM_UNIXTIME(" + sql + ')'; + } + + @Override + public String dateToEpochSecond(String sql) { + return "UNIX_TIMESTAMP(" + sql + ')'; + } + + @Override + public String dateToDayStamp(String sql) { + return "DATE(" + sql + ')'; + } + + @Override + public String dateToDayOfWeek(String sql) { + return "DAYOFWEEK(" + sql + ')'; + } + + @Override + public String dateToHour(String sql) { + return "HOUR(" + sql + ") % 24"; + } + } + + // https://h2database.com/html/functions.html + public static class H2 extends MySQL { + + @Override + public String epochSecondToDate(String sql) { + return "DATEADD('SECOND', " + sql + ", DATE '1970-01-01')"; + } + + @Override + public String dateToEpochSecond(String sql) { + return "DATEDIFF('SECOND', DATE '1970-01-01', " + sql + ')'; + } + + @Override + public String dateToDayOfWeek(String sql) { + return "DAY_OF_WEEK(" + sql + ')'; + } + + @Override + public String dateToHour(String sql) { + return "HOUR(" + sql + ')'; + } + } + + // https://sqlite.org/lang_datefunc.html + public static class SQLite extends Sql { + + @Override + public String epochSecondToDate(String sql) { + return "datetime(" + sql + ", 'unixepoch')"; + } + + @Override + public String dateToEpochSecond(String sql) { + return "strftime('%s'," + sql + ")"; + } + + @Override + public String dateToDayStamp(String sql) { + return "strftime('%Y-%m-%d'," + sql + ')'; + } + + @Override + public String dateToDayOfWeek(String sql) { + return "strftime('%w'," + sql + ")+1"; + } + + @Override + public String dateToHour(String sql) { + return "strftime('%H'," + sql + ')'; + } + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/SqlParser.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/SqlParser.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/SqlParser.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/SqlParser.java index bdff1ba0a..2ebb8364f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/SqlParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/SqlParser.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.parsing; +package com.djrapitops.plan.storage.database.sql.parsing; /** * Class for parsing different SQL strings. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Update.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Update.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Update.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Update.java index 87a22a009..2cdf2af27 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/Update.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/Update.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.parsing; +package com.djrapitops.plan.storage.database.sql.parsing; /** * @author Fuzzlemann diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/WhereParser.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/WhereParser.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/WhereParser.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/WhereParser.java index 14effd72e..3dea0d86f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/parsing/WhereParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/parsing/WhereParser.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.parsing; +package com.djrapitops.plan.storage.database.sql.parsing; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * @author Fuzzlemann diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionGroupsTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionGroupsTable.java new file mode 100644 index 000000000..23ce8bca7 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionGroupsTable.java @@ -0,0 +1,51 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.sql.tables; + +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; + +/** + * Table information about 'plan_extension_groups'. + * + * @author Rsl1122 + */ +public class ExtensionGroupsTable { + + public static final String TABLE_NAME = "plan_extension_groups"; + + public static final String ID = "id"; + public static final String PROVIDER_ID = "provider_id"; + public static final String USER_UUID = "uuid"; + public static final String GROUP_NAME = "group_name"; + + private ExtensionGroupsTable() { + /* Static information class */ + } + + public static String createTableSQL(DBType dbType) { + return CreateTableParser.create(TABLE_NAME, dbType) + .column(ID, Sql.INT).primaryKey() + .column(USER_UUID, Sql.varchar(36)).notNull() + .column(GROUP_NAME, Sql.varchar(50)) + .column(PROVIDER_ID, Sql.INT).notNull() + .foreignKey(PROVIDER_ID, ExtensionProviderTable.TABLE_NAME, ExtensionProviderTable.ID) + .build(); + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionIconTable.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionIconTable.java index 50ef71114..8ac1ddde9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionIconTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionIconTable.java @@ -14,21 +14,21 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; import com.djrapitops.plan.extension.icon.Color; import com.djrapitops.plan.extension.icon.Family; import com.djrapitops.plan.extension.icon.Icon; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; import org.apache.commons.lang3.StringUtils; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Types; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Table information about 'plan_extension_icons'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPlayerTableValueTable.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPlayerTableValueTable.java index eb7f4c041..f0d31c661 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerTableValueTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPlayerTableValueTable.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; -import static com.djrapitops.plan.db.sql.parsing.Sql.INT; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.INT; /** * Table information about 'plan_extension_user_table_values'. @@ -49,10 +49,10 @@ public class ExtensionPlayerTableValueTable { return CreateTableParser.create(TABLE_NAME, dbType) .column(ID, INT).primaryKey() .column(USER_UUID, Sql.varchar(36)).notNull() - .column(VALUE_1, Sql.varchar(50)) - .column(VALUE_2, Sql.varchar(50)) - .column(VALUE_3, Sql.varchar(50)) - .column(VALUE_4, Sql.varchar(50)) + .column(VALUE_1, Sql.varchar(250)) + .column(VALUE_2, Sql.varchar(250)) + .column(VALUE_3, Sql.varchar(250)) + .column(VALUE_4, Sql.varchar(250)) .column(TABLE_ID, INT).notNull() .foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID) .build(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerValueTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPlayerValueTable.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerValueTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPlayerValueTable.java index 8d46cf3c8..1ea9f1794 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPlayerValueTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPlayerValueTable.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; /** * Table information about 'plan_extension_user_values'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPluginTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPluginTable.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPluginTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPluginTable.java index e618c5076..91483c378 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionPluginTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionPluginTable.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Table information about 'plan_extension_plugins'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionProviderTable.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionProviderTable.java index 9a3cd9a1f..603af7004 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionProviderTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionProviderTable.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Table information about 'plan_extension_providers'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionServerTableValueTable.java similarity index 76% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionServerTableValueTable.java index 8d28a4817..91569fa85 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerTableValueTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionServerTableValueTable.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; -import static com.djrapitops.plan.db.sql.parsing.Sql.INT; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.INT; /** * Table information about 'plan_extension_server_table_values'. @@ -50,11 +50,11 @@ public class ExtensionServerTableValueTable { return CreateTableParser.create(TABLE_NAME, dbType) .column(ID, INT).primaryKey() .column(SERVER_UUID, Sql.varchar(36)).notNull() - .column(VALUE_1, Sql.varchar(50)) - .column(VALUE_2, Sql.varchar(50)) - .column(VALUE_3, Sql.varchar(50)) - .column(VALUE_4, Sql.varchar(50)) - .column(VALUE_5, Sql.varchar(50)) + .column(VALUE_1, Sql.varchar(250)) + .column(VALUE_2, Sql.varchar(250)) + .column(VALUE_3, Sql.varchar(250)) + .column(VALUE_4, Sql.varchar(250)) + .column(VALUE_5, Sql.varchar(250)) .column(TABLE_ID, INT).notNull() .foreignKey(TABLE_ID, ExtensionTableProviderTable.TABLE_NAME, ExtensionPluginTable.ID) .build(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerValueTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionServerValueTable.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerValueTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionServerValueTable.java index 45f6db092..42a360a23 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionServerValueTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionServerValueTable.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; /** * Table information about 'plan_extension_server_values'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTabTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionTabTable.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTabTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionTabTable.java index 2d57ab630..62c4adcc0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTabTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionTabTable.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; import com.djrapitops.plan.extension.ElementOrder; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Table information about 'plan_extension_tabs'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionTableProviderTable.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionTableProviderTable.java index a08eecc3f..ceb7ae7bd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ExtensionTableProviderTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ExtensionTableProviderTable.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; import com.djrapitops.plan.extension.icon.Color; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Table information about 'plan_extension_tables'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/GeoInfoTable.java similarity index 72% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/GeoInfoTable.java index c3f4bcd73..9b25330e9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/GeoInfoTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/GeoInfoTable.java @@ -14,15 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.patches.*; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.patches.GeoInfoLastUsedPatch; +import com.djrapitops.plan.storage.database.transactions.patches.GeoInfoOptimizationPatch; +import com.djrapitops.plan.storage.database.transactions.patches.Version10Patch; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; /** * Table information about 'plan_ips'. @@ -30,28 +32,24 @@ import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; * Patches related to this table: * {@link Version10Patch} * {@link GeoInfoLastUsedPatch} - * {@link IPAnonPatch} * {@link GeoInfoOptimizationPatch} - * {@link DeleteIPHashesPatch} * * @author Rsl1122 */ public class GeoInfoTable { - public static final String TABLE_NAME = "plan_ips"; + public static final String TABLE_NAME = "plan_geolocations"; public static final String ID = "id"; public static final String USER_UUID = "uuid"; - public static final String IP = "ip"; public static final String GEOLOCATION = "geolocation"; public static final String LAST_USED = "last_used"; public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + USER_UUID + ',' - + IP + ',' + GEOLOCATION + ',' + LAST_USED - + ") VALUES (?, ?, ?, ?)"; + + ") VALUES (?, ?, ?)"; public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET " + LAST_USED + "=?" + @@ -66,7 +64,6 @@ public class GeoInfoTable { return CreateTableParser.create(TABLE_NAME, dbType) .column(ID, Sql.INT).primaryKey() .column(USER_UUID, Sql.varchar(36)).notNull() - .column(IP, Sql.varchar(39)).notNull() .column(GEOLOCATION, Sql.varchar(50)).notNull() .column(LAST_USED, Sql.LONG).notNull().defaultValue("0") .toString(); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/KillsTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/KillsTable.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/KillsTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/KillsTable.java index 9bf967e00..a8bf7e397 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/KillsTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/KillsTable.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.patches.KillsOptimizationPatch; -import com.djrapitops.plan.db.patches.KillsServerIDPatch; -import com.djrapitops.plan.db.patches.Version10Patch; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.gathering.domain.PlayerKill; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.patches.KillsOptimizationPatch; +import com.djrapitops.plan.storage.database.transactions.patches.KillsServerIDPatch; +import com.djrapitops.plan.storage.database.transactions.patches.Version10Patch; import java.sql.PreparedStatement; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/NicknamesTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/NicknamesTable.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/NicknamesTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/NicknamesTable.java index be187fe7d..a70dca5bc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/NicknamesTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/NicknamesTable.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.patches.NicknameLastSeenPatch; -import com.djrapitops.plan.db.patches.NicknamesOptimizationPatch; -import com.djrapitops.plan.db.patches.Version10Patch; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.patches.NicknameLastSeenPatch; +import com.djrapitops.plan.storage.database.transactions.patches.NicknamesOptimizationPatch; +import com.djrapitops.plan.storage.database.transactions.patches.Version10Patch; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; /** * Table information about 'plan_nicknames'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/PingTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/PingTable.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/PingTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/PingTable.java index bdd5002d2..45351c302 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/PingTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/PingTable.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.patches.PingOptimizationPatch; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.patches.PingOptimizationPatch; /** * Table information about 'plan_ping'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SecurityTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SecurityTable.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SecurityTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SecurityTable.java index ff43fac70..ddfdd6ff6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SecurityTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SecurityTable.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Insert; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Insert; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; /** * Table information about 'plan_security' diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ServerTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ServerTable.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ServerTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ServerTable.java index 6044d41b1..8c81397fa 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/ServerTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/ServerTable.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Insert; -import com.djrapitops.plan.db.sql.parsing.Sql; -import com.djrapitops.plan.db.sql.parsing.Update; -import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Insert; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.sql.parsing.Update; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Table information about 'plan_servers'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SessionsTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SessionsTable.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SessionsTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SessionsTable.java index 25630c71c..f8ace64a2 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SessionsTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SessionsTable.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.patches.SessionAFKTimePatch; -import com.djrapitops.plan.db.patches.SessionsOptimizationPatch; -import com.djrapitops.plan.db.patches.Version10Patch; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.patches.SessionAFKTimePatch; +import com.djrapitops.plan.storage.database.transactions.patches.SessionsOptimizationPatch; +import com.djrapitops.plan.storage.database.transactions.patches.Version10Patch; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Table information about 'plan_sessions'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SettingsTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SettingsTable.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SettingsTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SettingsTable.java index 0d03abf62..375e60997 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/SettingsTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/SettingsTable.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; /** * Table information about 'plan_settings'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/TPSTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/TPSTable.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/TPSTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/TPSTable.java index 67e830186..ab982f673 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/TPSTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/TPSTable.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; /** * Table information about 'plan_tps'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/UserInfoTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UserInfoTable.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/UserInfoTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UserInfoTable.java index a17178d80..d3a58fd5f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/UserInfoTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UserInfoTable.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.patches.UserInfoOptimizationPatch; -import com.djrapitops.plan.db.patches.Version10Patch; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.patches.UserInfoOptimizationPatch; +import com.djrapitops.plan.storage.database.transactions.patches.Version10Patch; /** * Table information about 'plan_user_info'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/UsersTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UsersTable.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/UsersTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UsersTable.java index bf1df723e..d5aa58001 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/UsersTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/UsersTable.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Insert; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Insert; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; /** * Table information about 'plan_users'. @@ -27,7 +27,7 @@ import com.djrapitops.plan.db.sql.parsing.Sql; * This table is used to store Player information that applies to all servers. * * Patches that apply to this table: - * {@link com.djrapitops.plan.db.patches.Version10Patch} + * {@link com.djrapitops.plan.storage.database.transactions.patches.Version10Patch} * * @author Rsl1122 */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/WorldTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WorldTable.java similarity index 77% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/WorldTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WorldTable.java index 64cc7be48..55097cfbc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/WorldTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WorldTable.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.patches.Version10Patch; -import com.djrapitops.plan.db.patches.WorldsOptimizationPatch; -import com.djrapitops.plan.db.patches.WorldsServerIDPatch; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.patches.Version10Patch; +import com.djrapitops.plan.storage.database.transactions.patches.WorldsOptimizationPatch; +import com.djrapitops.plan.storage.database.transactions.patches.WorldsServerIDPatch; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Table information about 'plan_worlds'. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/WorldTimesTable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WorldTimesTable.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/WorldTimesTable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WorldTimesTable.java index 622f8b017..e22c1604f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/sql/tables/WorldTimesTable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/sql/tables/WorldTimesTable.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.sql.tables; +package com.djrapitops.plan.storage.database.sql.tables; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.time.GMTimes; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.patches.Version10Patch; -import com.djrapitops.plan.db.patches.WorldTimesOptimizationPatch; -import com.djrapitops.plan.db.patches.WorldTimesSeverIDPatch; -import com.djrapitops.plan.db.patches.WorldsServerIDPatch; -import com.djrapitops.plan.db.sql.parsing.CreateTableParser; -import com.djrapitops.plan.db.sql.parsing.Sql; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.gathering.domain.GMTimes; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.sql.parsing.CreateTableParser; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.transactions.patches.Version10Patch; +import com.djrapitops.plan.storage.database.transactions.patches.WorldTimesOptimizationPatch; +import com.djrapitops.plan.storage.database.transactions.patches.WorldTimesSeverIDPatch; +import com.djrapitops.plan.storage.database.transactions.patches.WorldsServerIDPatch; import java.sql.PreparedStatement; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/BackupCopyTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/BackupCopyTransaction.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/BackupCopyTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/BackupCopyTransaction.java index 07381cad4..319f641d9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/BackupCopyTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/BackupCopyTransaction.java @@ -14,15 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions; +package com.djrapitops.plan.storage.database.transactions; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.queries.LargeFetchQueries; -import com.djrapitops.plan.db.access.queries.LargeStoreQueries; -import com.djrapitops.plan.db.access.queries.objects.*; -import com.djrapitops.plan.db.access.transactions.commands.RemoveEverythingTransaction; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.LargeFetchQueries; +import com.djrapitops.plan.storage.database.queries.LargeStoreQueries; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.objects.*; +import com.djrapitops.plan.storage.database.transactions.commands.RemoveEverythingTransaction; import java.util.function.Function; @@ -56,7 +55,6 @@ public class BackupCopyTransaction extends RemoveEverythingTransaction { copyWorldNames(); copyTPSData(); copyPlanWebUsers(); - copyCommandUsageData(); copyGeoInformation(); copyNicknameData(); copySessionsWithKillAndWorldData(); @@ -73,10 +71,6 @@ public class BackupCopyTransaction extends RemoveEverythingTransaction { copy(LargeStoreQueries::storeAllPingData, PingQueries.fetchAllPingData()); } - private void copyCommandUsageData() { - copy(LargeStoreQueries::storeAllCommandUsageData, LargeFetchQueries.fetchAllCommandUsageData()); - } - private void copyGeoInformation() { copy(LargeStoreQueries::storeAllGeoInformation, GeoInfoQueries.fetchAllGeoInformation()); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/ExecBatchStatement.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/ExecBatchStatement.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/ExecBatchStatement.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/ExecBatchStatement.java index 7fee837d1..3c758c3a9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/ExecBatchStatement.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/ExecBatchStatement.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.transactions; import java.sql.PreparedStatement; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/ExecStatement.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/ExecStatement.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/ExecStatement.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/ExecStatement.java index 0bb5fbf41..ec819d3c1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/ExecStatement.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/ExecStatement.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.transactions; -import com.djrapitops.plan.api.exceptions.database.DBOpException; +import com.djrapitops.plan.exceptions.database.DBOpException; import java.sql.Connection; import java.sql.PreparedStatement; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/Executable.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Executable.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/Executable.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Executable.java index e56530612..68ea6fef6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/Executable.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Executable.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access; +package com.djrapitops.plan.storage.database.transactions; import java.sql.Connection; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/StoreConfigTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreConfigTransaction.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/StoreConfigTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreConfigTransaction.java index b16e8e52f..798b4dd61 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/StoreConfigTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreConfigTransaction.java @@ -14,15 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions; +package com.djrapitops.plan.storage.database.transactions; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.sql.tables.SettingsTable; -import com.djrapitops.plan.system.settings.config.Config; -import com.djrapitops.plan.system.settings.config.ConfigWriter; +import com.djrapitops.plan.settings.config.Config; +import com.djrapitops.plan.settings.config.ConfigWriter; +import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.sql.tables.SettingsTable; import org.apache.commons.text.TextStringBuilder; import java.sql.PreparedStatement; @@ -30,7 +28,7 @@ import java.sql.SQLException; import java.util.List; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction to store a server's configuration file in the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/StoreServerInformationTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreServerInformationTransaction.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/StoreServerInformationTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreServerInformationTransaction.java index 30585018b..34f021033 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/StoreServerInformationTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/StoreServerInformationTransaction.java @@ -14,17 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions; +package com.djrapitops.plan.storage.database.transactions; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.identification.Server; import java.sql.PreparedStatement; import java.sql.SQLException; -import static com.djrapitops.plan.db.sql.tables.ServerTable.INSERT_STATEMENT; -import static com.djrapitops.plan.db.sql.tables.ServerTable.UPDATE_STATEMENT; +import static com.djrapitops.plan.storage.database.sql.tables.ServerTable.INSERT_STATEMENT; +import static com.djrapitops.plan.storage.database.sql.tables.ServerTable.UPDATE_STATEMENT; /** * Transaction for keeping Plan Server serverrmation up to date in the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Transaction.java similarity index 94% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Transaction.java index 505a575a0..cf41d0075 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/Transaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/Transaction.java @@ -14,14 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions; +package com.djrapitops.plan.storage.database.transactions; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.Query; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.queries.Query; import com.djrapitops.plugin.utilities.Verify; import java.sql.*; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RegisterWebUserTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RegisterWebUserTransaction.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RegisterWebUserTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RegisterWebUserTransaction.java index dcf23199a..ad03cca08 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RegisterWebUserTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RegisterWebUserTransaction.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.commands; +package com.djrapitops.plan.storage.database.transactions.commands; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.SecurityTable; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.storage.database.sql.tables.SecurityTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java index 2230b7562..88a0fa00d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveEverythingTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveEverythingTransaction.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.commands; +package com.djrapitops.plan.storage.database.transactions.commands; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.Transaction; -import static com.djrapitops.plan.db.sql.parsing.Sql.DELETE_FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.DELETE_FROM; /** * Transaction that removes everything from the database. @@ -42,12 +42,12 @@ public class RemoveEverythingTransaction extends Transaction { clearTable(PingTable.TABLE_NAME); clearTable(UserInfoTable.TABLE_NAME); clearTable(UsersTable.TABLE_NAME); - clearTable(CommandUseTable.TABLE_NAME); clearTable(TPSTable.TABLE_NAME); clearTable(SecurityTable.TABLE_NAME); clearTable(ServerTable.TABLE_NAME); clearTable(ExtensionPlayerValueTable.TABLE_NAME); clearTable(ExtensionServerValueTable.TABLE_NAME); + clearTable(ExtensionGroupsTable.TABLE_NAME); clearTable(ExtensionProviderTable.TABLE_NAME); clearTable(ExtensionPlayerTableValueTable.TABLE_NAME); clearTable(ExtensionServerTableValueTable.TABLE_NAME); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemovePlayerTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemovePlayerTransaction.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemovePlayerTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemovePlayerTransaction.java index f6f9c9c18..043759939 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemovePlayerTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemovePlayerTransaction.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.commands; +package com.djrapitops.plan.storage.database.transactions.commands; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.queries.PlayerFetchQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction for removing a player's data from the database. @@ -57,6 +57,10 @@ public class RemovePlayerTransaction extends Transaction { deleteFromTable(PingTable.TABLE_NAME); deleteFromTable(UserInfoTable.TABLE_NAME); deleteFromTable(UsersTable.TABLE_NAME); + + deleteFromTable(ExtensionPlayerTableValueTable.TABLE_NAME); + deleteFromTable(ExtensionPlayerValueTable.TABLE_NAME); + deleteFromTable(ExtensionGroupsTable.TABLE_NAME); } private void deleteWebUser(String username) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveWebUserTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveWebUserTransaction.java similarity index 71% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveWebUserTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveWebUserTransaction.java index 8e8f1a36d..4c0a1b986 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/RemoveWebUserTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/RemoveWebUserTransaction.java @@ -14,20 +14,21 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.commands; +package com.djrapitops.plan.storage.database.transactions.commands; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.SecurityTable; +import com.djrapitops.plan.delivery.domain.WebUser; +import com.djrapitops.plan.storage.database.sql.tables.SecurityTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; -import static com.djrapitops.plan.db.sql.parsing.Sql.DELETE_FROM; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.DELETE_FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; /** - * Transaction to remove a Plan {@link com.djrapitops.plan.data.WebUser} from the database. + * Transaction to remove a Plan {@link WebUser} from the database. * * @author Rsl1122 */ diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/SetServerAsUninstalledTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/SetServerAsUninstalledTransaction.java similarity index 80% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/SetServerAsUninstalledTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/SetServerAsUninstalledTransaction.java index 9de5d3f0f..58b742414 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/commands/SetServerAsUninstalledTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/commands/SetServerAsUninstalledTransaction.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.commands; +package com.djrapitops.plan.storage.database.transactions.commands; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.ServerTable; +import com.djrapitops.plan.storage.database.sql.tables.ServerTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; /** * Transaction for telling Plan that Plan has been uninstalled from the server. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/BanStatusTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/BanStatusTransaction.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/BanStatusTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/BanStatusTransaction.java index 6a7593478..448bc4259 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/BanStatusTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/BanStatusTransaction.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.parsing.Update; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.parsing.Update; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/GeoInfoStoreTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/GeoInfoStoreTransaction.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/GeoInfoStoreTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/GeoInfoStoreTransaction.java index 7c0d5c8ad..364f24973 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/GeoInfoStoreTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/GeoInfoStoreTransaction.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.gathering.domain.GeoInfo; +import com.djrapitops.plan.storage.database.queries.DataStoreQueries; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.net.InetAddress; import java.util.UUID; @@ -57,7 +57,7 @@ public class GeoInfoStoreTransaction extends Transaction { private GeoInfo createGeoInfo() { String country = geolocationFunction.apply(ip.getHostAddress()); - return new GeoInfo(ip, country, time); + return new GeoInfo(country, time); } @Override diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/KickStoreTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/KickStoreTransaction.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/KickStoreTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/KickStoreTransaction.java index ee9ae7ce2..12719a4c1 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/KickStoreTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/KickStoreTransaction.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.UsersTable; +import com.djrapitops.plan.storage.database.sql.tables.UsersTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; /** * Transaction to store information in the database when a player is kicked from the server. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/NicknameStoreTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/NicknameStoreTransaction.java similarity index 86% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/NicknameStoreTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/NicknameStoreTransaction.java index 41e3d6a85..c6a5762cc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/NicknameStoreTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/NicknameStoreTransaction.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.storage.database.queries.DataStoreQueries; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.UUID; import java.util.function.BiPredicate; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/OperatorStatusTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/OperatorStatusTransaction.java similarity index 81% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/OperatorStatusTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/OperatorStatusTransaction.java index 458fb5305..1a21abad8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/OperatorStatusTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/OperatorStatusTransaction.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.parsing.Update; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.parsing.Update; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PingStoreTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PingStoreTransaction.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PingStoreTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PingStoreTransaction.java index 4bb4b2ce4..4caabe4f3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PingStoreTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PingStoreTransaction.java @@ -14,14 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.store.objects.DateObj; -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.gathering.domain.Ping; +import com.djrapitops.plan.storage.database.queries.DataStoreQueries; +import com.djrapitops.plan.storage.database.transactions.Transaction; import com.djrapitops.plan.utilities.analysis.Median; -import com.google.common.annotations.VisibleForTesting; import java.util.List; import java.util.OptionalInt; @@ -83,7 +82,7 @@ public class PingStoreTransaction extends Transaction { .max(); } - @VisibleForTesting + // VisibleForTesting int getMeanValue() { return (int) Median.forList(pingList.stream().map(DateObj::getValue).collect(Collectors.toList())).calculate(); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PlayerRegisterTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PlayerRegisterTransaction.java similarity index 69% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PlayerRegisterTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PlayerRegisterTransaction.java index 320e7da61..7b45a6892 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PlayerRegisterTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PlayerRegisterTransaction.java @@ -14,11 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.queries.PlayerFetchQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.gathering.cache.SessionCache; +import com.djrapitops.plan.storage.database.queries.DataStoreQueries; +import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.UUID; import java.util.function.LongSupplier; @@ -48,8 +51,12 @@ public class PlayerRegisterTransaction extends Transaction { @Override protected void performOperations() { if (!query(PlayerFetchQueries.isPlayerRegistered(playerUUID))) { - execute(DataStoreQueries.registerBaseUser(playerUUID, registered.getAsLong(), playerName)); + long registerDate = registered.getAsLong(); + execute(DataStoreQueries.registerBaseUser(playerUUID, registerDate, playerName)); + SessionCache.getCachedSession(playerUUID).ifPresent(session -> session.setAsFirstSessionIfMatches(registerDate)); } execute(DataStoreQueries.updatePlayerName(playerUUID, playerName)); + + JSONCache.invalidateMatching(DataID.PLAYERS); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PlayerServerRegisterTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PlayerServerRegisterTransaction.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PlayerServerRegisterTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PlayerServerRegisterTransaction.java index dc4af70e0..86e5ac17d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/PlayerServerRegisterTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/PlayerServerRegisterTransaction.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.queries.PlayerFetchQueries; +import com.djrapitops.plan.storage.database.queries.DataStoreQueries; +import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries; import java.util.UUID; import java.util.function.LongSupplier; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/ServerShutdownTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/ServerShutdownTransaction.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/ServerShutdownTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/ServerShutdownTransaction.java index 21cf21b87..a95886546 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/ServerShutdownTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/ServerShutdownTransaction.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.db.access.queries.LargeStoreQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.storage.database.queries.LargeStoreQueries; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.Collection; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/SessionEndTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/SessionEndTransaction.java similarity index 52% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/SessionEndTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/SessionEndTransaction.java index 5101e74c3..4089438fb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/SessionEndTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/SessionEndTransaction.java @@ -14,11 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.delivery.domain.keys.SessionKeys; +import com.djrapitops.plan.delivery.webserver.cache.DataID; +import com.djrapitops.plan.delivery.webserver.cache.JSONCache; +import com.djrapitops.plan.gathering.domain.Session; +import com.djrapitops.plan.storage.database.queries.DataStoreQueries; +import com.djrapitops.plan.storage.database.transactions.Transaction; /** * Transaction for storing a session after a session has ended. @@ -36,5 +39,19 @@ public class SessionEndTransaction extends Transaction { @Override protected void performOperations() { execute(DataStoreQueries.storeSession(session)); + + session.getValue(SessionKeys.SERVER_UUID) + .ifPresent(serverUUID -> JSONCache.invalidate( + serverUUID, + DataID.SESSIONS, + DataID.GRAPH_WORLD_PIE, + DataID.GRAPH_PUNCHCARD, + DataID.KILLS, + DataID.ONLINE_OVERVIEW, + DataID.SESSIONS_OVERVIEW, + DataID.PVP_PVE, + DataID.GRAPH_UNIQUE_NEW, + DataID.GRAPH_CALENDAR + )); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/TPSStoreTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/TPSStoreTransaction.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/TPSStoreTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/TPSStoreTransaction.java index 2b4b1852a..4af6f620f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/TPSStoreTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/TPSStoreTransaction.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.builders.TPSBuilder; -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.gathering.domain.TPS; +import com.djrapitops.plan.gathering.domain.builders.TPSBuilder; +import com.djrapitops.plan.storage.database.queries.DataStoreQueries; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.util.List; import java.util.UUID; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/WorldNameStoreTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/WorldNameStoreTransaction.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/WorldNameStoreTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/WorldNameStoreTransaction.java index 89d7e57b2..d9d87a45f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/events/WorldNameStoreTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/events/WorldNameStoreTransaction.java @@ -14,18 +14,18 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.events; +package com.djrapitops.plan.storage.database.transactions.events; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.access.queries.DataStoreQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.WorldTable; +import com.djrapitops.plan.storage.database.queries.DataStoreQueries; +import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.WorldTable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction to store world name after an event. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateIndexTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateIndexTransaction.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateIndexTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateIndexTransaction.java index a1380f1c7..f9605b359 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateIndexTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateIndexTransaction.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.init; +package com.djrapitops.plan.storage.database.transactions.init; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.access.queries.schema.MySQLSchemaQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.queries.schema.MySQLSchemaQueries; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.Transaction; import org.apache.commons.text.TextStringBuilder; /** diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java index 35fd01ee1..955a9a399 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/CreateTablesTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/CreateTablesTransaction.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.init; +package com.djrapitops.plan.storage.database.transactions.init; -import com.djrapitops.plan.db.sql.tables.*; +import com.djrapitops.plan.storage.database.sql.tables.*; /** * Transaction that creates the table schema of Plan database. @@ -39,7 +39,6 @@ public class CreateTablesTransaction extends OperationCriticalTransaction { execute(SessionsTable.createTableSQL(dbType)); execute(KillsTable.createTableSQL(dbType)); execute(PingTable.createTableSQL(dbType)); - execute(CommandUseTable.createTableSQL(dbType)); execute(TPSTable.createTableSQL(dbType)); execute(WorldTable.createTableSQL(dbType)); execute(WorldTimesTable.createTableSQL(dbType)); @@ -56,5 +55,6 @@ public class CreateTablesTransaction extends OperationCriticalTransaction { execute(ExtensionTableProviderTable.createTableSQL(dbType)); execute(ExtensionPlayerTableValueTable.createTableSQL(dbType)); execute(ExtensionServerTableValueTable.createTableSQL(dbType)); + execute(ExtensionGroupsTable.createTableSQL(dbType)); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/OperationCriticalTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/OperationCriticalTransaction.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/OperationCriticalTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/OperationCriticalTransaction.java index 08a8e7219..0b7732587 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/OperationCriticalTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/OperationCriticalTransaction.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.init; +package com.djrapitops.plan.storage.database.transactions.init; -import com.djrapitops.plan.api.exceptions.database.FatalDBException; -import com.djrapitops.plan.db.SQLDB; -import com.djrapitops.plan.db.access.transactions.Transaction; +import com.djrapitops.plan.exceptions.database.FatalDBException; +import com.djrapitops.plan.storage.database.SQLDB; +import com.djrapitops.plan.storage.database.transactions.Transaction; /** * Transaction that is required to be executed before a database is operable. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveDuplicateUserInfoTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveDuplicateUserInfoTransaction.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveDuplicateUserInfoTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveDuplicateUserInfoTransaction.java index 2d5ebccce..b4f87914e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveDuplicateUserInfoTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveDuplicateUserInfoTransaction.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.init; +package com.djrapitops.plan.storage.database.transactions.init; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; +import com.djrapitops.plan.storage.database.transactions.Transaction; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction for removing duplicate data in plan_user_info. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldExtensionsTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldExtensionsTransaction.java new file mode 100644 index 000000000..7071482be --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldExtensionsTransaction.java @@ -0,0 +1,169 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.transactions.init; + +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.*; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Transaction; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashSet; +import java.util.UUID; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Transaction that removes outdated plugin's data after configurable threshold. + * + * @author Rsl1122 + */ +public class RemoveOldExtensionsTransaction extends Transaction { + + private final long deleteOlder; + private final UUID serverUUID; + + public RemoveOldExtensionsTransaction(long deleteAfterMs, UUID serverUUID) { + deleteOlder = System.currentTimeMillis() - deleteAfterMs; + this.serverUUID = serverUUID; + } + + @Override + protected void performOperations() { + for (Integer providerID : query(inactiveProviderIDsQuery())) { + removeValues(providerID); + } + for (Integer providerID : query(inactiveTableProviderIDsQuery())) { + removeTableValues(providerID); + } + removeProviders(); + } + + private void removeValues(int providerID) { + for (String table : new String[]{ + ExtensionPlayerValueTable.TABLE_NAME, + ExtensionServerValueTable.TABLE_NAME, + ExtensionGroupsTable.TABLE_NAME + }) { + execute(new ExecStatement(DELETE_FROM + table + WHERE + "provider_id=?") { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, providerID); + } + }); + } + } + + private void removeTableValues(Integer providerID) { + for (String table : new String[]{ + ExtensionPlayerTableValueTable.TABLE_NAME, + ExtensionServerTableValueTable.TABLE_NAME + }) { + execute(new ExecStatement(DELETE_FROM + table + WHERE + "table_id=?") { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setInt(1, providerID); + } + }); + } + } + + private void removeProviders() { + execute(new ExecStatement( + DELETE_FROM + ExtensionProviderTable.TABLE_NAME + + WHERE + ExtensionProviderTable.PLUGIN_ID + + " IN (" + + SELECT + ExtensionPluginTable.ID + + FROM + ExtensionPluginTable.TABLE_NAME + + WHERE + ExtensionPluginTable.LAST_UPDATED + "> inactiveProviderIDsQuery() { + String sql = SELECT + "pr." + ExtensionProviderTable.ID + + FROM + ExtensionProviderTable.TABLE_NAME + " pr" + + INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " pl on pl." + ExtensionPluginTable.ID + "=pr." + ExtensionProviderTable.PLUGIN_ID + + WHERE + ExtensionPluginTable.LAST_UPDATED + ">(sql, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, deleteOlder); + statement.setString(2, serverUUID.toString()); + } + + @Override + public Collection processResults(ResultSet set) throws SQLException { + Collection providerIds = new HashSet<>(); + while (set.next()) { + providerIds.add(set.getInt(ExtensionProviderTable.ID)); + } + return providerIds; + } + }; + } + + private Query> inactiveTableProviderIDsQuery() { + String sql = SELECT + "pr." + ExtensionTableProviderTable.ID + + FROM + ExtensionTableProviderTable.TABLE_NAME + " pr" + + INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " pl on pl." + ExtensionPluginTable.ID + "=pr." + ExtensionTableProviderTable.PLUGIN_ID + + WHERE + ExtensionPluginTable.LAST_UPDATED + ">(sql, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, deleteOlder); + statement.setString(2, serverUUID.toString()); + } + + @Override + public Collection processResults(ResultSet set) throws SQLException { + Collection providerIds = new HashSet<>(); + while (set.next()) { + providerIds.add(set.getInt(ExtensionProviderTable.ID)); + } + return providerIds; + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldSampledDataTransaction.java similarity index 82% rename from Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldSampledDataTransaction.java index 1c1f6d085..6f2471485 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/transactions/init/RemoveOldSampledDataTransaction.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/init/RemoveOldSampledDataTransaction.java @@ -14,22 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.access.transactions.init; +package com.djrapitops.plan.storage.database.transactions.init; -import com.djrapitops.plan.data.store.objects.DateObj; -import com.djrapitops.plan.db.access.ExecStatement; -import com.djrapitops.plan.db.access.Executable; -import com.djrapitops.plan.db.access.queries.objects.TPSQueries; -import com.djrapitops.plan.db.access.transactions.Transaction; -import com.djrapitops.plan.db.sql.tables.PingTable; -import com.djrapitops.plan.db.sql.tables.TPSTable; +import com.djrapitops.plan.delivery.domain.DateObj; +import com.djrapitops.plan.storage.database.queries.objects.TPSQueries; +import com.djrapitops.plan.storage.database.sql.tables.PingTable; +import com.djrapitops.plan.storage.database.sql.tables.TPSTable; +import com.djrapitops.plan.storage.database.transactions.ExecStatement; +import com.djrapitops.plan.storage.database.transactions.Executable; +import com.djrapitops.plan.storage.database.transactions.Transaction; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Optional; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Transaction for cleaning up old data from the database. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/BadAFKThresholdValuePatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/BadAFKThresholdValuePatch.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/BadAFKThresholdValuePatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/BadAFKThresholdValuePatch.java index 6b6cc600e..94a4e7ce0 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/BadAFKThresholdValuePatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/BadAFKThresholdValuePatch.java @@ -14,14 +14,14 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.access.HasMoreThanZeroQueryStatement; -import com.djrapitops.plan.db.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.queries.HasMoreThanZeroQueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; import java.sql.PreparedStatement; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Patch that resets AFK time of sessions with afk time of length of the session to 0. diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/CommandUsageTableRemovalPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/CommandUsageTableRemovalPatch.java new file mode 100644 index 000000000..fb7688156 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/CommandUsageTableRemovalPatch.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.transactions.patches; + +/** + * Patch that removes plan_commandusages table. + * + * @author Rsl1122 + */ +public class CommandUsageTableRemovalPatch extends Patch { + + @Override + public boolean hasBeenApplied() { + return !hasTable("plan_commandusages"); + } + + @Override + protected void applyPatch() { + dropTable("plan_commandusages"); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/DeleteIPsPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/DeleteIPsPatch.java new file mode 100644 index 000000000..f0741d068 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/DeleteIPsPatch.java @@ -0,0 +1,91 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.transactions.patches; + +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; + +/** + * Patch that replaces plan_ips with plan_geolocations table. + * + * @author Rsl1122 + */ +public class DeleteIPsPatch extends Patch { + + private final String tempTableName; + + public DeleteIPsPatch() { + tempTableName = "temp_ips"; + } + + @Override + public boolean hasBeenApplied() { + return !hasTable("plan_ips"); + } + + @Override + protected void applyPatch() { + if (hasTable(GeoInfoTable.TABLE_NAME) && hasLessDataInPlanIPs()) { + dropTable("plan_ips"); + return; + } + tempOldTable(); + + dropTable(GeoInfoTable.TABLE_NAME); + execute(GeoInfoTable.createTableSQL(dbType)); + + execute("INSERT INTO " + GeoInfoTable.TABLE_NAME + " (" + + GeoInfoTable.USER_UUID + ',' + + GeoInfoTable.LAST_USED + ',' + + GeoInfoTable.GEOLOCATION + + ") SELECT " + DISTINCT + + GeoInfoTable.USER_UUID + ',' + + GeoInfoTable.LAST_USED + ',' + + GeoInfoTable.GEOLOCATION + + FROM + tempTableName + ); + + dropTable(tempTableName); + } + + private boolean hasLessDataInPlanIPs() { + Integer inIPs = query(new QueryAllStatement(SELECT + "COUNT(1) as c" + FROM + "plan_ips") { + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("c") : 0; + } + }); + Integer inGeoInfo = query(new QueryAllStatement(SELECT + "COUNT(1) as c" + FROM + GeoInfoTable.TABLE_NAME) { + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("c") : 0; + } + }); + return inIPs <= inGeoInfo; + } + + private void tempOldTable() { + if (!hasTable(tempTableName)) { + renameTable("plan_ips", tempTableName); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DiskUsagePatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/DiskUsagePatch.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/DiskUsagePatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/DiskUsagePatch.java index 3493c3893..7136d3594 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/DiskUsagePatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/DiskUsagePatch.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.sql.tables.TPSTable; +import com.djrapitops.plan.storage.database.sql.tables.TPSTable; public class DiskUsagePatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/ExtensionShowInPlayersTablePatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/ExtensionShowInPlayersTablePatch.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/ExtensionShowInPlayersTablePatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/ExtensionShowInPlayersTablePatch.java index 66c12e74a..dfee9181c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/ExtensionShowInPlayersTablePatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/ExtensionShowInPlayersTablePatch.java @@ -14,10 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.sql.parsing.Sql; -import com.djrapitops.plan.db.sql.tables.ExtensionProviderTable; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable; /** * Patch to add 'show_in_players_table' to 'plan_extension_providers' diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/ExtensionTableRowValueLengthPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/ExtensionTableRowValueLengthPatch.java new file mode 100644 index 000000000..07fbb6a66 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/ExtensionTableRowValueLengthPatch.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.database.transactions.patches; + +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.queries.schema.H2SchemaQueries; +import com.djrapitops.plan.storage.database.queries.schema.MySQLSchemaQueries; +import com.djrapitops.plan.storage.database.sql.parsing.Sql; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerTableValueTable; +import com.djrapitops.plan.storage.database.sql.tables.ExtensionServerTableValueTable; + +public class ExtensionTableRowValueLengthPatch extends Patch { + + private String playerTable; + private String serverTable; + + public ExtensionTableRowValueLengthPatch() { + playerTable = ExtensionPlayerTableValueTable.TABLE_NAME; + serverTable = ExtensionServerTableValueTable.TABLE_NAME; + } + + @Override + public boolean hasBeenApplied() { + return dbType == DBType.SQLITE || // SQLite does not limit varchar lengths + (columnVarcharLength(playerTable, ExtensionPlayerTableValueTable.VALUE_4) >= 250 + && columnVarcharLength(serverTable, ExtensionServerTableValueTable.VALUE_5) >= 250); + } + + private int columnVarcharLength(String table, String column) { + if (dbType == DBType.MYSQL) { + return query(MySQLSchemaQueries.columnVarcharLength(table, column)); + } else { + return query(H2SchemaQueries.columnVarcharLength(table, column)); + } + } + + @Override + protected void applyPatch() { + increaseLength(playerTable, ExtensionPlayerTableValueTable.VALUE_1); + increaseLength(playerTable, ExtensionPlayerTableValueTable.VALUE_2); + increaseLength(playerTable, ExtensionPlayerTableValueTable.VALUE_3); + increaseLength(playerTable, ExtensionPlayerTableValueTable.VALUE_4); + + increaseLength(serverTable, ExtensionServerTableValueTable.VALUE_1); + increaseLength(serverTable, ExtensionServerTableValueTable.VALUE_2); + increaseLength(serverTable, ExtensionServerTableValueTable.VALUE_3); + increaseLength(serverTable, ExtensionServerTableValueTable.VALUE_4); + increaseLength(serverTable, ExtensionServerTableValueTable.VALUE_5); + } + + private void increaseLength(String table, String column) { + execute("ALTER TABLE " + table + " MODIFY " + column + " " + Sql.varchar(250)); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoLastUsedPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/GeoInfoLastUsedPatch.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoLastUsedPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/GeoInfoLastUsedPatch.java index 010d3d1ca..5e605de1f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoLastUsedPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/GeoInfoLastUsedPatch.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.sql.tables.GeoInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable; public class GeoInfoLastUsedPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/GeoInfoOptimizationPatch.java similarity index 67% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/GeoInfoOptimizationPatch.java index f8c1ca011..4fb29a00c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/GeoInfoOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/GeoInfoOptimizationPatch.java @@ -14,28 +14,29 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.sql.tables.GeoInfoTable; +import com.djrapitops.plan.storage.database.sql.tables.GeoInfoTable; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; public class GeoInfoOptimizationPatch extends Patch { private String tempTableName; - private String tableName; + private String oldTableName; public GeoInfoOptimizationPatch() { - tableName = GeoInfoTable.TABLE_NAME; + oldTableName = "plan_ips"; tempTableName = "temp_ips"; } @Override public boolean hasBeenApplied() { - return hasColumn(tableName, GeoInfoTable.ID) - && hasColumn(tableName, GeoInfoTable.USER_UUID) - && !hasColumn(tableName, "user_id") - && !hasTable(tempTableName); // If this table exists the patch has failed to finish. + return !hasTable(oldTableName) + || (hasColumn(oldTableName, GeoInfoTable.ID) + && hasColumn(oldTableName, GeoInfoTable.USER_UUID) + && !hasColumn(oldTableName, "user_id") + && !hasTable(tempTableName)); // If this table exists the patch has failed to finish. } @Override @@ -43,14 +44,12 @@ public class GeoInfoOptimizationPatch extends Patch { tempOldTable(); execute(GeoInfoTable.createTableSQL(dbType)); - execute("INSERT INTO " + tableName + " (" + + execute("INSERT INTO " + GeoInfoTable.TABLE_NAME + " (" + GeoInfoTable.USER_UUID + ',' + - GeoInfoTable.IP + ',' + GeoInfoTable.LAST_USED + ',' + GeoInfoTable.GEOLOCATION + - ") SELECT " + + ") " + SELECT + DISTINCT + "(SELECT plan_users.uuid FROM plan_users WHERE plan_users.id = " + tempTableName + ".user_id LIMIT 1), " + - GeoInfoTable.IP + ',' + GeoInfoTable.LAST_USED + ',' + GeoInfoTable.GEOLOCATION + FROM + tempTableName @@ -61,7 +60,7 @@ public class GeoInfoOptimizationPatch extends Patch { private void tempOldTable() { if (!hasTable(tempTableName)) { - renameTable(tableName, tempTableName); + renameTable(oldTableName, tempTableName); } } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/KillsOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/KillsOptimizationPatch.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/KillsOptimizationPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/KillsOptimizationPatch.java index 6d88bc802..0dfe9cc4e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/KillsOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/KillsOptimizationPatch.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.sql.tables.KillsTable; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.sql.tables.KillsTable; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; public class KillsOptimizationPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/KillsServerIDPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/KillsServerIDPatch.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/KillsServerIDPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/KillsServerIDPatch.java index e897c1681..7fd556136 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/KillsServerIDPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/KillsServerIDPatch.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.access.ExecBatchStatement; -import com.djrapitops.plan.db.access.queries.schema.SessionIDServerIDRelationQuery; -import com.djrapitops.plan.db.sql.tables.KillsTable; +import com.djrapitops.plan.storage.database.queries.schema.SessionIDServerIDRelationQuery; +import com.djrapitops.plan.storage.database.sql.tables.KillsTable; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; import java.sql.PreparedStatement; import java.sql.SQLException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/NicknameLastSeenPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/NicknameLastSeenPatch.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/NicknameLastSeenPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/NicknameLastSeenPatch.java index 10f1ad6ee..ffe2d9794 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/NicknameLastSeenPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/NicknameLastSeenPatch.java @@ -14,22 +14,22 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.db.access.ExecBatchStatement; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.sql.parsing.Select; -import com.djrapitops.plan.db.sql.tables.NicknamesTable; -import com.djrapitops.plan.db.sql.tables.ServerTable; +import com.djrapitops.plan.delivery.domain.Nickname; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.sql.parsing.Select; +import com.djrapitops.plan.storage.database.sql.tables.NicknamesTable; +import com.djrapitops.plan.storage.database.sql.tables.ServerTable; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; -import static com.djrapitops.plan.db.sql.parsing.Sql.AND; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.AND; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; public class NicknameLastSeenPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/NicknamesOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/NicknamesOptimizationPatch.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/NicknamesOptimizationPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/NicknamesOptimizationPatch.java index 440e35818..8d58d4969 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/NicknamesOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/NicknamesOptimizationPatch.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.sql.tables.NicknamesTable; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.sql.tables.NicknamesTable; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; public class NicknamesOptimizationPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/Patch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Patch.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/Patch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Patch.java index 266dc170f..15eb33fe8 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/Patch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Patch.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.DBType; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.access.queries.schema.H2SchemaQueries; -import com.djrapitops.plan.db.access.queries.schema.MySQLSchemaQueries; -import com.djrapitops.plan.db.access.queries.schema.SQLiteSchemaQueries; -import com.djrapitops.plan.db.access.transactions.init.OperationCriticalTransaction; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.DBType; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.queries.schema.H2SchemaQueries; +import com.djrapitops.plan.storage.database.queries.schema.MySQLSchemaQueries; +import com.djrapitops.plan.storage.database.queries.schema.SQLiteSchemaQueries; +import com.djrapitops.plan.storage.database.transactions.init.OperationCriticalTransaction; import com.djrapitops.plugin.utilities.Verify; import java.sql.PreparedStatement; @@ -30,7 +30,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; public abstract class Patch extends OperationCriticalTransaction { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/PingOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/PingOptimizationPatch.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/PingOptimizationPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/PingOptimizationPatch.java index 540fc8f5c..bdf072924 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/PingOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/PingOptimizationPatch.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.sql.tables.PingTable; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.sql.tables.PingTable; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; public class PingOptimizationPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/SessionAFKTimePatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionAFKTimePatch.java similarity index 88% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/SessionAFKTimePatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionAFKTimePatch.java index 38c8a7f5b..d46280a3f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/SessionAFKTimePatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionAFKTimePatch.java @@ -14,9 +14,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; public class SessionAFKTimePatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/SessionsOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionsOptimizationPatch.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/SessionsOptimizationPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionsOptimizationPatch.java index b32d28ff9..790580a18 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/SessionsOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/SessionsOptimizationPatch.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.sql.tables.SessionsTable; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; public class SessionsOptimizationPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/TransferTableRemovalPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/TransferTableRemovalPatch.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/TransferTableRemovalPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/TransferTableRemovalPatch.java index c4d8b9002..8fad3d5f5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/TransferTableRemovalPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/TransferTableRemovalPatch.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; public class TransferTableRemovalPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/UserInfoOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/UserInfoOptimizationPatch.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/UserInfoOptimizationPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/UserInfoOptimizationPatch.java index 42a18ee59..641b2cddd 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/UserInfoOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/UserInfoOptimizationPatch.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.sql.tables.UserInfoTable; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.sql.tables.UserInfoTable; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; public class UserInfoOptimizationPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/Version10Patch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Version10Patch.java similarity index 83% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/Version10Patch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Version10Patch.java index 4d24ed7e8..e39754673 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/Version10Patch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/Version10Patch.java @@ -14,15 +14,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.sql.tables.*; -import com.djrapitops.plan.system.info.server.Server; +import com.djrapitops.plan.identification.Server; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.sql.tables.*; import java.util.Optional; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; public class Version10Patch extends Patch { @@ -43,8 +43,6 @@ public class Version10Patch extends Patch { } public void alterTablesToV10() { - copyCommandUsage(); - copyTPS(); dropTable(UserInfoTable.TABLE_NAME); @@ -103,22 +101,6 @@ public class Version10Patch extends Patch { execute(statement); } - private void copyCommandUsage() { - String tempTableName = "temp_cmdusg"; - - renameTable("plan_commandusages", tempTableName); - - execute(CommandUseTable.createTableSQL(dbType)); - - String statement = "INSERT INTO plan_commandusages " + - "(command, times_used, server_id)" + - " SELECT command, times_used, '" + serverID + "'" + - FROM + tempTableName; - execute(statement); - - dropTable(tempTableName); - } - private void copyTPS() { String tempTableName = "temp_tps"; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/VersionTableRemovalPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/VersionTableRemovalPatch.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/VersionTableRemovalPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/VersionTableRemovalPatch.java index 9ec07d43f..34d18a32d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/VersionTableRemovalPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/VersionTableRemovalPatch.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; public class VersionTableRemovalPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldTimesOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldTimesOptimizationPatch.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldTimesOptimizationPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldTimesOptimizationPatch.java index 30d7dcdbe..883979872 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldTimesOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldTimesOptimizationPatch.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.sql.tables.WorldTimesTable; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.sql.tables.WorldTimesTable; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; public class WorldTimesOptimizationPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldTimesSeverIDPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldTimesSeverIDPatch.java similarity index 84% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldTimesSeverIDPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldTimesSeverIDPatch.java index 7588fd0c2..9cc58ed68 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldTimesSeverIDPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldTimesSeverIDPatch.java @@ -14,17 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.access.ExecBatchStatement; -import com.djrapitops.plan.db.access.queries.schema.SessionIDServerIDRelationQuery; -import com.djrapitops.plan.db.sql.tables.WorldTimesTable; +import com.djrapitops.plan.storage.database.queries.schema.SessionIDServerIDRelationQuery; +import com.djrapitops.plan.storage.database.sql.tables.WorldTimesTable; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Map; -import static com.djrapitops.plan.db.sql.parsing.Sql.WHERE; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.WHERE; public class WorldTimesSeverIDPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldsOptimizationPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldsOptimizationPatch.java similarity index 89% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldsOptimizationPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldsOptimizationPatch.java index d2d0d22bd..9b177efdc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldsOptimizationPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldsOptimizationPatch.java @@ -14,12 +14,12 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.sql.tables.WorldTable; +import com.djrapitops.plan.exceptions.database.DBOpException; +import com.djrapitops.plan.storage.database.sql.tables.WorldTable; -import static com.djrapitops.plan.db.sql.parsing.Sql.FROM; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.FROM; public class WorldsOptimizationPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldsServerIDPatch.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldsServerIDPatch.java similarity index 90% rename from Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldsServerIDPatch.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldsServerIDPatch.java index f5feabc57..bf8ac1261 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/patches/WorldsServerIDPatch.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/transactions/patches/WorldsServerIDPatch.java @@ -14,16 +14,16 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.patches; +package com.djrapitops.plan.storage.database.transactions.patches; -import com.djrapitops.plan.db.access.ExecBatchStatement; -import com.djrapitops.plan.db.access.QueryAllStatement; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.access.queries.LargeStoreQueries; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.sql.tables.SessionsTable; -import com.djrapitops.plan.db.sql.tables.WorldTable; -import com.djrapitops.plan.db.sql.tables.WorldTimesTable; +import com.djrapitops.plan.storage.database.queries.LargeStoreQueries; +import com.djrapitops.plan.storage.database.queries.QueryAllStatement; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.queries.objects.ServerQueries; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.sql.tables.WorldTable; +import com.djrapitops.plan.storage.database.sql.tables.WorldTimesTable; +import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -32,7 +32,7 @@ import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; public class WorldsServerIDPatch extends Patch { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/file/FileResource.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/FileResource.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/system/file/FileResource.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/file/FileResource.java index 00e239c71..afbce3918 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/file/FileResource.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/FileResource.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.file; +package com.djrapitops.plan.storage.file; import java.io.File; import java.io.FileInputStream; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/file/JarResource.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/JarResource.java similarity index 98% rename from Plan/common/src/main/java/com/djrapitops/plan/system/file/JarResource.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/file/JarResource.java index 406ff6e79..cb3e2e5e7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/file/JarResource.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/JarResource.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.file; +package com.djrapitops.plan.storage.file; import java.io.FileNotFoundException; import java.io.IOException; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/file/PlanFiles.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/PlanFiles.java similarity index 87% rename from Plan/common/src/main/java/com/djrapitops/plan/system/file/PlanFiles.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/file/PlanFiles.java index 14b9f9c82..67b789aac 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/file/PlanFiles.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/PlanFiles.java @@ -14,12 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.file; +package com.djrapitops.plan.storage.file; import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.api.exceptions.EnableException; -import com.djrapitops.plan.system.SubSystem; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.exceptions.EnableException; import com.djrapitops.plugin.utilities.Verify; +import org.apache.commons.lang3.StringUtils; import javax.inject.Inject; import javax.inject.Singleton; @@ -52,6 +53,10 @@ public class PlanFiles implements SubSystem { return dataFolder; } + public Path getDataDirectory() { + return dataFolder.toPath(); + } + public File getLogsFolder() { File folder = getFileFromPluginFolder("logs"); folder.mkdirs(); @@ -72,6 +77,8 @@ public class PlanFiles implements SubSystem { @Override public void enable() throws EnableException { + ResourceCache.invalidateAll(); + ResourceCache.cleanUp(); Verify.isTrue((dataFolder.exists() && dataFolder.isDirectory()) || dataFolder.mkdirs(), () -> new EnableException("Could not create data folder at " + dataFolder.getAbsolutePath())); try { @@ -114,13 +121,16 @@ public class PlanFiles implements SubSystem { * @return a {@link Resource} for accessing the resource, either from the plugin folder or jar. */ public Resource getCustomizableResourceOrDefault(String resourceName) { - return attemptToFind(resourceName).map(file -> (Resource) new FileResource(resourceName, file)).orElse(getResourceFromJar(resourceName)); + return ResourceCache.getOrCache(resourceName, () -> + attemptToFind(resourceName).map(file -> (Resource) new FileResource(resourceName, file)) + .orElse(getResourceFromJar(resourceName)) + ); } private Optional attemptToFind(String resourceName) { if (dataFolder.exists() && dataFolder.isDirectory()) { - String[] path = resourceName.split("/"); + String[] path = StringUtils.split(resourceName, '/'); Path toFile = dataFolder.getAbsoluteFile().toPath().toAbsolutePath(); for (String next : path) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/file/Resource.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/Resource.java similarity index 97% rename from Plan/common/src/main/java/com/djrapitops/plan/system/file/Resource.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/file/Resource.java index 76f84c14a..9a96f1c0b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/file/Resource.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/Resource.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.file; +package com.djrapitops.plan.storage.file; import java.io.IOException; import java.io.InputStream; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/file/ResourceCache.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/ResourceCache.java new file mode 100644 index 000000000..d0a14b599 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/ResourceCache.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.file; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +/** + * In-memory cache for different resources on disk or jar. + * + * @author Rsl1122 + */ +public class ResourceCache { + + private static final Cache cache = Caffeine.newBuilder() + .expireAfterAccess(1, TimeUnit.MINUTES) + .build(); + + private ResourceCache() { + // Static class + } + + public static Resource getOrCache(String resourceName, Supplier resourceSupplier) { + String found = cache.getIfPresent(resourceName); + if (found == null) { + return new StringCachingResource(resourceSupplier.get()); + } + return new StringResource(resourceName, found); + } + + public static void cache(String resourceName, String got) { + cache.put(resourceName, got); + } + + public static void invalidateAll() { + cache.invalidateAll(); + } + + public static void cleanUp() { + cache.cleanUp(); + } + + public static List getCachedResourceNames() { + List resourceNames = new ArrayList<>(cache.asMap().keySet()); + Collections.sort(resourceNames); + return resourceNames; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/file/StringCachingResource.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/StringCachingResource.java new file mode 100644 index 000000000..80979c4ca --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/StringCachingResource.java @@ -0,0 +1,57 @@ +/* + * 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 . + */ +package com.djrapitops.plan.storage.file; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +/** + * Resource decorator to cache result of asString method call in {@link ResourceCache}. + * + * @author Rsl1122 + */ +public class StringCachingResource implements Resource { + + private final Resource implementation; + + StringCachingResource(Resource implementation) { + this.implementation = implementation; + } + + @Override + public String getResourceName() { + return implementation.getResourceName(); + } + + @Override + public InputStream asInputStream() throws IOException { + return implementation.asInputStream(); + } + + @Override + public List asLines() throws IOException { + return implementation.asLines(); + } + + @Override + public String asString() throws IOException { + String got = implementation.asString(); + ResourceCache.cache(implementation.getResourceName(), got); + return got; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/file/StringResource.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/StringResource.java new file mode 100644 index 000000000..0f421f7cd --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/file/StringResource.java @@ -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 . + */ +package com.djrapitops.plan.storage.file; + +import org.apache.commons.lang3.StringUtils; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +/** + * {@link Resource} implementation for a {@link String}. + * + * @author Rsl1122 + */ +public class StringResource implements Resource { + + private final String resourceName; + private final String resource; + + StringResource(String resourceName, String resource) { + this.resourceName = resourceName; + this.resource = resource; + } + + @Override + public String getResourceName() { + return resourceName; + } + + @Override + public InputStream asInputStream() { + return new ByteArrayInputStream(resource.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public List asLines() { + return Arrays.asList(StringUtils.split(resource, "\r\n")); + } + + @Override + public String asString() { + return resource; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBCleanTask.java similarity index 73% rename from Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBCleanTask.java index 3e7bf1b23..a5ba420a7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/DBCleanTask.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBCleanTask.java @@ -14,30 +14,30 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.tasks; +package com.djrapitops.plan.storage.upkeep; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.Query; -import com.djrapitops.plan.db.access.QueryStatement; -import com.djrapitops.plan.db.access.transactions.commands.RemovePlayerTransaction; -import com.djrapitops.plan.db.access.transactions.init.RemoveDuplicateUserInfoTransaction; -import com.djrapitops.plan.db.access.transactions.init.RemoveOldSampledDataTransaction; -import com.djrapitops.plan.db.sql.tables.SessionsTable; +import com.djrapitops.plan.exceptions.database.DBOpException; import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalPlayerResultsTransaction; import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveUnsatisfiedConditionalServerResultsTransaction; +import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plan.query.QueryServiceImplementation; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.TimeSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; +import com.djrapitops.plan.storage.database.DBSystem; +import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.Query; +import com.djrapitops.plan.storage.database.queries.QueryStatement; +import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; +import com.djrapitops.plan.storage.database.transactions.commands.RemovePlayerTransaction; +import com.djrapitops.plan.storage.database.transactions.init.RemoveDuplicateUserInfoTransaction; +import com.djrapitops.plan.storage.database.transactions.init.RemoveOldExtensionsTransaction; +import com.djrapitops.plan.storage.database.transactions.init.RemoveOldSampledDataTransaction; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.logging.error.ErrorHandler; import com.djrapitops.plugin.task.AbsRunnable; -import com.google.common.annotations.VisibleForTesting; import javax.inject.Inject; import javax.inject.Singleton; @@ -48,7 +48,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; -import static com.djrapitops.plan.db.sql.parsing.Sql.*; +import static com.djrapitops.plan.storage.database.sql.parsing.Sql.*; /** * Task for cleaning the active database. @@ -66,6 +66,10 @@ public class DBCleanTask extends AbsRunnable { private final PluginLogger logger; private final ErrorHandler errorHandler; + // This variable assumes that the system is thrown away on reload and new one is constructed. + // It is to avoid cleaning extension data that has not been updated after uptime longer than the deletion threshold. + private final long lastReload; + @Inject public DBCleanTask( PlanConfig config, @@ -84,6 +88,8 @@ public class DBCleanTask extends AbsRunnable { this.serverInfo = serverInfo; this.logger = logger; this.errorHandler = errorHandler; + + lastReload = System.currentTimeMillis(); } @Override @@ -103,6 +109,10 @@ public class DBCleanTask extends AbsRunnable { if (removed > 0) { logger.info(locale.getString(PluginLang.DB_NOTIFY_CLEAN, removed)); } + Long deleteExtensionDataAfter = config.get(TimeSettings.DELETE_EXTENSION_DATA_AFTER); + if (System.currentTimeMillis() - lastReload <= deleteExtensionDataAfter) { + database.executeTransaction(new RemoveOldExtensionsTransaction(deleteExtensionDataAfter, serverInfo.getServerUUID())); + } } } catch (DBOpException e) { errorHandler.log(L.ERROR, this.getClass(), e); @@ -110,7 +120,7 @@ public class DBCleanTask extends AbsRunnable { } } - @VisibleForTesting + // VisibleForTesting public int cleanOldPlayers(Database database) { long now = System.currentTimeMillis(); long keepActiveAfter = now - config.get(TimeSettings.DELETE_INACTIVE_PLAYERS_AFTER); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/KeepAliveTask.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBKeepAliveTask.java similarity index 91% rename from Plan/common/src/main/java/com/djrapitops/plan/db/tasks/KeepAliveTask.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBKeepAliveTask.java index fd5001b2e..d7522e291 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/tasks/KeepAliveTask.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/DBKeepAliveTask.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.db.tasks; +package com.djrapitops.plan.storage.upkeep; import com.djrapitops.plan.utilities.MiscUtils; import com.djrapitops.plugin.logging.L; @@ -32,13 +32,13 @@ import java.sql.Statement; * * @author Fuzzlemann */ -public class KeepAliveTask extends AbsRunnable { - private Connection connection; +public class DBKeepAliveTask extends AbsRunnable { private final IReconnect iReconnect; private final PluginLogger logger; private final ErrorHandler errorHandler; + private Connection connection; - public KeepAliveTask(Connection connection, IReconnect iReconnect, PluginLogger logger, ErrorHandler errorHandler) { + public DBKeepAliveTask(Connection connection, IReconnect iReconnect, PluginLogger logger, ErrorHandler errorHandler) { this.connection = connection; this.iReconnect = iReconnect; this.logger = logger; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/LogsFolderCleanTask.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/LogsFolderCleanTask.java similarity index 92% rename from Plan/common/src/main/java/com/djrapitops/plan/system/tasks/LogsFolderCleanTask.java rename to Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/LogsFolderCleanTask.java index 5f011a534..38d061f31 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/LogsFolderCleanTask.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/upkeep/LogsFolderCleanTask.java @@ -14,11 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.tasks; +package com.djrapitops.plan.storage.upkeep; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.storage.file.PlanFiles; import com.djrapitops.plugin.logging.console.PluginLogger; import com.djrapitops.plugin.task.AbsRunnable; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/HtmlUtilities.java b/Plan/common/src/main/java/com/djrapitops/plan/system/HtmlUtilities.java deleted file mode 100644 index 27b29cc09..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/HtmlUtilities.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system; - -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.graphs.Graphs; -import com.djrapitops.plan.utilities.html.structure.Accordions; -import com.djrapitops.plan.utilities.html.structure.AnalysisPluginsTabContentCreator; -import com.djrapitops.plan.utilities.html.tables.HtmlTables; -import dagger.Lazy; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class HtmlUtilities { - - private final Lazy formatters; - private final Lazy htmlTables; - private final Lazy graphs; - private final Lazy accordions; - private final Lazy analysisPluginsTabContentCreator; - - @Inject - public HtmlUtilities( - Lazy formatters, - Lazy htmlTables, - Lazy graphs, - Lazy accordions, - Lazy analysisPluginsTabContentCreator - ) { - this.formatters = formatters; - this.htmlTables = htmlTables; - this.graphs = graphs; - this.accordions = accordions; - this.analysisPluginsTabContentCreator = analysisPluginsTabContentCreator; - } - - public Formatters getFormatters() { - return formatters.get(); - } - - public HtmlTables getHtmlTables() { - return htmlTables.get(); - } - - public Graphs getGraphs() { - return graphs.get(); - } - - public Accordions getAccordions() { - return accordions.get(); - } - - public AnalysisPluginsTabContentCreator getAnalysisPluginsTabContentCreator() { - return analysisPluginsTabContentCreator.get(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/database/databases/operation/FetchOperations.java b/Plan/common/src/main/java/com/djrapitops/plan/system/database/databases/operation/FetchOperations.java deleted file mode 100644 index 0682c63f4..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/database/databases/operation/FetchOperations.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.database.databases.operation; - -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.UserInfo; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.containers.ServerContainer; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.settings.config.Config; - -import java.util.*; - -/** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ -@Deprecated -public interface FetchOperations { - - /** - * Used to get a NetworkContainer, some limitations apply to values returned by DataContainer keys. - *

    - * Limitations: - * - Bungee ServerContainer does not support: ServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT - * - Bungee ServerContainer ServerKeys.TPS only contains playersOnline values - * - NetworkKeys.PLAYERS PlayerContainers: - * - do not support: PlayerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT - * - PlayerKeys.PER_SERVER does not support: PerServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_DEATHS, PLAYER_KILL_COUNT - *

    - * Blocking methods are not called until DataContainer getter methods are called. - * - * @return a new NetworkContainer. - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.queries.containers.NetworkContainerQuery} - */ - @Deprecated - NetworkContainer getNetworkContainer(); - - /** - * Used to get a ServerContainer, some limitations apply to values returned by DataContainer keys. - *

    - * Limitations: - * - ServerKeys.PLAYERS PlayerContainers PlayerKeys.PER_SERVER only contains information about the queried server. - *

    - * Blocking methods are not called until DataContainer getter methods are called. - * - * @param serverUUID UUID of the Server. - * @return a new ServerContainer. - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.queries.containers.ServerContainerQuery}. - */ - @Deprecated - ServerContainer getServerContainer(UUID serverUUID); - - /** - * Used to get PlayerContainers of all players on the network, some limitations apply to DataContainer keys. - *

    - * Limitations: - * - PlayerContainers do not support: PlayerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_KILL_COUNT - * - PlayerContainers PlayerKeys.PER_SERVER does not support: PerServerKeys WORLD_TIMES, PLAYER_KILLS, PLAYER_KILL_COUNT - *

    - * Blocking methods are not called until DataContainer getter methods are called. - * - * @return a list of PlayerContainers in Plan database. - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.queries.containers.AllPlayerContainersQuery}. - */ - @Deprecated - List getAllPlayerContainers(); - - /** - * Used to get a PlayerContainer of a specific player. - *

    - * Blocking methods are not called until DataContainer getter methods are called. - * - * @param uuid UUID of the player. - * @return a new PlayerContainer. - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.queries.containers.PlayerContainerQuery}. - */ - @Deprecated - PlayerContainer getPlayerContainer(UUID uuid); - - // UUIDs - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Set getSavedUUIDs(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Set getSavedUUIDs(UUID server); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Map getServerNames(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Optional getServerUUID(String serverName); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - UUID getUuidOf(String playerName); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - WebUser getWebUser(String username); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Optional getServerName(UUID serverUUID); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Optional getBungeeInformation(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Optional getServerID(UUID serverUUID); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - List getTPSData(UUID serverUUID); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Map>> getSessionsWithNoExtras(); - - /** - * @deprecated It was not possible to keep this compatible so now empty map is returned. - */ - @Deprecated - Map getUsers(); - - /** - * @deprecated Now empty map is returned. - */ - @Deprecated - Map getLastSeenForAllPlayers(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Map> getAllGeoInfo(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Map getPlayerNames(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - String getPlayerName(UUID playerUUID); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - List getNicknames(UUID uuid); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Map getBukkitServers(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - List getWebUsers(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - List getServers(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - List getServerUUIDs(); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Map> getPlayersOnlineForServers(Collection servers); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Map getPlayersRegisteredForServers(Collection servers); - - /** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ - @Deprecated - Optional getNewConfig(long updatedAfter, UUID serverUUID); -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/database/databases/sql/operation/SQLFetchOps.java b/Plan/common/src/main/java/com/djrapitops/plan/system/database/databases/sql/operation/SQLFetchOps.java deleted file mode 100644 index 6af93e9ac..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/database/databases/sql/operation/SQLFetchOps.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.database.databases.sql.operation; - -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.container.TPS; -import com.djrapitops.plan.data.container.UserInfo; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.containers.ServerContainer; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.ServerAggregateQueries; -import com.djrapitops.plan.db.access.queries.containers.ContainerFetchQueries; -import com.djrapitops.plan.db.access.queries.objects.*; -import com.djrapitops.plan.system.database.databases.operation.FetchOperations; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.settings.config.Config; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * @deprecated Bad API, replaced with {@link com.djrapitops.plan.db.access.Query} objects. - */ -@Deprecated -public class SQLFetchOps implements FetchOperations { - - private final Database db; - - public SQLFetchOps(Database db) { - this.db = db; - } - - @Override - public NetworkContainer getNetworkContainer() { - return db.query(ContainerFetchQueries.fetchNetworkContainer()); - } - - @Override - public ServerContainer getServerContainer(UUID serverUUID) { - return db.query(ContainerFetchQueries.fetchServerContainer(serverUUID)); - } - - @Override - public List getAllPlayerContainers() { - return db.query(ContainerFetchQueries.fetchAllPlayerContainers()); - } - - @Override - public PlayerContainer getPlayerContainer(UUID uuid) { - return db.query(ContainerFetchQueries.fetchPlayerContainer(uuid)); - } - - @Override - public Set getSavedUUIDs() { - return db.query(UserIdentifierQueries.fetchAllPlayerUUIDs()); - } - - @Override - public Set getSavedUUIDs(UUID serverUUID) { - return db.query(UserIdentifierQueries.fetchPlayerUUIDsOfServer(serverUUID)); - } - - @Override - public Map getServerNames() { - return db.query(ServerQueries.fetchServerNames()); - } - - @Override - public Optional getServerUUID(String serverName) { - return db.query(ServerQueries.fetchServerMatchingIdentifier(serverName)) - .map(Server::getUuid); - } - - @Override - public UUID getUuidOf(String playerName) { - return db.query(UserIdentifierQueries.fetchPlayerUUIDOf(playerName)).orElse(null); - } - - @Override - public WebUser getWebUser(String username) { - return db.query(WebUserQueries.fetchWebUser(username)).orElse(null); - } - - @Override - public List getTPSData(UUID serverUUID) { - return db.query(TPSQueries.fetchTPSDataOfServer(serverUUID)); - } - - @Override - public Map>> getSessionsWithNoExtras() { - return db.query(SessionQueries.fetchAllSessionsWithoutKillOrWorldData()); - } - - @Override - public Map getUsers() { - return new HashMap<>(); - } - - @Override - public Map getLastSeenForAllPlayers() { - return new HashMap<>(); - } - - @Override - public Map> getAllGeoInfo() { - return db.query(GeoInfoQueries.fetchAllGeoInformation()); - } - - @Override - public Map getPlayerNames() { - return db.query(UserIdentifierQueries.fetchAllPlayerNames()); - } - - @Override - public String getPlayerName(UUID playerUUID) { - return db.query(UserIdentifierQueries.fetchPlayerNameOf(playerUUID)).orElse(null); - } - - @Override - public Optional getServerName(UUID serverUUID) { - return db.query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)).map(Server::getName); - } - - @Override - public List getNicknames(UUID playerUUID) { - return db.query(NicknameQueries.fetchNicknameDataOfPlayer(playerUUID)).stream() - .map(Nickname::getName).collect(Collectors.toList()); - } - - @Override - public Optional getBungeeInformation() { - return db.query(ServerQueries.fetchProxyServerInformation()); - } - - @Override - public Optional getServerID(UUID serverUUID) { - return db.query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)).map(Server::getId); - } - - @Override - public Map getBukkitServers() { - return db.query(ServerQueries.fetchPlanServerInformation()).entrySet().stream() - .filter(entry -> entry.getValue().isNotProxy()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - @Override - public List getWebUsers() { - return new ArrayList<>(db.query(WebUserQueries.fetchAllPlanWebUsers())); - } - - @Override - public List getServers() { - List servers = new ArrayList<>(db.query(ServerQueries.fetchPlanServerInformation()).values()); - Collections.sort(servers); - return servers; - } - - @Override - public List getServerUUIDs() { - return new ArrayList<>(db.query(ServerQueries.fetchPlanServerInformation()).keySet()); - } - - @Override - public Map> getPlayersOnlineForServers(Collection servers) { - return db.query(TPSQueries.fetchPlayerOnlineDataOfServers(servers)); - } - - @Override - public Map getPlayersRegisteredForServers(Collection servers) { - return db.query(ServerAggregateQueries.serverUserCounts()); - } - - @Override - public Optional getNewConfig(long updatedAfter, UUID serverUUID) { - return db.query(new NewerConfigQuery(serverUUID, updatedAfter)); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java deleted file mode 100644 index 47aa56180..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/ExportSystem.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.export; - -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ExportSettings; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * System in charge of exporting html. - * - * @author Rsl1122 - */ -@Singleton -public class ExportSystem implements SubSystem { - - private final PlanConfig config; - private final ServerInfo serverInfo; - private final Processing processing; - private final HtmlExport htmlExport; - private final ConnectionSystem connectionSystem; - - @Inject - public ExportSystem( - PlanConfig config, - ServerInfo serverInfo, - Processing processing, - HtmlExport htmlExport, - ConnectionSystem connectionSystem - ) { - this.config = config; - this.serverInfo = serverInfo; - this.processing = processing; - this.htmlExport = htmlExport; - this.connectionSystem = connectionSystem; - } - - @Override - public void enable() { - if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) { - return; - } - if (config.isTrue(ExportSettings.JS_AND_CSS)) { - processing.submitNonCritical(htmlExport::exportJs); - processing.submitNonCritical(htmlExport::exportCss); - processing.submitNonCritical(htmlExport::exportPlugins); - } - if (config.isTrue(ExportSettings.PLAYERS_PAGE)) { - processing.submitNonCritical(htmlExport::exportPlayersPage); - } - if (config.isTrue(ExportSettings.PLAYER_PAGES)) { - processing.submitNonCritical(htmlExport::exportAvailablePlayers); - } - if (config.isTrue(ExportSettings.SERVER_PAGE)) { - processing.submitNonCritical(() -> { - htmlExport.cacheNetworkPage(); - htmlExport.exportAvailableServerPages(); - }); - } - } - - @Override - public void disable() { - // Nothing to disable - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java deleted file mode 100644 index 844955ecb..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java +++ /dev/null @@ -1,304 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.export; - -import com.djrapitops.plan.api.exceptions.ParseException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.data.container.BaseUser; -import com.djrapitops.plan.db.access.queries.objects.BaseUserQueries; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ExportSettings; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.settings.theme.ThemeVal; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; -import com.djrapitops.plan.system.webserver.response.pages.NetworkPageResponse; -import com.djrapitops.plan.utilities.html.pages.InspectPage; -import com.djrapitops.plan.utilities.html.pages.NetworkPage; -import com.djrapitops.plan.utilities.html.pages.PageFactory; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.djrapitops.plugin.utilities.Verify; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.file.Files; -import java.util.*; - -/** - * Class responsible for Html Export task. - * - * @author Rsl1122 - */ -@Singleton -public class HtmlExport extends SpecificExport { - - private final PlanConfig config; - private final Theme theme; - private final PlanFiles files; - private final DBSystem dbSystem; - private final PageFactory pageFactory; - private final ConnectionSystem connectionSystem; - private final ErrorHandler errorHandler; - - @Inject - public HtmlExport( - PlanFiles files, - PlanConfig config, - Theme theme, - DBSystem dbSystem, - PageFactory pageFactory, - JSONFactory jsonFactory, - ServerInfo serverInfo, - ConnectionSystem connectionSystem, - ErrorHandler errorHandler - ) { - super(files, jsonFactory, serverInfo); - this.config = config; - this.theme = theme; - this.files = files; - this.dbSystem = dbSystem; - this.pageFactory = pageFactory; - this.connectionSystem = connectionSystem; - this.errorHandler = errorHandler; - } - - @Override - protected String getPath() { - return config.get(ExportSettings.HTML_EXPORT_PATH); - } - - public void exportServer(UUID serverUUID) { - if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) { - return; - } - dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)) - .map(Server::getName) - .ifPresent(serverName -> { - try { - exportAvailableServerPage(serverUUID, serverName); - } catch (IOException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - }); - } - - public void exportPlayerPage(UUID playerUUID) { - Optional name = dbSystem.getDatabase().query(UserIdentifierQueries.fetchPlayerNameOf(playerUUID)); - exportPlayerPage(playerUUID, name.orElse("Unknown")); - } - - public void exportPlayerPage(UUID playerUUID, String playerName) { - InspectPage playerPage = pageFactory.inspectPage(playerUUID); - try { - exportPlayerPage(playerName, playerPage.toHtml()); - } catch (ParseException | IOException e) { - errorHandler.log(L.ERROR, this.getClass(), e); - } - } - - public void exportCachedPlayerPage(UUID playerUUID) { - if (serverInfo.getServer().isNotProxy() && connectionSystem.isServerAvailable()) { - return; - } - - dbSystem.getDatabase().query(UserIdentifierQueries.fetchPlayerNameOf(playerUUID)) - .ifPresent(playerName -> { - try { - exportAvailablePlayerPage(playerUUID, playerName); - } catch (IOException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - }); - } - - public void exportPlayersPage() { - try { - String html = pageFactory.playersPage().toHtml() - .replace("href=\"plugins/", "href=\"../plugins/") - .replace("href=\"css/", "href=\"../css/") - .replace("src=\"plugins/", "src=\"../plugins/") - .replace("src=\"js/", "src=\"../js/"); - List lines = Arrays.asList(html.split("\n")); - - File htmlLocation = new File(getFolder(), "players"); - Verify.isTrue(htmlLocation.exists() && htmlLocation.isDirectory() || htmlLocation.mkdirs(), - () -> new FileNotFoundException("Output folder could not be created at" + htmlLocation.getAbsolutePath())); - File exportFile = new File(htmlLocation, "index.html"); - export(exportFile, lines); - } catch (IOException | DBOpException | ParseException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - - public void exportAvailablePlayers() { - try { - Collection users = dbSystem.getDatabase().query(BaseUserQueries.fetchAllBaseUsers()); - for (BaseUser user : users) { - exportAvailablePlayerPage(user.getUuid(), user.getName()); - } - } catch (IOException | DBOpException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - - public void cacheNetworkPage() { - if (serverInfo.getServer().isNotProxy()) { - return; - } - - NetworkPage networkPage = pageFactory.networkPage(); - ResponseCache.cacheResponse(PageId.SERVER.of(serverInfo.getServerUUID()), () -> { - try { - return new NetworkPageResponse(networkPage); - } catch (ParseException e) { - errorHandler.log(L.WARN, this.getClass(), e); - return null; - } - }); - } - - public void exportNetworkPage() { - if (serverInfo.getServer().isNotProxy()) { - return; - } - - cacheNetworkPage(); - try { - exportAvailableServerPage(serverInfo.getServerUUID(), serverInfo.getServer().getName()); - } catch (IOException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - - public void exportAvailableServerPages() { - try { - Map serverNames = dbSystem.getDatabase().query(ServerQueries.fetchServerNames()); - - for (Map.Entry entry : serverNames.entrySet()) { - exportAvailableServerPage(entry.getKey(), entry.getValue()); - } - } catch (IOException | DBOpException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - - public void exportCss() { - String[] resources = new String[]{ - "web/css/main.css", - "web/css/materialize.css", - "web/css/style.css", - "web/css/themes/all-themes.css" - }; - copyFromJar(resources); - } - - public void exportJs() { - String[] resources = new String[]{ - "web/js/demo.js", - "web/js/admin.js", - "web/js/helpers.js", - "web/js/script.js", - "web/js/charts/activityPie.js", - "web/js/charts/lineGraph.js", - "web/js/charts/horizontalBarGraph.js", - "web/js/charts/stackGraph.js", - "web/js/charts/performanceGraph.js", - "web/js/charts/playerGraph.js", - "web/js/charts/playerGraphNoNav.js", - "web/js/charts/resourceGraph.js", - "web/js/charts/diskGraph.js", - "web/js/charts/tpsGraph.js", - "web/js/charts/worldGraph.js", - "web/js/charts/worldMap.js", - "web/js/charts/punchCard.js", - "web/js/charts/serverPie.js", - "web/js/charts/worldPie.js", - "web/js/charts/healthGauge.js", - "web/js/charts/sessionCalendar.js", - "web/js/charts/onlineActivityCalendar.js" - }; - copyFromJar(resources); - - try { - String demo = files.getCustomizableResourceOrDefault("web/js/demo.js") - .asString() - .replace("${defaultTheme}", theme.getValue(ThemeVal.THEME_DEFAULT)); - List lines = Arrays.asList(demo.split("\n")); - File outputFolder = new File(getFolder(), "js"); - Verify.isTrue(outputFolder.exists() && outputFolder.isDirectory() || outputFolder.mkdirs(), - () -> new FileNotFoundException("Output folder could not be created at " + outputFolder.getAbsolutePath())); - export(new File(outputFolder, "demo.js"), lines); - } catch (IOException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - - public void exportPlugins() { - String[] resources = new String[]{ - "web/plugins/node-waves/waves.css", - "web/plugins/node-waves/waves.js", - "web/plugins/animate-css/animate.css", - "web/plugins/jquery-slimscroll/jquery.slimscroll.js", - "web/plugins/jquery/jquery.min.js", - "web/plugins/fullcalendar/fullcalendar.min.js", - "web/plugins/fullcalendar/fullcalendar.min.css", - "web/plugins/momentjs/moment.js", - }; - copyFromJar(resources); - } - - private void copyFromJar(String[] resources) { - for (String resource : resources) { - try { - copyFromJar(resource); - } catch (IOException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - } - } - - private void copyFromJar(String resource) throws IOException { - List lines = files.getCustomizableResourceOrDefault(resource).asLines(); - - File to = new File(getFolder(), resource.replace("web/", "").replace("/", File.separator)); - File locationFolder = to.getParentFile(); - - Verify.isTrue(locationFolder.exists() && locationFolder.isDirectory() || locationFolder.mkdirs(), - () -> new FileNotFoundException("Output folder could not be created at" + locationFolder.getAbsolutePath())); - - if (to.exists()) { - Files.delete(to.toPath()); - if (!to.createNewFile()) { - return; - } - } - - export(to, lines); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java deleted file mode 100644 index 04ddf947c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.export; - -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ExportSettings; -import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.error.ErrorHandler; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.io.File; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.Collections; -import java.util.UUID; - -/** - * Class in charge of exporting json files. - * - * @author Rsl1122 - */ -@Singleton -public class JSONExport extends SpecificExport { - - private final PlanConfig config; - private final DBSystem dbSystem; - private final ResponseFactory responseFactory; - private final ErrorHandler errorHandler; - - @Inject - public JSONExport( - PlanFiles files, - PlanConfig config, - DBSystem dbSystem, - ServerInfo serverInfo, - JSONFactory jsonFactory, - ResponseFactory responseFactory, - ErrorHandler errorHandler - ) { - super(files, jsonFactory, serverInfo); - this.config = config; - this.dbSystem = dbSystem; - this.responseFactory = responseFactory; - this.errorHandler = errorHandler; - } - - @Override - protected String getPath() { - return config.get(ExportSettings.JSON_EXPORT_PATH); - } - - public void exportPlayerJSON(UUID playerUUID) { - String json = responseFactory.rawPlayerPageResponse(playerUUID).getContent(); - - dbSystem.getDatabase().query(UserIdentifierQueries.fetchPlayerNameOf(playerUUID)) - .ifPresent(playerName -> { - try { - File htmlLocation = getPlayerFolder(); - htmlLocation.mkdirs(); - File exportFile = new File(htmlLocation, URLEncoder.encode(playerName, "UTF-8") + ".json"); - - export(exportFile, Collections.singletonList(json)); - } catch (IOException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - }); - } - - public void exportServerJSON(UUID serverUUID) { - String json = responseFactory.rawServerPageResponse(serverUUID).getContent(); - dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)) - .map(Server::getName) - .ifPresent(serverName -> { - try { - File htmlLocation = getServerFolder(); - htmlLocation.mkdirs(); - File exportFile = new File(htmlLocation, URLEncoder.encode(serverName, "UTF-8") + ".json"); - - export(exportFile, Collections.singletonList(json)); - } catch (IOException e) { - errorHandler.log(L.WARN, this.getClass(), e); - } - }); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java deleted file mode 100644 index b10b48bd9..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.export; - -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; -import com.djrapitops.plan.system.webserver.response.Response; - -import java.io.File; -import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.UUID; - -/** - * Abstract Html Export Task. - * - * @author Rsl1122 - */ -public abstract class SpecificExport { - - private final PlanFiles files; - private final JSONFactory jsonFactory; // Hacky, TODO export needs a rework - protected final ServerInfo serverInfo; - - SpecificExport( - PlanFiles files, - JSONFactory jsonFactory, ServerInfo serverInfo - ) { - this.files = files; - this.jsonFactory = jsonFactory; - this.serverInfo = serverInfo; - } - - protected File getFolder() { - File folder; - - String path = getPath(); - boolean isAbsolute = Paths.get(path).isAbsolute(); - if (isAbsolute) { - folder = new File(path); - } else { - File dataFolder = files.getDataFolder(); - folder = new File(dataFolder, path); - } - - if (!folder.exists() || !folder.isDirectory()) { - folder.mkdirs(); - } - return folder; - } - - protected abstract String getPath(); - - protected void export(File to, List lines) throws IOException { - Files.write(to.toPath(), lines, StandardCharsets.UTF_8, StandardOpenOption.CREATE); - } - - File getServerFolder() { - File server = new File(getFolder(), "server"); - server.mkdirs(); - return server; - } - - File getPlayerFolder() { - File player = new File(getFolder(), "player"); - player.mkdirs(); - return player; - } - - void exportPlayerPage(String playerName, String html) throws IOException { - List lines = Arrays.asList(html.replace("../", "../../").split("\n")); - - File htmlLocation = new File(getPlayerFolder(), URLEncoder.encode(playerName, "UTF-8").replace(".", "%2E")); - htmlLocation.mkdirs(); - File exportFile = new File(htmlLocation, "index.html"); - - export(exportFile, lines); - } - - void exportAvailablePlayerPage(UUID playerUUID, String name) throws IOException { - Response response = ResponseCache.loadResponse(PageId.PLAYER.of(playerUUID)); - if (response == null) { - return; - } - - String html = response.getContent(); - exportPlayerPage(name, html); - } - - void exportAvailableServerPage(UUID serverUUID, String serverName) throws IOException { - - Response response = ResponseCache.loadResponse(PageId.SERVER.of(serverUUID)); - if (response == null) { - return; - } - - String html = response.getContent() - .replace("href=\"plugins/", "href=\"../plugins/") - .replace("href=\"css/", "href=\"../css/") - .replace("src=\"plugins/", "src=\"../plugins/") - .replace("src=\"js/", "src=\"../js/") - .replace("../json/players?serverName=" + serverName, "./players_table.json"); - - File htmlLocation; - if (serverInfo.getServer().isProxy()) { - if (serverUUID.equals(serverInfo.getServerUUID())) { - htmlLocation = new File(getFolder(), "network"); - } else { - htmlLocation = new File(getServerFolder(), URLEncoder.encode(serverName, "UTF-8").replace(".", "%2E")); - html = html.replace("../", "../../"); - exportPlayersTableJSON(htmlLocation, serverUUID); - } - } else { - htmlLocation = getServerFolder(); - exportPlayersTableJSON(htmlLocation, serverUUID); - } - - htmlLocation.mkdirs(); - File exportFile = new File(htmlLocation, "index.html"); - - List lines = Arrays.asList(html.split("\n")); - - export(exportFile, lines); - } - - private void exportPlayersTableJSON(File htmlLocation, UUID serverUUID) throws IOException { - htmlLocation.mkdirs(); - File exportFile = new File(htmlLocation, "players_table.json"); - export(exportFile, Collections.singletonList(jsonFactory.serverPlayersTableJSON(serverUUID))); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java deleted file mode 100644 index 7a7d115f9..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/InfoSystem.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info; - -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.ConnectionFailException; -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.DebugChannels; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.request.GenerateRequest; -import com.djrapitops.plan.system.info.request.InfoRequest; -import com.djrapitops.plan.system.info.request.InfoRequestFactory; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.logging.console.PluginLogger; -import dagger.Lazy; - -import java.util.UUID; - -/** - * Information management system. - *

    - * Subclasses should decide how InfoRequests are run locally if necessary. - *

    - * Everything should be called from an Async thread. - * - * @author Rsl1122 - */ -public abstract class InfoSystem implements SubSystem { - - protected final InfoRequestFactory infoRequestFactory; - protected final ConnectionSystem connectionSystem; - protected final ServerInfo serverInfo; - protected final Lazy webServer; - protected final PluginLogger logger; - - protected InfoSystem( - InfoRequestFactory infoRequestFactory, - ConnectionSystem connectionSystem, - ServerInfo serverInfo, - Lazy webServer, - PluginLogger logger - ) { - this.infoRequestFactory = infoRequestFactory; - this.connectionSystem = connectionSystem; - this.serverInfo = serverInfo; - this.webServer = webServer; - this.logger = logger; - } - - /** - * Refreshes Player page. - *

    - * No calls from non-async thread found on 09.02.2018 - * - * @param player UUID of the player. - * @throws WebException If fails. - */ - public void generateAndCachePlayerPage(UUID player) throws WebException { - GenerateRequest infoRequest = infoRequestFactory.generateInspectPageRequest(player); - try { - sendRequest(infoRequest); - } catch (ConnectionFailException e) { - runLocally(infoRequest); - } - } - - /** - * Refreshes Analysis page. - *

    - * No calls from non-async thread found on 09.02.2018 - * - * @param serverUUID UUID of the server to analyze - * @throws WebException If fails. - */ - public void generateAnalysisPage(UUID serverUUID) throws WebException { - GenerateRequest request = infoRequestFactory.generateAnalysisPageRequest(serverUUID); - if (serverInfo.getServerUUID().equals(serverUUID)) { - runLocally(request); - } else { - sendRequest(request); - } - } - - /** - * Send an InfoRequest to another server or run locally if necessary. - *

    - * No calls from non-async thread found on 09.02.2018 - * - * @param infoRequest InfoRequest to send or run. - * @throws WebException If fails. - */ - public void sendRequest(InfoRequest infoRequest) throws WebException { - try { - if (!connectionSystem.isServerAvailable()) { - logger.getDebugLogger().logOn(DebugChannels.INFO_REQUESTS, "Main server unavailable, running locally."); - runLocally(infoRequest); - return; - } - connectionSystem.sendInfoRequest(infoRequest); - } catch (WebException original) { - try { - logger.getDebugLogger().logOn(DebugChannels.INFO_REQUESTS, "Exception during request: " + original.toString() + ", running locally."); - runLocally(infoRequest); - } catch (NoServersException noServers) { - throw original; - } - } - } - - /** - * Run the InfoRequest locally. - *

    - * No calls from non-async thread found on 09.02.2018 - * - * @param infoRequest InfoRequest to run. - * @throws WebException If fails. - */ - public abstract void runLocally(InfoRequest infoRequest) throws WebException; - - @Override - public void enable() { - connectionSystem.enable(); - } - - @Override - public void disable() { - connectionSystem.disable(); - } - - public ConnectionSystem getConnectionSystem() { - return connectionSystem; - } - - /** - * Requests Set up from Bungee. - *

    - * No calls from non-async thread found on 09.02.2018 - * - * @param addressToRequestServer Address of Bungee server. - * @throws WebException If fails. - */ - public void requestSetUp(String addressToRequestServer) throws WebException { - if (serverInfo.getServer().isProxy()) { - throw new BadRequestException("Method not available on a Proxy server."); - } - Server bungee = new Server(-1, null, "Bungee", addressToRequestServer, -1); - String addressOfThisServer = webServer.get().getAccessAddress(); - - connectionSystem.setSetupAllowed(true); - connectionSystem.sendInfoRequest(infoRequestFactory.sendDBSettingsRequest(addressOfThisServer), bungee); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/ProxyInfoSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/ProxyInfoSystem.java deleted file mode 100644 index 9a703d8d9..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/ProxyInfoSystem.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info; - -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.request.*; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.logging.console.PluginLogger; -import dagger.Lazy; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * InfoSystem for Bungee. - * - * @author Rsl1122 - */ -@Singleton -public class ProxyInfoSystem extends InfoSystem { - - @Inject - public ProxyInfoSystem( - InfoRequestFactory infoRequestFactory, - ConnectionSystem connectionSystem, - ServerInfo serverInfo, - Lazy webServer, - PluginLogger logger - ) { - super(infoRequestFactory, connectionSystem, serverInfo, webServer, logger); - } - - @Override - public void runLocally(InfoRequest infoRequest) throws WebException { - if (infoRequest instanceof CacheRequest - || infoRequest instanceof GenerateInspectPageRequest - || infoRequest instanceof GenerateInspectPluginsTabRequest - ) { - infoRequest.runLocally(); - } else { - // runLocally is called when ConnectionSystem has no servers. - throw new NoServersException("No servers were available to process this request (Local attempt): " + infoRequest.getClass().getSimpleName()); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/ServerInfoSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/ServerInfoSystem.java deleted file mode 100644 index a0e262a96..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/ServerInfoSystem.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info; - -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.DebugChannels; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.request.InfoRequest; -import com.djrapitops.plan.system.info.request.InfoRequestFactory; -import com.djrapitops.plan.system.info.request.SetupRequest; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.logging.console.PluginLogger; -import dagger.Lazy; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * InfoSystem for Bukkit servers. - * - * @author Rsl1122 - */ -@Singleton -public class ServerInfoSystem extends InfoSystem { - - @Inject - public ServerInfoSystem( - ConnectionSystem connectionSystem, - ServerInfo serverInfo, - InfoRequestFactory infoRequestFactory, - Lazy webServer, - PluginLogger logger - ) { - super(infoRequestFactory, connectionSystem, serverInfo, webServer, logger); - } - - @Override - public void runLocally(InfoRequest infoRequest) throws WebException { - if (infoRequest instanceof SetupRequest) { - throw new NoServersException("Set-up requests can not be run locally."); - } - logger.getDebugLogger().logOn(DebugChannels.INFO_REQUESTS, "Local: " + infoRequest.getClass().getSimpleName()); - infoRequest.runLocally(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionIn.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionIn.java deleted file mode 100644 index fd30a568e..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionIn.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.connection; - -import com.djrapitops.plan.api.exceptions.connection.*; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.system.info.request.InfoRequest; -import com.djrapitops.plan.system.info.request.SetupRequest; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plugin.utilities.Verify; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Map; -import java.util.UUID; -import java.util.stream.Collectors; - -public class ConnectionIn { - - private final Map variables; - private final InfoRequest infoRequest; - private final Database database; - private final ConnectionSystem connectionSystem; - - public ConnectionIn( - Request httpRequest, InfoRequest infoRequest, - Database database, - ConnectionSystem connectionSystem - ) throws WebException { - this.database = database; - this.connectionSystem = connectionSystem; - Verify.nullCheck(httpRequest, infoRequest); - - this.variables = readVariables(httpRequest); - this.infoRequest = infoRequest; - - checkAuthentication(); - } - - private void checkAuthentication() throws WebException { - UUID serverUUID = getServerUUID(); - - try { - if (database.query(ServerQueries.fetchServerMatchingIdentifier(serverUUID)).isPresent()) { - return; - } - } catch (DBOpException e) { - throw new TransferDatabaseException(e); - } - - if (infoRequest instanceof SetupRequest) { - if (!connectionSystem.isSetupAllowed()) { - throw new ForbiddenException("Setup not enabled on this server, use commands to enable."); - } - } else { - throw new UnauthorizedServerException(serverUUID + " (Sender) was not found from database"); - } - } - - private UUID getServerUUID() throws BadRequestException { - String sender = variables.get("sender"); - Verify.nullCheck(sender, () -> new BadRequestException("Sender ('sender') variable not supplied in the request.")); - - try { - return UUID.fromString(sender); - } catch (IllegalArgumentException e) { - throw new BadRequestException("Sender ('sender') was not a valid UUID: " + e.getMessage()); - } - } - - private Map readVariables(Request request) throws WebException { - String requestBody = readRequestBody(request.getRequestBody()); - String[] bodyVariables = requestBody.split(";&variable;"); - - return Arrays.stream(bodyVariables) - .map(variable -> variable.split("=", 2)) - .filter(splitVariables -> splitVariables.length == 2) - .collect(Collectors.toMap(splitVariables -> splitVariables[0], splitVariables -> splitVariables[1], (a, b) -> b)); - } - - public Response handleRequest() throws WebException { - return infoRequest.handleRequest(variables); - } - - private String readRequestBody(InputStream in) throws WebFailException { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - byte[] bytes; - - byte[] buf = new byte[4096]; - for (int n = in.read(buf); n > 0; n = in.read(buf)) { - out.write(buf, 0, n); - } - - bytes = out.toByteArray(); - - return new String(bytes, StandardCharsets.UTF_8); - } catch (IOException e) { - throw new WebFailException("Exception while reading Request.", e); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionLog.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionLog.java deleted file mode 100644 index 30a0a918a..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionLog.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.connection; - -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.system.DebugChannels; -import com.djrapitops.plan.system.info.request.InfoRequest; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plugin.logging.debug.DebugLogger; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * Class responsible for logging what {@link ConnectionOut} and {@link ConnectionIn} objects get as response. - * - * @author Rsl1122 - */ -@Singleton -public class ConnectionLog { - - private final DebugLogger debugLogger; - - private final Map> log; - - @Inject - public ConnectionLog(DebugLogger debugLogger) { - this.debugLogger = debugLogger; - log = new HashMap<>(); - } - - public void logConnectionTo(Server server, InfoRequest request, int responseCode) { - String requestName = request.getClass().getSimpleName(); - String address = server.getWebAddress(); - logConnection(address, "Out: " + requestName, responseCode); - debugLogger.logOn(DebugChannels.CONNECTIONS, "ConnectionOut: " + requestName + " to " + address); - } - - public void logConnectionFrom(String server, String requestTarget, int responseCode) { - logConnection(server, "In: " + requestTarget, responseCode); - debugLogger.logOn(DebugChannels.CONNECTIONS, "ConnectionIn: " + requestTarget + " from " + server); - } - - private void logConnection(String address, String infoRequestName, int responseCode) { - Map requestMap = log.getOrDefault(address, new HashMap<>()); - requestMap.put(infoRequestName, new Entry(responseCode, System.currentTimeMillis())); - log.put(address, requestMap); - } - - public Map> getLogEntries() { - return log; - } - - public static class Entry implements Comparable, DateHolder { - - private final int responseCode; - private final long date; - - public Entry(int responseCode, long date) { - this.responseCode = responseCode; - this.date = date; - } - - public int getResponseCode() { - return responseCode; - } - - @Override - public long getDate() { - return date; - } - - /** - * Most recent first. - * - * @param o object - * @return -1 or 1 - */ - @Override - public int compareTo(Entry o) { - return Long.compare(o.date, this.date); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Entry)) return false; - Entry entry = (Entry) o; - return responseCode == entry.responseCode && - date == entry.date; - } - - @Override - public int hashCode() { - return Objects.hash(responseCode, date); - } - } - -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionOut.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionOut.java deleted file mode 100644 index 04270b1aa..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionOut.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.connection; - -import com.djrapitops.plan.api.exceptions.connection.*; -import com.djrapitops.plan.system.info.request.InfoRequest; -import com.djrapitops.plan.system.info.request.InfoRequestWithVariables; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.utilities.MiscUtils; -import com.djrapitops.plugin.utilities.Verify; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.TrustAllStrategy; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.ssl.SSLContextBuilder; - -import java.io.IOException; -import java.net.SocketTimeoutException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.util.Map; -import java.util.Properties; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Represents an outbound action request to another Plan server. - * - * @author Rsl1122 - */ -public class ConnectionOut { - - private final Server toServer; - private final UUID serverUUID; - private final InfoRequest infoRequest; - - static { - try { - Properties properties = System.getProperties(); - properties.setProperty("sun.net.client.defaultConnectTimeout", Long.toString(TimeUnit.MINUTES.toMillis(1L))); - properties.setProperty("sun.net.client.defaultReadTimeout", Long.toString(TimeUnit.MINUTES.toMillis(1L))); - properties.setProperty("sun.net.http.retryPost", Boolean.toString(false)); - } catch (Exception e) { - Logger.getGlobal().log(Level.WARNING, "[Plan] Failed to set sun client timeout system properties.", e); - } - } - - private final ConnectionLog connectionLog; - - /** - * Constructor. - * - * @param toServer Full address to another Plan webserver. (http://something:port) - * @param serverUUID UUID of server this outbound connection. - * @param infoRequest Type of the action this connection wants to be performed. - * @param connectionLog Where the connection should be logged. - */ - public ConnectionOut( - Server toServer, UUID serverUUID, InfoRequest infoRequest, - ConnectionLog connectionLog - ) { - this.connectionLog = connectionLog; - Verify.nullCheck(toServer, serverUUID, infoRequest); - this.toServer = toServer; - this.serverUUID = serverUUID; - this.infoRequest = infoRequest; - } - - public void sendRequest() throws WebException { - String address = getAddress(); - - CloseableHttpClient client = null; - HttpPost post = null; - CloseableHttpResponse response = null; - long start = System.currentTimeMillis(); - try { - client = getHttpClient(address); - String url = address + "/info/" + infoRequest.getClass().getSimpleName().toLowerCase(); - - post = new HttpPost(url); - String parameters = parseVariables(); - prepareRequest(post, parameters); - - // Send request - response = client.execute(post); - int responseCode = response.getStatusLine().getStatusCode(); - - handleResult(url, parameters, responseCode); - } catch (SocketTimeoutException e) { - connectionLog.logConnectionTo(toServer, infoRequest, 0); - long seconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - start); - throw new ConnectionFailException("Connection to " + address + " timed out (" + seconds + "s): " + e.getMessage(), e); - } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) { - connectionLog.logConnectionTo(toServer, infoRequest, -1); - throw new ConnectionFailException("Connection failed to address: " + address + " - Make sure the server is online.", e); - } finally { - if (post != null) { - post.releaseConnection(); - } - MiscUtils.close(response); - MiscUtils.close(client); - } - } - - private void handleResult(String url, String parameters, int responseCode) throws WebException { - connectionLog.logConnectionTo(toServer, infoRequest, responseCode); - switch (responseCode) { - case 200: - return; - case 400: - throw new BadRequestException("Bad Request: " + url + " | " + parameters); - case 403: - throw new ForbiddenException(url + " returned 403 | " + parameters); - case 404: - throw new NotFoundException(url + " returned a 404, ensure that your server is connected to an up to date Plan server."); - case 412: - throw new UnauthorizedServerException(url + " reported that it does not recognize this server. Make sure '/plan m setup' was successful."); - case 500: - throw new InternalErrorException(); - case 504: - throw new GatewayException(url + " reported that it failed to connect to this server."); - default: - throw new WebException(url + "| Wrong response code " + responseCode); - } - } - - private void prepareRequest(HttpPost post, String parameters) { - RequestConfig requestConfig = RequestConfig.custom() - .setConnectionRequestTimeout(5000) - .setSocketTimeout(10000) - .setConnectTimeout(9000) - .setRedirectsEnabled(true) - .setRelativeRedirectsAllowed(true) - .setContentCompressionEnabled(true) - .build(); - post.setConfig(requestConfig); - - post.setHeader("Content-Type", "application/x-www-form-urlencoded"); - post.setHeader("charset", "UTF-8"); - post.setHeader("Connection", "close"); - - byte[] toSend = parameters.getBytes(); - post.setEntity(new ByteArrayEntity(toSend)); - } - - private CloseableHttpClient getHttpClient(String address) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { - if (address.startsWith("https")) { - SSLContextBuilder builder = new SSLContextBuilder(); - builder.loadTrustMaterial(null, new TrustAllStrategy()); - SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE); - return HttpClients.custom().setSSLSocketFactory(sslsf).build(); - } else { - return HttpClients.createDefault(); - } - } - - private String getAddress() { - String address = toServer.getWebAddress(); - if (address.contains("://:")) { - String[] parts = address.split("://:", 2); - address = parts[0] + "://localhost:" + parts[1]; - } - return address; - } - - private String parseVariables() { - StringBuilder parameters = new StringBuilder("sender=" + serverUUID + ";&variable;" + - "type=" + infoRequest.getClass().getSimpleName()); - - if (infoRequest instanceof InfoRequestWithVariables) { - Map variables = ((InfoRequestWithVariables) infoRequest).getVariables(); - for (Map.Entry entry : variables.entrySet()) { - parameters.append(";&variable;").append(entry.getKey()).append("=").append(entry.getValue()); - } - } - - return parameters.toString(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionSystem.java deleted file mode 100644 index 5ecf6f907..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ConnectionSystem.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.connection; - -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.request.InfoRequest; -import com.djrapitops.plan.system.info.request.InfoRequests; -import com.djrapitops.plan.system.info.request.WideRequest; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import dagger.Lazy; - -import java.util.*; - -/** - * ConnectionSystem manages out- and inbound InfoRequest connections. - *

    - * It decides what server to use for each request. - * - * @author Rsl1122 - */ -public abstract class ConnectionSystem implements SubSystem { - - protected final ConnectionLog connectionLog; - protected final InfoRequests infoRequests; - protected final Lazy infoSystem; - protected final ServerInfo serverInfo; - - protected Map dataServers; - private boolean setupAllowed; - - public ConnectionSystem( - ConnectionLog connectionLog, - InfoRequests infoRequests, - Lazy infoSystem, - ServerInfo serverInfo - ) { - this.connectionLog = connectionLog; - this.infoSystem = infoSystem; - this.serverInfo = serverInfo; - setupAllowed = false; - dataServers = new HashMap<>(); - this.infoRequests = infoRequests; - } - - public InfoRequest getInfoRequest(String name) { - return infoRequests.get(name.toLowerCase()); - } - - public void setSetupAllowed(boolean setupAllowed) { - this.setupAllowed = setupAllowed; - } - - protected abstract Server selectServerForRequest(InfoRequest infoRequest) throws NoServersException; - - public boolean isSetupAllowed() { - return setupAllowed; - } - - public void sendInfoRequest(InfoRequest infoRequest) throws WebException { - Server server = selectServerForRequest(infoRequest); - sendInfoRequest(infoRequest, server); - } - - public void sendInfoRequest(InfoRequest infoRequest, Server toServer) throws WebException { - UUID serverUUID = serverInfo.getServerUUID(); - if (serverUUID.equals(toServer.getUuid())) { - infoSystem.get().runLocally(infoRequest); - } else { - new ConnectionOut(toServer, serverUUID, infoRequest, connectionLog).sendRequest(); - } - } - - public ConnectionLog getConnectionLog() { - return connectionLog; - } - - public abstract boolean isServerAvailable(); - - public abstract String getMainAddress(); - - public abstract void sendWideInfoRequest(WideRequest infoRequest) throws NoServersException; - - public List getDataServers() { - return new ArrayList<>(dataServers.values()); - } - - @Override - public void enable() { - infoRequests.initializeRequests(); - } - - @Override - public void disable() { - setupAllowed = false; - dataServers.clear(); - infoRequests.clear(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java deleted file mode 100644 index 7e5da8f27..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/InfoRequestPageHandler.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.connection; - -import com.djrapitops.plan.api.exceptions.connection.NotFoundException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.request.InfoRequest; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.pages.PageHandler; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse; -import com.djrapitops.plugin.logging.console.PluginLogger; -import com.djrapitops.plugin.utilities.Verify; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * PageHandler for /info/requestclassname pages. - *

    - * Used for answering info requests by other servers. - *

    - * requestclassname should be replaced with lowercase version of {@code Class.getSimpleName()} - * - * @author Rsl1122 - */ -@Singleton -public class InfoRequestPageHandler implements PageHandler { - - private final DBSystem dbSystem; - private final ConnectionSystem connectionSystem; - private final ResponseFactory responseFactory; - private final PluginLogger logger; - - @Inject - public InfoRequestPageHandler( - DBSystem dbSystem, - ConnectionSystem connectionSystem, - ResponseFactory responseFactory, PluginLogger logger - ) { - this.dbSystem = dbSystem; - this.connectionSystem = connectionSystem; - this.responseFactory = responseFactory; - this.logger = logger; - } - - @Override - public Response getResponse(Request request, RequestTarget target) throws WebException { - int responseCode = 200; - - try { - if (target.isEmpty()) { - return responseFactory.pageNotFound404(); - } - - if (!request.getRequestMethod().equals("POST")) { - return new BadRequestResponse("POST should be used for Info calls."); - } - - String requestName = target.get(0); - InfoRequest infoRequest = connectionSystem.getInfoRequest(requestName); - - Verify.nullCheck(infoRequest, () -> new NotFoundException("Info Request has not been registered.")); - - logger.debug("ConnectionIn: " + infoRequest.getClass().getSimpleName()); - return new ConnectionIn(request, infoRequest, dbSystem.getDatabase(), connectionSystem).handleRequest(); - } catch (WebException e) { - responseCode = getResponseCodeFor(e); - throw e; - } finally { - connectionSystem.getConnectionLog().logConnectionFrom(request.getRemoteAddress(), request.getTargetString(), responseCode); - } - } - - private int getResponseCodeFor(WebException e) { - return e.getResponseCode().getCode(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ProxyConnectionSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ProxyConnectionSystem.java deleted file mode 100644 index 03c8dd92c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ProxyConnectionSystem.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.connection; - -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.request.*; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.error.ErrorHandler; -import dagger.Lazy; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -/** - * ConnectionSystem for proxy servers. - * - * @author Rsl1122 - */ -@Singleton -public class ProxyConnectionSystem extends ConnectionSystem { - - private final DBSystem dbSystem; - private final Lazy webServer; - private final ErrorHandler errorHandler; - private final WebExceptionLogger webExceptionLogger; - - private long latestServerMapRefresh; - - @Inject - public ProxyConnectionSystem( - DBSystem dbSystem, - Lazy webServer, - ConnectionLog connectionLog, - InfoRequests infoRequests, - Lazy infoSystem, - ServerInfo serverInfo, - ErrorHandler errorHandler, - WebExceptionLogger webExceptionLogger - ) { - super(connectionLog, infoRequests, infoSystem, serverInfo); - this.dbSystem = dbSystem; - this.webServer = webServer; - this.errorHandler = errorHandler; - this.webExceptionLogger = webExceptionLogger; - latestServerMapRefresh = 0; - } - - private void refreshServerMap() { - if (latestServerMapRefresh < System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(15L)) { - try { - dataServers = dbSystem.getDatabase().query(ServerQueries.fetchPlanServerInformation()).entrySet().stream() - .filter(entry -> entry.getValue().isNotProxy()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - latestServerMapRefresh = System.currentTimeMillis(); - } catch (DBOpException e) { - errorHandler.log(L.ERROR, this.getClass(), e); - } - } - } - - @Override - protected Server selectServerForRequest(InfoRequest infoRequest) throws NoServersException { - refreshServerMap(); - Server server = null; - if (infoRequest instanceof CacheRequest - || infoRequest instanceof GenerateInspectPageRequest - || infoRequest instanceof GenerateInspectPluginsTabRequest) { - // Run locally - return serverInfo.getServer(); - } else if (infoRequest instanceof GenerateAnalysisPageRequest) { - UUID serverUUID = ((GenerateAnalysisPageRequest) infoRequest).getServerUUID(); - server = dataServers.get(serverUUID); - } - if (server == null) { - throw new NoServersException("Proper server is not available to process request: " + infoRequest.getClass().getSimpleName()); - } - return server; - } - - @Override - public void sendWideInfoRequest(WideRequest infoRequest) throws NoServersException { - refreshServerMap(); - if (dataServers.isEmpty()) { - throw new NoServersException("No Servers available to make wide-request: " + infoRequest.getClass().getSimpleName()); - } - for (Server server : dataServers.values()) { - webExceptionLogger.logIfOccurs(this.getClass(), () -> sendInfoRequest(infoRequest, server)); - } - // Quick hack for Bungee Plugins Tab - if (infoRequest instanceof GenerateInspectPluginsTabRequest) { - webExceptionLogger.logIfOccurs(this.getClass(), infoRequest::runLocally); - } - } - - @Override - public boolean isServerAvailable() { - return true; - } - - @Override - public String getMainAddress() { - return webServer.get().getAccessAddress(); - } - - @Override - public void enable() { - super.enable(); - refreshServerMap(); - } - -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ServerConnectionSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ServerConnectionSystem.java deleted file mode 100644 index 87f3c8c41..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/ServerConnectionSystem.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.connection; - -import com.djrapitops.plan.api.exceptions.connection.ConnectionFailException; -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.request.*; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.WebserverSettings; -import com.djrapitops.plan.system.webserver.WebServer; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.console.PluginLogger; -import dagger.Lazy; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - -/** - * Connection system for Bukkit servers. - * - * @author Rsl1122 - */ -@Singleton -public class ServerConnectionSystem extends ConnectionSystem { - - private final Locale locale; - private final PlanConfig config; - private final Processing processing; - private final DBSystem dbSystem; - private final Lazy webServer; - private final PluginLogger pluginLogger; - private final WebExceptionLogger webExceptionLogger; - - private long latestServerMapRefresh; - - private Server mainServer; - - @Inject - public ServerConnectionSystem( - Locale locale, - PlanConfig config, - Processing processing, - DBSystem dbSystem, - Lazy webServer, - ConnectionLog connectionLog, - InfoRequests infoRequests, - Lazy infoSystem, - ServerInfo serverInfo, - PluginLogger pluginLogger, - WebExceptionLogger webExceptionLogger - ) { - super(connectionLog, infoRequests, infoSystem, serverInfo); - this.locale = locale; - this.config = config; - this.processing = processing; - this.dbSystem = dbSystem; - this.webServer = webServer; - this.pluginLogger = pluginLogger; - this.webExceptionLogger = webExceptionLogger; - latestServerMapRefresh = 0; - } - - private void refreshServerMap() { - processing.submitNonCritical(() -> { - if (latestServerMapRefresh < System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(15L)) { - Database database = dbSystem.getDatabase(); - Map servers = database.query(ServerQueries.fetchPlanServerInformation()); - Optional proxy = servers.values().stream() - .filter(Server::isProxy) - .findFirst(); - mainServer = proxy.orElse(null); - - proxy.ifPresent(proxyServer -> servers.remove(proxyServer.getUuid())); - - dataServers = servers; - latestServerMapRefresh = System.currentTimeMillis(); - } - }); - } - - @Override - protected Server selectServerForRequest(InfoRequest infoRequest) throws NoServersException { - refreshServerMap(); - - if (mainServer == null && dataServers.isEmpty()) { - throw new NoServersException("Zero servers available to process requests."); - } - - Server server = null; - if (infoRequest instanceof CacheRequest || - infoRequest instanceof GenerateInspectPageRequest) { - server = mainServer; - } else if (infoRequest instanceof GenerateAnalysisPageRequest) { - UUID serverUUID = ((GenerateAnalysisPageRequest) infoRequest).getServerUUID(); - server = dataServers.get(serverUUID); - } - if (server == null) { - throw new NoServersException("Proper server is not available to process request: " + infoRequest.getClass().getSimpleName()); - } - return server; - } - - @Override - public void sendWideInfoRequest(WideRequest infoRequest) throws NoServersException { - if (dataServers.isEmpty()) { - throw new NoServersException("No Servers available to make wide-request: " + infoRequest.getClass().getSimpleName()); - } - for (Server server : dataServers.values()) { - webExceptionLogger.logIfOccurs(this.getClass(), () -> { - try { - sendInfoRequest(infoRequest, server); - } catch (ConnectionFailException ignored) { - /* Wide Requests are used when at least one result is wanted. */ - } - }); - } - } - - @Override - public boolean isServerAvailable() { - return mainServer != null; - } - - @Override - public String getMainAddress() { - return isServerAvailable() ? mainServer.getWebAddress() : serverInfo.getServer().getWebAddress(); - - } - - @Override - public void enable() { - super.enable(); - refreshServerMap(); - - boolean usingBungeeWebServer = isServerAvailable(); - boolean usingAlternativeIP = config.isTrue(WebserverSettings.SHOW_ALTERNATIVE_IP); - - if (!usingAlternativeIP && serverInfo.getServerProperties().getIp().isEmpty()) { - pluginLogger.log(L.INFO_COLOR, "§e" + locale.getString(PluginLang.ENABLE_NOTIFY_EMPTY_IP)); - } - if (usingBungeeWebServer && usingAlternativeIP) { - String webServerAddress = webServer.get().getAccessAddress(); - pluginLogger.info(locale.getString(PluginLang.ENABLE_NOTIFY_ADDRESS_CONFIRMATION, webServerAddress)); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/WebExceptionLogger.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/WebExceptionLogger.java deleted file mode 100644 index f86009702..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/connection/WebExceptionLogger.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.connection; - -import com.djrapitops.plan.api.exceptions.connection.*; -import com.djrapitops.plan.utilities.java.ThrowingVoidFunction; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.console.PluginLogger; -import com.djrapitops.plugin.logging.error.ErrorHandler; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * Class that decides what to do with WebExceptions. - * - * @author Rsl1122 - */ -@Singleton -public class WebExceptionLogger { - - private final ConnectionLog connectionLog; - private final PluginLogger logger; - private final ErrorHandler errorHandler; - - @Inject - public WebExceptionLogger( - ConnectionLog connectionLog, - PluginLogger logger, - ErrorHandler errorHandler - ) { - this.connectionLog = connectionLog; - this.logger = logger; - this.errorHandler = errorHandler; - } - - public void logIfOccurs(Class definingClass, ThrowingVoidFunction function) { - try { - function.apply(); - } catch (ConnectionFailException e) { - if (shouldLog(e)) { - logger.debug(e.getMessage()); - } - } catch (UnauthorizedServerException | NotFoundException | NoServersException e) { - logger.debug(e.getMessage()); - } catch (WebException e) { - errorHandler.log(L.WARN, definingClass, e); - } - } - - private boolean shouldLog(ConnectionFailException e) { - String address = getAddress(e); - if (address == null) { - return true; - } - Map> logEntries = connectionLog.getLogEntries(); - Map entries = logEntries.get("Out: " + address); - if (entries != null) { - List connections = new ArrayList<>(entries.values()); - Collections.sort(connections); - return connections.isEmpty() || connections.get(0).getResponseCode() != -1; - } - return true; - } - - private static String getAddress(ConnectionFailException e) { - if (e.getMessage().contains("to address")) { - String[] split = e.getMessage().split("to address: "); - if (split.length == 2) { - String[] split2 = split[1].split("
    "); - if (split2.length == 2) { - return split2[0]; - } - } - } - return null; - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java deleted file mode 100644 index 8ce23d583..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheAnalysisPageRequest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.export.HtmlExport; -import com.djrapitops.plan.system.export.JSONExport; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ExportSettings; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.pages.AnalysisPageResponse; -import com.djrapitops.plan.utilities.Base64Util; -import com.djrapitops.plugin.utilities.Verify; - -import java.util.Map; -import java.util.UUID; - -/** - * InfoRequest used to place HTML of a server to ResponseCache. - * - * @author Rsl1122 - */ -public class CacheAnalysisPageRequest extends InfoRequestWithVariables implements CacheRequest { - - private final PlanConfig config; - private final Processing processing; - private final HtmlExport htmlExport; - private final JSONExport jsonExport; - - private final UUID networkUUID; - - private UUID serverUUID; - private String html; - - CacheAnalysisPageRequest( - PlanConfig config, - Processing processing, - HtmlExport htmlExport, - JSONExport jsonExport, - UUID networkUUID - ) { - this.config = config; - this.processing = processing; - this.jsonExport = jsonExport; - this.networkUUID = networkUUID; - this.htmlExport = htmlExport; - } - - CacheAnalysisPageRequest( - UUID serverUUID, String html, - PlanConfig config, - Processing processing, - HtmlExport htmlExport, - JSONExport jsonExport, - UUID networkUUID - ) { - this.config = config; - this.processing = processing; - this.jsonExport = jsonExport; - this.networkUUID = networkUUID; - this.htmlExport = htmlExport; - - Verify.nullCheck(serverUUID, html); - this.serverUUID = serverUUID; - variables.put("html", Base64Util.encode(html)); - this.html = html; - } - - @Override - public Response handleRequest(Map variables) throws WebException { - // Available variables: sender, html (Base64) - - UUID sender = UUID.fromString(variables.get("sender")); - - String sentHtml = variables.get("html"); - Verify.nullCheck(sentHtml, () -> new BadRequestException("HTML 'html' variable not supplied in the request")); - - cache(sender, Base64Util.decode(sentHtml)); - return DefaultResponses.SUCCESS.get(); - } - - private void cache(UUID serverUUID, String html) { - ResponseCache.cacheResponse(PageId.SERVER.of(serverUUID), () -> new AnalysisPageResponse(html)); - if (!networkUUID.equals(serverUUID)) { - ResponseCache.clearResponse(PageId.SERVER.of(networkUUID)); - } - - if (config.get(ExportSettings.SERVER_PAGE)) { - processing.submitNonCritical(() -> { - htmlExport.exportNetworkPage(); - htmlExport.exportServer(serverUUID); - }); - } - if (config.get(ExportSettings.SERVER_JSON)) { - processing.submitNonCritical(() -> jsonExport.exportServerJSON(serverUUID)); - } - } - - @Override - public void runLocally() { - cache(serverUUID, html); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheInspectPageRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheInspectPageRequest.java deleted file mode 100644 index 1d1485b78..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheInspectPageRequest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.export.HtmlExport; -import com.djrapitops.plan.system.export.JSONExport; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ExportSettings; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.pages.InspectPageResponse; -import com.djrapitops.plan.utilities.Base64Util; -import com.djrapitops.plugin.utilities.Verify; -import org.apache.commons.text.StringSubstitutor; - -import java.util.Collections; -import java.util.Map; -import java.util.UUID; - -/** - * InfoRequest used to place HTML of a player to ResponseCache. - * - * @author Rsl1122 - */ -public class CacheInspectPageRequest extends InfoRequestWithVariables implements CacheRequest { - - private final PlanConfig config; - private final Processing processing; - private final ServerInfo serverInfo; - private final HtmlExport htmlExport; - private final JSONExport jsonExport; - - private UUID player; - private String html; - - CacheInspectPageRequest( - PlanConfig config, - Processing processing, - ServerInfo serverInfo, - HtmlExport htmlExport, - JSONExport jsonExport - ) { - this.config = config; - this.processing = processing; - this.serverInfo = serverInfo; - this.htmlExport = htmlExport; - this.jsonExport = jsonExport; - } - - CacheInspectPageRequest( - UUID player, String html, - PlanConfig config, - Processing processing, - ServerInfo serverInfo, - HtmlExport htmlExport, - JSONExport jsonExport - ) { - this.config = config; - this.processing = processing; - this.serverInfo = serverInfo; - this.htmlExport = htmlExport; - this.jsonExport = jsonExport; - - Verify.nullCheck(player, html); - variables.put("player", player.toString()); - variables.put("html", Base64Util.encode(html)); - this.player = player; - this.html = html; - } - - @Override - public Response handleRequest(Map variables) throws WebException { - // Available variables: sender, player, html (Base64) - - String player = variables.get("player"); - Verify.nullCheck(player, () -> new BadRequestException("Player UUID 'player' variable not supplied in the request.")); - UUID uuid = UUID.fromString(player); - - String html = variables.get("html"); - Verify.nullCheck(html, () -> new BadRequestException("HTML 'html' variable not supplied in the request")); - - Map replace = Collections.singletonMap("networkName", serverInfo.getServer().getName()); - cache(uuid, StringSubstitutor.replace(Base64Util.decode(html), replace)); - - return DefaultResponses.SUCCESS.get(); - } - - private void cache(UUID playerUUID, String html) { - ResponseCache.cacheResponse(PageId.PLAYER.of(playerUUID), () -> new InspectPageResponse(playerUUID, html)); - if (config.get(ExportSettings.PLAYER_PAGES)) { - processing.submitNonCritical(() -> htmlExport.exportCachedPlayerPage(playerUUID)); - } - if (config.get(ExportSettings.PLAYER_JSON)) { - processing.submitNonCritical(() -> jsonExport.exportPlayerJSON(playerUUID)); - } - } - - @Override - public void runLocally() { - cache(player, html); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheInspectPluginsTabRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheInspectPluginsTabRequest.java deleted file mode 100644 index 3d4725ad0..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheInspectPluginsTabRequest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.pages.parts.InspectPagePluginsContent; -import com.djrapitops.plan.utilities.Base64Util; -import com.djrapitops.plugin.utilities.Verify; - -import java.util.Map; -import java.util.UUID; - -/** - * InfoRequest used to place HTML of player's Plugins Tab to ResponseCache. - * - * @deprecated Marked for removal when the connection system will be removed. - * @author Rsl1122 - */ -@Deprecated -public class CacheInspectPluginsTabRequest extends InfoRequestWithVariables implements CacheRequest { - - private UUID player; - private String html; - - CacheInspectPluginsTabRequest() { - } - - CacheInspectPluginsTabRequest(UUID player, String nav, String html) { - Verify.nullCheck(player, nav); - variables.put("player", player.toString()); - variables.put("nav", nav); - variables.put("html", Base64Util.encode(html)); - this.player = player; - this.html = html; - } - - @Override - public Response handleRequest(Map variables) throws WebException { - // Available variables: sender, player, nav, html - - String player = variables.get("player"); - Verify.nullCheck(player, () -> new BadRequestException("Player UUID 'player' variable not supplied in the request.")); - UUID uuid = UUID.fromString(player); - - String nav = variables.get("nav"); - String html = variables.get("html"); - Verify.nullCheck(nav, () -> new BadRequestException("Nav HTML 'nav' variable not supplied in the request")); - Verify.nullCheck(html, () -> new BadRequestException("HTML 'html' variable not supplied in the request")); - - InspectPagePluginsContent pluginsTab = getPluginsTab(uuid); - - pluginsTab.addTab(nav, Base64Util.decode(html)); - return DefaultResponses.SUCCESS.get(); - } - - private InspectPagePluginsContent getPluginsTab(UUID uuid) { - return (InspectPagePluginsContent) ResponseCache.loadResponse(PageId.PLAYER_PLUGINS_TAB.of(uuid), InspectPagePluginsContent::new); - } - - @Override - public void runLocally() { - getPluginsTab(player).addTab(variables.get("nav"), html); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java deleted file mode 100644 index 2aa60a8b6..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CheckConnectionRequest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.ConnectionFailException; -import com.djrapitops.plan.api.exceptions.connection.GatewayException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plugin.utilities.Verify; - -import java.util.Map; -import java.util.UUID; - -/** - * InfoRequest used for Checking Bukkit-Bungee connections. - * - * @author Rsl1122 - */ -public class CheckConnectionRequest extends InfoRequestWithVariables { - - private final ServerInfo serverInfo; - private final ConnectionSystem connectionSystem; - - CheckConnectionRequest(String webServerAddress, ServerInfo serverInfo, ConnectionSystem connectionSystem) { - this.serverInfo = serverInfo; - this.connectionSystem = connectionSystem; - Verify.nullCheck(webServerAddress, () -> new IllegalArgumentException("webServerAddress can not be null.")); - - variables.put("address", webServerAddress); - variables.put("continue", "yes"); - } - - CheckConnectionRequest(ServerInfo serverInfo, ConnectionSystem connectionSystem) { - this.serverInfo = serverInfo; - this.connectionSystem = connectionSystem; - } - - @Override - public void runLocally() { - /* Won't be run */ - } - - @Override - public Response handleRequest(Map variables) throws WebException { - // Available variables: sender, address - - if (serverInfo.getServer().isProxy()) { - attemptConnection(variables); - } - - return DefaultResponses.SUCCESS.get(); - } - - private void attemptConnection(Map variables) throws WebException { - // Continue variable not present in rebound connection, leading to a single round ping. - boolean shouldNotContinue = variables.get("continue") == null; - if (shouldNotContinue) { - return; - } - - String address = variables.get("address"); - Verify.nullCheck(address, () -> new BadRequestException("WebServer Address ('address') not specified in the request.")); - - UUID serverUUID = UUID.fromString(variables.get("sender")); - - Server bukkit = new Server(-1, serverUUID, "", address, -1); - - try { - connectionSystem.sendInfoRequest(new CheckConnectionRequest(serverInfo, connectionSystem), bukkit); - } catch (ConnectionFailException e) { - throw new GatewayException(e.getMessage()); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateAnalysisPageRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateAnalysisPageRequest.java deleted file mode 100644 index 1e3b1637d..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateAnalysisPageRequest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.InternalErrorException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.connection.WebExceptionLogger; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.utilities.html.pages.PageFactory; -import com.djrapitops.plugin.utilities.Verify; - -import java.util.Collections; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * InfoRequest to generate Analysis page HTML at the receiving end. - * - * @author Rsl1122 - */ -public class GenerateAnalysisPageRequest extends InfoRequestWithVariables implements GenerateRequest { - - private final Processing processing; - private final WebExceptionLogger webExceptionLogger; - private final InfoRequestFactory infoRequestFactory; - private final ServerInfo serverInfo; - private final InfoSystem infoSystem; - private final PageFactory pageFactory; - - private AtomicBoolean runningAnalysis = new AtomicBoolean(false); - private UUID serverUUID; - - GenerateAnalysisPageRequest( - Processing processing, - WebExceptionLogger webExceptionLogger, - InfoRequestFactory infoRequestFactory, - ServerInfo serverInfo, - InfoSystem infoSystem, - PageFactory pageFactory - ) { - this.processing = processing; - this.webExceptionLogger = webExceptionLogger; - this.infoRequestFactory = infoRequestFactory; - this.serverInfo = serverInfo; - this.infoSystem = infoSystem; - this.pageFactory = pageFactory; - } - - GenerateAnalysisPageRequest( - UUID serverUUID, - Processing processing, - WebExceptionLogger webExceptionLogger, - InfoRequestFactory infoRequestFactory, - ServerInfo serverInfo, - InfoSystem infoSystem, - PageFactory pageFactory - ) { - this.processing = processing; - this.webExceptionLogger = webExceptionLogger; - this.infoRequestFactory = infoRequestFactory; - this.serverInfo = serverInfo; - this.infoSystem = infoSystem; - this.pageFactory = pageFactory; - - Verify.nullCheck(serverUUID); - this.serverUUID = serverUUID; - variables.put("server", serverUUID.toString()); - } - - @Override - public Response handleRequest(Map variables) throws WebException { - // Variables available: sender, server - - String server = variables.get("server"); - Verify.nullCheck(server, () -> new BadRequestException("Server UUID 'server' variable not supplied in the request.")); - - UUID serverUUID = UUID.fromString(server); - if (!serverInfo.getServerUUID().equals(serverUUID)) { - throw new BadRequestException("Requested Analysis page from wrong server."); - } - - if (!runningAnalysis.get()) { - runningAnalysis.set(true); - processing.submitNonCritical(() -> - webExceptionLogger.logIfOccurs(GenerateAnalysisPageRequest.class, () -> generateAndCache(serverUUID)) - ); - } - - return DefaultResponses.SUCCESS.get(); - } - - private void generateAndCache(UUID serverUUID) throws WebException { - infoSystem.sendRequest(infoRequestFactory.cacheAnalysisPageRequest(serverUUID, analyseAndGetHtml())); - } - - @Override - public void runLocally() throws WebException { - // Get the handler from ConnectionSystem and run the request. - // This is done to keep the concurrent analysis in check with runningAnalysis variable. - infoSystem.getConnectionSystem() - .getInfoRequest(this.getClass().getSimpleName()) - .handleRequest(Collections.singletonMap("server", serverUUID.toString())); - } - - private String analyseAndGetHtml() throws InternalErrorException { - try { - UUID serverUUID = serverInfo.getServerUUID(); - return pageFactory.analysisPage(serverUUID).toHtml(); - } catch (Exception e) { - throw new InternalErrorException("Analysis failed due to exception", e); - } finally { - runningAnalysis.set(false); - } - } - - public UUID getServerUUID() { - return serverUUID; - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPageRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPageRequest.java deleted file mode 100644 index 7ce7c8854..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPageRequest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.ParseException; -import com.djrapitops.plan.api.exceptions.connection.*; -import com.djrapitops.plan.api.exceptions.database.DBOpException; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import com.djrapitops.plan.utilities.html.pages.PageFactory; -import com.djrapitops.plugin.utilities.Verify; - -import java.util.Map; -import java.util.UUID; - -/** - * InfoRequest for Generating Inspect page on receiving WebServer. - * - * @author Rsl1122 - */ -public class GenerateInspectPageRequest extends InfoRequestWithVariables implements GenerateRequest { - - private final InfoRequestFactory infoRequestFactory; - private final ResponseFactory responseFactory; - private final PageFactory pageFactory; - private final InfoSystem infoSystem; - - private UUID playerUUID; - - GenerateInspectPageRequest( - InfoRequestFactory infoRequestFactory, - ResponseFactory responseFactory, PageFactory pageFactory, - InfoSystem infoSystem - ) { - this.infoRequestFactory = infoRequestFactory; - this.responseFactory = responseFactory; - this.pageFactory = pageFactory; - this.infoSystem = infoSystem; - } - - GenerateInspectPageRequest( - UUID uuid, - InfoRequestFactory infoRequestFactory, - ResponseFactory responseFactory, PageFactory pageFactory, - InfoSystem infoSystem - ) { - this.infoRequestFactory = infoRequestFactory; - this.responseFactory = responseFactory; - this.pageFactory = pageFactory; - this.infoSystem = infoSystem; - - Verify.nullCheck(uuid); - playerUUID = uuid; - variables.put("player", uuid.toString()); - } - - @Override - public Response handleRequest(Map variables) throws WebException { - // Available variables: sender, player - - String player = variables.get("player"); - Verify.nullCheck(player, () -> new BadRequestException("Player UUID 'player' variable not supplied in the request.")); - - UUID uuid = UUID.fromString(player); - - generateAndCache(uuid); - - return DefaultResponses.SUCCESS.get(); - } - - private void generateAndCache(UUID uuid) throws WebException { - String html; - try { - html = getHtml(uuid); - infoSystem.getConnectionSystem().sendWideInfoRequest(infoRequestFactory.generateInspectPluginsTabRequest(uuid)); - } catch (NotFoundException e) { - html = responseFactory.notFound404(e.getMessage()).getContent(); - } - infoSystem.sendRequest(infoRequestFactory.cacheInspectPageRequest(uuid, html)); - } - - @Override - public void runLocally() throws WebException { - generateAndCache(playerUUID); - } - - private String getHtml(UUID uuid) throws WebException { - try { - - return pageFactory.inspectPage(uuid).toHtml(); - - } catch (ParseException e) { - Throwable cause = e.getCause(); - if (cause instanceof DBOpException) { - throw new TransferDatabaseException((DBOpException) cause); - } else if (cause instanceof IllegalStateException && "Player profile was null!".equals(cause.getMessage())) { - throw new NotFoundException("Player has not played on this server."); - } else { - throw new WebFailException("Exception during HTML Parsing", cause); - } - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPluginsTabRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPluginsTabRequest.java deleted file mode 100644 index 2c0ae4166..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateInspectPluginsTabRequest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.utilities.html.pages.PageFactory; -import com.djrapitops.plugin.utilities.Verify; - -import java.util.Map; -import java.util.UUID; - -/** - * InfoRequest for Generating Inspect page plugins tab on receiving WebServer. - * - * @deprecated Marked for removal when the connection system will be removed. - * @author Rsl1122 - */ -@Deprecated -public class GenerateInspectPluginsTabRequest extends InfoRequestWithVariables implements GenerateRequest, WideRequest { - - private final InfoSystem infoSystem; - private final InfoRequestFactory infoRequestFactory; - private final PageFactory pageFactory; - - private UUID playerUUID; - - GenerateInspectPluginsTabRequest( - InfoSystem infoSystem, - InfoRequestFactory infoRequestFactory, - PageFactory pageFactory - ) { - this.infoSystem = infoSystem; - this.infoRequestFactory = infoRequestFactory; - this.pageFactory = pageFactory; - } - - GenerateInspectPluginsTabRequest( - UUID uuid, - InfoSystem infoSystem, - InfoRequestFactory infoRequestFactory, - PageFactory pageFactory - ) { - this(infoSystem, infoRequestFactory, pageFactory); - Verify.nullCheck(uuid); - playerUUID = uuid; - variables.put("player", uuid.toString()); - } - - @Override - public Response handleRequest(Map variables) throws WebException { - // Available variables: sender, player - - String player = variables.get("player"); - Verify.nullCheck(player, () -> new BadRequestException("Player UUID 'player' variable not supplied in the request.")); - - UUID uuid = UUID.fromString(player); - - generateAndCache(uuid); - - return DefaultResponses.SUCCESS.get(); - } - - private void generateAndCache(UUID uuid) throws WebException { - String[] navAndHtml = pageFactory.inspectPagePluginsContent(uuid).getContents(); - infoSystem.sendRequest(infoRequestFactory.cacheInspectPluginsTabRequest(uuid, navAndHtml[0], navAndHtml[1])); - } - - @Override - public void runLocally() throws WebException { - generateAndCache(playerUUID); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateRequest.java deleted file mode 100644 index 783536132..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/GenerateRequest.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -/** - * Interface for all InfoRequests that generate something. - * - * @author Rsl1122 - */ -public interface GenerateRequest extends InfoRequest { -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequest.java deleted file mode 100644 index 75de0c1f3..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.webserver.response.Response; - -import java.util.Map; - -/** - * Represents a request that Plan servers can send each other. - * - * @author Rsl1122 - */ -public interface InfoRequest { - - Response handleRequest(Map variables) throws WebException; - - void runLocally() throws WebException; - -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java deleted file mode 100644 index e7ef56a49..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestFactory.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.system.export.HtmlExport; -import com.djrapitops.plan.system.export.JSONExport; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.connection.WebExceptionLogger; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import com.djrapitops.plan.utilities.html.pages.PageFactory; -import com.djrapitops.plugin.logging.console.PluginLogger; -import com.djrapitops.plugin.task.RunnableFactory; -import dagger.Lazy; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.UUID; - -/** - * Factory for {@link InfoRequest} objects used for server-server communications. - * - * @author Rsl1122 - */ -@Singleton -public class InfoRequestFactory { - - private final Lazy plugin; - private final Lazy config; - private final Lazy processing; - private final Lazy infoSystem; - private final Lazy connectionSystem; - private final Lazy webExceptionLogger; - private final Lazy serverInfo; - private final Lazy responseFactory; - private final Lazy pageFactory; - private final Lazy htmlExport; - private final Lazy jsonExport; - private final Lazy logger; - private final Lazy runnableFactory; - - @Inject - public InfoRequestFactory( - Lazy plugin, - Lazy config, - Lazy processing, - Lazy infoSystem, - Lazy connectionSystem, - Lazy webExceptionLogger, - Lazy serverInfo, - Lazy responseFactory, - Lazy pageFactory, - Lazy htmlExport, - Lazy jsonExport, - Lazy logger, - Lazy runnableFactory - ) { - this.plugin = plugin; - this.config = config; - this.processing = processing; - this.infoSystem = infoSystem; - this.connectionSystem = connectionSystem; - this.webExceptionLogger = webExceptionLogger; - this.serverInfo = serverInfo; - this.responseFactory = responseFactory; - this.pageFactory = pageFactory; - this.htmlExport = htmlExport; - this.jsonExport = jsonExport; - this.logger = logger; - this.runnableFactory = runnableFactory; - } - - public CacheRequest cacheAnalysisPageRequest(UUID serverUUID, String html) { - return new CacheAnalysisPageRequest( - serverUUID, html, - config.get(), processing.get(), - htmlExport.get(), jsonExport.get(), - serverInfo.get().getServerUUID() - ); - } - - public CacheRequest cacheInspectPageRequest(UUID uuid, String html) { - return new CacheInspectPageRequest( - uuid, html, - config.get(), processing.get(), - serverInfo.get(), - htmlExport.get(), jsonExport.get() - ); - } - - @Deprecated - public CacheRequest cacheInspectPluginsTabRequest(UUID uuid, String nav, String html) { - return new CacheInspectPluginsTabRequest(uuid, nav, html); - } - - public GenerateRequest generateAnalysisPageRequest(UUID serverUUID) { - return new GenerateAnalysisPageRequest(serverUUID, processing.get(), webExceptionLogger.get(), this, serverInfo.get(), infoSystem.get(), pageFactory.get()); - } - - public GenerateRequest generateInspectPageRequest(UUID uuid) { - return new GenerateInspectPageRequest(uuid, this, responseFactory.get(), pageFactory.get(), infoSystem.get()); - } - - @Deprecated - public GenerateInspectPluginsTabRequest generateInspectPluginsTabRequest(UUID uuid) { - return new GenerateInspectPluginsTabRequest(uuid, infoSystem.get(), this, pageFactory.get()); - } - - public SaveDBSettingsRequest saveDBSettingsRequest() { - return new SaveDBSettingsRequest(plugin.get(), config.get(), serverInfo.get(), logger.get(), runnableFactory.get()); - } - - public SetupRequest sendDBSettingsRequest(String addressOfThisServer) { - return new SendDBSettingsRequest(addressOfThisServer, serverInfo.get(), this, connectionSystem.get()); - } - - public CheckConnectionRequest checkConnectionRequest(String webAddress) { - return new CheckConnectionRequest(webAddress, serverInfo.get(), connectionSystem.get()); - } - - @Singleton - public static class Handlers { - - private final InfoRequestFactory factory; - - @Inject - public Handlers(InfoRequestFactory factory) { - this.factory = factory; - } - - CacheRequest cacheAnalysisPageRequest() { - return new CacheAnalysisPageRequest( - factory.config.get(), - factory.processing.get(), - factory.htmlExport.get(), - factory.jsonExport.get(), - factory.serverInfo.get().getServerUUID() - ); - } - - CacheRequest cacheInspectPageRequest() { - return new CacheInspectPageRequest( - factory.config.get(), - factory.processing.get(), - factory.serverInfo.get(), - factory.htmlExport.get(), - factory.jsonExport.get() - ); - } - - CacheRequest cacheInspectPluginsTabRequest() { - return new CacheInspectPluginsTabRequest(); - } - - CheckConnectionRequest checkConnectionRequest() { - return new CheckConnectionRequest(factory.serverInfo.get(), factory.connectionSystem.get()); - } - - GenerateRequest generateAnalysisPageRequest() { - return new GenerateAnalysisPageRequest( - factory.processing.get(), - factory.webExceptionLogger.get(), - factory, - factory.serverInfo.get(), - factory.infoSystem.get(), - factory.pageFactory.get() - ); - } - - GenerateRequest generateInspectPageRequest() { - return new GenerateInspectPageRequest( - factory, - factory.responseFactory.get(), - factory.pageFactory.get(), - factory.infoSystem.get() - ); - } - - GenerateRequest generateInspectPluginsTabRequest() { - return new GenerateInspectPluginsTabRequest( - factory.infoSystem.get(), - factory, - factory.pageFactory.get() - ); - } - - SetupRequest saveDBSettingsRequest() { - return new SaveDBSettingsRequest( - factory.plugin.get(), - factory.config.get(), - factory.serverInfo.get(), - factory.logger.get(), - factory.runnableFactory.get() - ); - } - - SetupRequest sendDBSettingsRequest() { - return new SendDBSettingsRequest( - factory.serverInfo.get(), - factory, - factory.connectionSystem.get() - ); - } - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequests.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequests.java deleted file mode 100644 index bac4f52b3..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequests.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.HashMap; -import java.util.Map; - -/** - * Map object that holds {@link InfoRequest} objects used for handling incoming requests. - *

    - * Convenience class for Dagger injection. - * - * @author Rsl1122 - */ -@Singleton -public class InfoRequests { - - private final InfoRequestFactory.Handlers handlerFactory; - - private final Map requestHandlers; - - @Inject - public InfoRequests(InfoRequestFactory.Handlers handlerFactory) { - this.handlerFactory = handlerFactory; - this.requestHandlers = new HashMap<>(); - } - - public void initializeRequests() { - putRequest(handlerFactory.cacheAnalysisPageRequest()); - putRequest(handlerFactory.cacheInspectPageRequest()); - putRequest(handlerFactory.cacheInspectPluginsTabRequest()); - - putRequest(handlerFactory.generateAnalysisPageRequest()); - putRequest(handlerFactory.generateInspectPageRequest()); - putRequest(handlerFactory.generateInspectPluginsTabRequest()); - - putRequest(handlerFactory.saveDBSettingsRequest()); - putRequest(handlerFactory.sendDBSettingsRequest()); - putRequest(handlerFactory.checkConnectionRequest()); - } - - private void putRequest(InfoRequest request) { - requestHandlers.put(request.getClass().getSimpleName().toLowerCase(), request); - } - - public InfoRequest get(String name) { - return requestHandlers.get(name); - } - - public void clear() { - requestHandlers.clear(); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java deleted file mode 100644 index 9c3f6716d..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SaveDBSettingsRequest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.PlanPlugin; -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.InternalErrorException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DatabaseSettings; -import com.djrapitops.plan.system.settings.paths.PluginSettings; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse; -import com.djrapitops.plugin.api.TimeAmount; -import com.djrapitops.plugin.logging.console.PluginLogger; -import com.djrapitops.plugin.task.AbsRunnable; -import com.djrapitops.plugin.task.RunnableFactory; -import com.djrapitops.plugin.utilities.Verify; - -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -/** - * InfoRequest for sending Database config settings to Bukkit servers. - * - * @author Rsl1122 - */ -public class SaveDBSettingsRequest extends InfoRequestWithVariables implements SetupRequest { - - private final PlanPlugin plugin; - private final PlanConfig config; - private final ServerInfo serverInfo; - private final PluginLogger logger; - private final RunnableFactory runnableFactory; - - SaveDBSettingsRequest( - PlanPlugin plugin, - PlanConfig config, - ServerInfo serverInfo, PluginLogger logger, - RunnableFactory runnableFactory - ) { - this.plugin = plugin; - this.config = config; - this.serverInfo = serverInfo; - this.logger = logger; - this.runnableFactory = runnableFactory; - - variables.put("DB_TYPE", "mysql"); // DatabaseSettings.TYPE - variables.put("DB_HOST", config.get(DatabaseSettings.MYSQL_HOST)); - variables.put("DB_USER", config.get(DatabaseSettings.MYSQL_USER)); - variables.put("DB_PASS", config.get(DatabaseSettings.MYSQL_PASS)); - variables.put("DB_DATABASE", config.get(DatabaseSettings.MYSQL_DATABASE)); - variables.put("DB_PORT", config.get(DatabaseSettings.MYSQL_PORT)); - } - - @Override - public void runLocally() { - /* Won't be run */ - } - - @Override - public Response handleRequest(Map variables) throws WebException { - if (serverInfo.getServer().isProxy()) { - return new BadRequestResponse("Not supposed to be called on a proxy server"); - } - if (config.isFalse(PluginSettings.BUNGEE_COPY_CONFIG)) { - return new BadRequestResponse("Bungee config settings overridden on this server."); - } - - try { - setSettings(variables); - logger.info("----------------------------------"); - logger.info("The Received Bungee Database Settings, restarting Plan.."); - logger.info("----------------------------------"); - return DefaultResponses.SUCCESS.get(); - } finally { - runnableFactory.create("Bungee Setup Restart Task", new AbsRunnable() { - @Override - public void run() { - plugin.reloadPlugin(true); - } - }).runTaskLater(TimeAmount.toTicks(2L, TimeUnit.SECONDS)); - } - } - - private void setSettings(Map variables) throws BadRequestException, InternalErrorException { - String type = variables.get("DB_TYPE"); - String host = variables.get("DB_HOST"); - String user = variables.get("DB_USER"); - String pass = variables.get("DB_PASS"); - String database = variables.get("DB_DATABASE"); - String portS = variables.get("DB_PORT"); - - Verify.nullCheck(type, () -> new BadRequestException("DB_TYPE not specified in the request.")); - Verify.nullCheck(host, () -> new BadRequestException("DB_HOST not specified in the request.")); - Verify.nullCheck(user, () -> new BadRequestException("DB_USER not specified in the request.")); - Verify.nullCheck(pass, () -> new BadRequestException("DB_PASS not specified in the request.")); - Verify.nullCheck(database, () -> new BadRequestException("DB_DATABASE not specified in the request.")); - Verify.nullCheck(portS, () -> new BadRequestException("DB_PORT not specified in the request.")); - - config.set(DatabaseSettings.MYSQL_PORT, portS); - config.set(DatabaseSettings.TYPE, type); - config.set(DatabaseSettings.MYSQL_HOST, host); - config.set(DatabaseSettings.MYSQL_USER, user); - config.set(DatabaseSettings.MYSQL_PASS, pass); - config.set(DatabaseSettings.MYSQL_DATABASE, database); - try { - config.save(); - } catch (IOException e) { - throw new InternalErrorException("Failed to Save Config", e); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java deleted file mode 100644 index 50cd628d2..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SendDBSettingsRequest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.info.request; - -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.ConnectionFailException; -import com.djrapitops.plan.api.exceptions.connection.GatewayException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.system.info.connection.ConnectionSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.response.DefaultResponses; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.errors.BadRequestResponse; -import com.djrapitops.plugin.utilities.Verify; - -import java.net.SocketException; -import java.util.Map; -import java.util.UUID; - -/** - * InfoRequest used for requesting DB settings from Bungee. - * - * @author Rsl1122 - */ -public class SendDBSettingsRequest extends InfoRequestWithVariables implements SetupRequest { - - private final ServerInfo serverInfo; - private final InfoRequestFactory infoRequestFactory; - private final ConnectionSystem connectionSystem; - - SendDBSettingsRequest( - ServerInfo serverInfo, InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem - ) { - this.serverInfo = serverInfo; - this.infoRequestFactory = infoRequestFactory; - this.connectionSystem = connectionSystem; - } - - SendDBSettingsRequest( - String webServerAddress, - ServerInfo serverInfo, InfoRequestFactory infoRequestFactory, ConnectionSystem connectionSystem - ) { - this.serverInfo = serverInfo; - this.infoRequestFactory = infoRequestFactory; - this.connectionSystem = connectionSystem; - - Verify.nullCheck(webServerAddress, () -> new IllegalArgumentException("webServerAddress can not be null.")); - variables.put("address", webServerAddress); - } - - @Override - public void runLocally() { - /* Won't be run */ - } - - @Override - public Response handleRequest(Map variables) throws WebException { - // Available variables: sender, address - if (serverInfo.getServer().isNotProxy()) { - return new BadRequestResponse("Not supposed to be called on a non proxy server"); - } - - String address = variables.get("address"); - Verify.nullCheck(address, () -> new BadRequestException("WebServer Address ('address') not specified in the request.")); - - UUID serverUUID = UUID.fromString(variables.get("sender")); - - Server bukkit = new Server(-1, serverUUID, null, address, -1); - - try { - connectionSystem.sendInfoRequest(infoRequestFactory.saveDBSettingsRequest(), bukkit); - } catch (ConnectionFailException e) { - Throwable cause = e.getCause(); - if (!(cause instanceof SocketException) || !cause.getMessage().contains("Unexpected end of file from server")) { - throw new GatewayException(e.getMessage()); - } - } - - return DefaultResponses.SUCCESS.get(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java deleted file mode 100644 index 0a3c95763..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/Locale.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.locale; - -import com.djrapitops.plan.system.file.FileResource; -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.locale.lang.*; - -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * Represents loaded language information. - * - * @author Rsl1122 - */ -public class Locale extends HashMap { - - public static Locale forLangCodeString(PlanFiles files, String code) throws IOException { - return forLangCode(LangCode.fromString(code), files); - } - - private LangCode langCode; - - public Locale() { - this(LangCode.EN); - } - - public Locale(LangCode langCode) { - this.langCode = langCode; - } - - public static Locale forLangCode(LangCode code, PlanFiles files) throws IOException { - return new LocaleFileReader(files.getResourceFromJar("locale/" + code.getFileName())).load(code); - } - - public static Locale fromFile(File file) throws IOException { - return new LocaleFileReader(new FileResource(file.getName(), file)).load(LangCode.CUSTOM); - } - - public LangCode getLangCode() { - return langCode; - } - - @Override - public Message get(Object key) { - Message storedValue = super.get(key); - if (key instanceof Lang && storedValue == null) { - return new Message(((Lang) key).getDefault()); - } else { - return storedValue; - } - } - - public String getString(Lang key) { - return get(key).toString(); - } - - public String getString(Lang key, Serializable... values) { - return get(key).parse(values); - } - - public String[] getArray(Lang key) { - return get(key).toArray(); - } - - public String[] getArray(Lang key, Serializable... values) { - return get(key).toArray(values); - } - - public void loadFromAnotherLocale(Locale locale) { - putAll(locale); - this.langCode = locale.langCode; - } - - public String replaceMatchingLanguage(String from) { - if (isEmpty()) { - return from; - } - - String replaced = from; - - Lang[][] langs = new Lang[][]{ - NetworkPageLang.values(), - PlayerPageLang.values(), - ServerPageLang.values(), - CommonHtmlLang.values() - }; - - List> entries = Arrays.stream(langs) - .flatMap(Arrays::stream) - .collect(Collectors.toMap(Function.identity(), this::get)) - .entrySet().stream() - // Longest first so that entries that contain each other don't partially replace. - .sorted((one, two) -> Integer.compare( - two.getKey().getIdentifier().length(), - one.getKey().getIdentifier().length() - )).collect(Collectors.toList()); - - for (Entry entry : entries) { - String defaultValue = entry.getKey().getDefault(); - String replacement = entry.getValue().toString(); - - replaced = replaced.replace(defaultValue, replacement); - } - return replaced; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Locale)) return false; - if (!super.equals(o)) return false; - Locale locale = (Locale) o; - return langCode == locale.langCode; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), langCode); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/HealthInfoLang.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/HealthInfoLang.java deleted file mode 100644 index bc5698c44..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/HealthInfoLang.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.locale.lang; - -/** - * {@link Lang} enum for {@link com.djrapitops.plan.data.store.mutators.health.AbstractHealthInfo} related language. - * - * @author Rsl1122 - */ -public enum HealthInfoLang implements Lang { - REGULAR_ACTIVITY_REMAIN("Regular Activity Remain", " ${0} of regular players have remained active (${1}/${2})"), - REGULAR_CHANGE("Regular Activity Change", " Number of regular players has "), - REGULAR_CHANGE_INCREASE("Regular Activity Change Increase", "increased (+${0})"), - REGULAR_CHANGE_ZERO("Regular Activity Change Zero", "stayed the same (+${0})"), - REGULAR_CHANGE_DECREASE("Regular Activity Change Decrease", "decreased (${0})"), - ACTIVE_PLAY_COMPARISON_INCREASE("Active Playtime Comparison Increase", " Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4)"), - ACTIVE_PLAY_COMPARISON_DECREASE("Active Playtime Comparison Decrease", " Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4)"), - NEW_PLAYER_JOIN_PLAYERS_GOOD("New Player Join Players, Yes", " New Players have players to play with when they join (${0} on average)"), - NEW_PLAYER_JOIN_PLAYERS_BAD("New Player Join Players, No", " New Players may not have players to play with when they join (${0} on average)"), - NEW_PLAYER_STICKINESS("New Player Stickiness", " ${0} of new players have stuck around (${1}/${2})"), - TPS_ABOVE_LOW_THERSHOLD("TPS Above Low Threshold", " Average TPS was above Low Threshold ${0} of the time"), - TPS_LOW_DIPS("TPS Low Dips", " Average TPS dropped below Low Threshold (${0}) ${1} times"), - DOWNTIME("Downtime", " Total Server downtime (No Data) was ${0}"), - NO_SERVERS_INACCURACY("No Servers Inaccuracy", " No Bukkit/Sponge servers to gather session data - These measures are inaccurate."), - SINGLE_SERVER_INACCURACY("Single Servers Inaccuracy", " Single Bukkit/Sponge server to gather session data."), - PLAYER_VISIT_PER_SERVER("Player Visit Server", " players visit on servers per day/server on average."), - PLAYER_REGISTER_PER_SERVER("Player Register Server", " players register on servers per day/server on average."), - PLAYER_PLAY_ON_NETWORK("Player Play on Network", " players played on the network:"); - - private final String identifier; - private final String defaultValue; - - HealthInfoLang(String identifier, String defaultValue) { - this.identifier = identifier; - this.defaultValue = defaultValue; - } - - @Override - public String getIdentifier() { - return "Health - " + identifier; - } - - @Override - public String getDefault() { - return defaultValue; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PlayerPageLang.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PlayerPageLang.java deleted file mode 100644 index 9c36cdeb8..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/PlayerPageLang.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.locale.lang; - -/** - * {@link Lang} implementation for player.html replacement values. - * - * @author Rsl1122 - */ -public enum PlayerPageLang implements Lang { - ONLINE(" Online"), - OFFLINE(" Offline"), - TIMES_KICKED("Times Kicked"), - PLAYER_KILLS("Player Kills"), - MOB_KILLS("Mob Kills"), - DEATHS("Deaths"), - SESSIONS("Sessions"), - TOTAL_PLAYTIME("Total Playtime"), - PLAYTIME("Playtime"), - TOTAL_ACTIVE_TEXT("Total Active"), - TOTAL_AFK("Total AFK"), - SESSION_MEDIAN("Session Median"), - LONGEST("Longest"), - SESSION("Session"), - ACTIVITY_INDEX("Activity Index"), - INDEX_ACTIVE("Active"), - INDEX_VERY_ACTIVE("Very Active"), - INDEX_REGULAR("Regular"), - INDEX_IRREGULAR("Irregular"), - INDEX_INACTIVE("Inactive"), - FAVORITE_SERVER("Favorite Server"), - REGISTERED("REGISTERED"), - LAST_SEEN("LAST SEEN"), - PUNCH_CARD("Punchcard"), - SEEN_NICKNAMES("Seen Nicknames"), - NICKNAME("Nickname"), - SERVER("Server"), - LAST_SEEN_TEXT("Last Seen"), - CONNECTION_INFORMATION("Connection Information"), - IP_ADDRESS("IP-address"), - GEOLOCATION("Geolocation"), - LAST_CONNECTED("Last Connected"), - LOCAL_MACHINE("Local Machine"), - CALENDAR_TEXT(" Calendar"), - MOST_RECENT_SESSIONS("Most Recent Sessions"), - SESSION_ENDED("Session Ended"), - SESSION_LENGTH("Session Lenght"), - - WORLD(" World"), - SERVER_PREFERENCE("Server Preference"), - LAST_30_DAYS("LAST 30 DAYS"), - LAST_7_DAYS("LAST 7 DAYS"), - LAST_24_HOURS("LAST 24 HOURS"), - SERVERS("Servers"), - OPERATOR("Operator"), - BANNED("Banned"), - OVERVIEW("OVERVIEW"), - PLAYER_CAUSED_DEATHS("Player caused Deaths"), - MOB_CAUSED_DEATHS("Mob caused Deaths"), - MOB_KDR("Mob KDR"), - TIME(" Time"), - KILLED("Killed"), - WITH("

    With"), - KILLED_BY("Killed by"), - NO_KILLS("No Kills"), - NO_PLAYER_CAUSED_DEATHS("No Player caused Deaths"); - - private final String defaultValue; - - PlayerPageLang(String defaultValue) { - this.defaultValue = defaultValue; - } - - @Override - public String getIdentifier() { - return "HTML - " + name(); - } - - @Override - public String getDefault() { - return defaultValue; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ServerPageLang.java b/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ServerPageLang.java deleted file mode 100644 index 1c0642eb0..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/locale/lang/ServerPageLang.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.locale.lang; - -/** - * {@link Lang} implementation for player.html replacement values. - * - * @author Rsl1122 - */ -public enum ServerPageLang implements Lang { - SERVER_ANALYSIS("Server Analysis"), - PLAYERS_ONLINE("PLAYERS ONLINE"), - UNIQUE("UNIQUE"), - NEW("NEW"), - REGULAR("REGULAR"), - TOTAL_PLAYERS("Total Players"), - UNIQUE_PLAYERS_TEXT("Unique Players"), - NEW_PLAYERS_TEXT("New Players"), - PER_DAY("/ Day"), - LAST_PEAK("Last Peak"), - ALL_TIME_PEAK("All Time Peak"), - SERVER_INFORMATION("SERVER INFORMATION"), - USER_INFORMATION("USER INFORMATION"), - RECENT_LOGINS("RECENT LOGINS"), - ONLINE_ACTIVITY("ONLINE ACTIVITY"), - UNIQUE_PLAYERS("UNIQUE PLAYERS"), - CALENDAR("CALENDAR"), - PUNCHCARD("PUNCHCARD"), - UNIQUE_CALENDAR("Unique:"), - NEW_CALENDAR("New:"), - NEW_RETENTION("New Player Retention"), - PREDICETED_RETENTION("Predicted Retention"), - SERVER_HEALTH_ESTIMATE("Server Health Estimate"), - LAST_30_DAYS_TEXT("Last 30 Days"), - PLAYERBASE_DEVELOPMENT("Playerbase Development"), - CURRENT_PLAYERBASE("Current Playerbase"), - WORLD_PLAYTIME("World Playtime"), - WORLD_LOAD("WORLD LOAD"), - ALL("ALL"), - LOW_TPS_SPIKES("Low TPS Spikes"), - USAGE(" Usage"), - LOADED_ENTITIES("Loaded Entities"), - LOADED_CHUNKS("Loaded Chunks"), - ENTITIES("Entities"), - CHUNKS("Chunks"), - AVG("AVG"), - PLAYER_LIST("Player List"), - NAME(" Name"), - REGISTERED_TEXT("Registered"), - GEOLOCATION_TEXT("Geolocation"), - COUNTRY("Country"), - COMMNAND_USAGE("Command Usage"), - USED_COMMANDS("Used Commands"), - UNIQUE_TEXT("Unique"), - COMMAND(" Command"), - TIMES_USED("Times Used"), - FREE_DISK_SPACE("Free Disk Space"), - DISK_SPACE("DISK SPACE"), - ; - - private final String defaultValue; - - ServerPageLang(String defaultValue) { - this.defaultValue = defaultValue; - } - - @Override - public String getIdentifier() { - return "HTML - " + name(); - } - - @Override - public String getDefault() { - return defaultValue; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/InfoProcessors.java b/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/InfoProcessors.java deleted file mode 100644 index 4331bb963..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/InfoProcessors.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.processing.processors.info; - -import com.djrapitops.plan.system.export.HtmlExport; -import com.djrapitops.plan.system.export.JSONExport; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.connection.WebExceptionLogger; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plugin.command.Sender; -import dagger.Lazy; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.UUID; -import java.util.function.BiConsumer; - -/** - * Factory for creating Runnables related to {@link InfoSystem} to run with {@link com.djrapitops.plan.system.processing.Processing}. - * - * @author Rsl1122 - */ -@Singleton -public class InfoProcessors { - - private final Lazy config; - private final Lazy htmlExport; - private final Lazy jsonExport; - private final Lazy infoSystem; - private final Lazy webExceptionLogger; - - @Inject - public InfoProcessors( - Lazy config, - Lazy htmlExport, - Lazy jsonExport, - Lazy infoSystem, - Lazy webExceptionLogger - ) { - this.config = config; - this.htmlExport = htmlExport; - this.jsonExport = jsonExport; - this.infoSystem = infoSystem; - this.webExceptionLogger = webExceptionLogger; - } - - public InspectCacheRequestProcessor inspectCacheRequestProcessor( - UUID uuid, - Sender sender, - String playerName, - BiConsumer msgSender - ) { - return new InspectCacheRequestProcessor(uuid, sender, playerName, msgSender, - infoSystem.get(), webExceptionLogger.get() - ); - } - - public PlayerPageUpdateProcessor playerPageUpdateProcessor(UUID uuid) { - return new PlayerPageUpdateProcessor(uuid, config.get(), htmlExport.get(), jsonExport.get()); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/InspectCacheRequestProcessor.java b/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/InspectCacheRequestProcessor.java deleted file mode 100644 index 3a4c46d7d..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/InspectCacheRequestProcessor.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.processing.processors.info; - -import com.djrapitops.plan.api.exceptions.connection.ConnectionFailException; -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.api.exceptions.connection.NotFoundException; -import com.djrapitops.plan.api.exceptions.connection.UnauthorizedServerException; -import com.djrapitops.plan.system.cache.SessionCache; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.connection.WebExceptionLogger; -import com.djrapitops.plugin.command.Sender; - -import java.util.UUID; -import java.util.function.BiConsumer; - -/** - * Sends a request to cache players inspect page to the ResponseCache on the appropriate WebServer. - * - * @author Rsl1122 - */ -public class InspectCacheRequestProcessor implements Runnable { - - private final UUID uuid; - private final Sender sender; - private final String playerName; - private final BiConsumer msgSender; - - private final InfoSystem infoSystem; - private final WebExceptionLogger webExceptionLogger; - - InspectCacheRequestProcessor( - UUID uuid, - Sender sender, - String playerName, - BiConsumer msgSender, - InfoSystem infoSystem, - WebExceptionLogger webExceptionLogger - ) { - this.uuid = uuid; - this.sender = sender; - this.playerName = playerName; - this.msgSender = msgSender; - this.infoSystem = infoSystem; - this.webExceptionLogger = webExceptionLogger; - } - - @Override - public void run() { - SessionCache.refreshActiveSessionsState(); - webExceptionLogger.logIfOccurs(this.getClass(), () -> { - try { - infoSystem.generateAndCachePlayerPage(uuid); - msgSender.accept(sender, playerName); - } catch (ConnectionFailException | UnauthorizedServerException - | NotFoundException | NoServersException e) { - sender.sendMessage("§c" + e.getMessage()); - } - }); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/PlayerPageUpdateProcessor.java b/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/PlayerPageUpdateProcessor.java deleted file mode 100644 index 0f80614d9..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/processing/processors/info/PlayerPageUpdateProcessor.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.processing.processors.info; - -import com.djrapitops.plan.system.export.HtmlExport; -import com.djrapitops.plan.system.export.JSONExport; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.ExportSettings; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; - -import java.util.UUID; - -public class PlayerPageUpdateProcessor implements Runnable { - - private final UUID playerUUID; - - private final PlanConfig config; - private final HtmlExport htmlExport; - private final JSONExport jsonExport; - - PlayerPageUpdateProcessor( - UUID playerUUID, - PlanConfig config, - HtmlExport htmlExport, - JSONExport jsonExport - ) { - this.playerUUID = playerUUID; - this.config = config; - this.htmlExport = htmlExport; - this.jsonExport = jsonExport; - } - - @Override - public void run() { - ResponseCache.clearResponse(PageId.PLAYER.of(playerUUID)); - - if (config.get(ExportSettings.EXPORT_ON_ONLINE_STATUS_CHANGE)) { - if (config.get(ExportSettings.PLAYER_JSON)) { - jsonExport.exportPlayerJSON(playerUUID); - } - if (config.get(ExportSettings.PLAYER_PAGES)) { - htmlExport.exportPlayerPage(playerUUID); - } - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/PluginDataSettings.java b/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/PluginDataSettings.java deleted file mode 100644 index 6ba486b43..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/settings/paths/PluginDataSettings.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.settings.paths; - -import com.djrapitops.plan.system.settings.paths.key.Setting; -import com.djrapitops.plan.system.settings.paths.key.StringListSetting; -import com.djrapitops.plan.system.settings.paths.key.StringSetting; - -import java.util.List; - -/** - * {@link Setting} values that are in "Plugins" section. - * - * @author Rsl1122 - */ -public class PluginDataSettings { - - public static final Setting PLUGIN_BUYCRAFT_SECRET = new StringSetting("Plugins.BuyCraft.Secret"); - public static final Setting> HIDE_FACTIONS = new StringListSetting("Plugins.Factions.HideFactions"); - public static final Setting> HIDE_TOWNS = new StringListSetting("Plugins.Towny.HideTowns"); - - private PluginDataSettings() { - /* static variable class */ - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/PlayersPageRefreshTask.java b/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/PlayersPageRefreshTask.java deleted file mode 100644 index 852b77fd6..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/PlayersPageRefreshTask.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.tasks; - -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plugin.task.AbsRunnable; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class PlayersPageRefreshTask extends AbsRunnable { - - @Inject - public PlayersPageRefreshTask() { - // Inject constructor required for dagger - } - - @Override - public void run() { - ResponseCache.clearResponse(PageId.PLAYERS.id()); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/ServerTaskSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/ServerTaskSystem.java deleted file mode 100644 index 194aba116..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/ServerTaskSystem.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.tasks; - -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.system.tasks.server.BootAnalysisTask; -import com.djrapitops.plan.system.tasks.server.PeriodicAnalysisTask; -import com.djrapitops.plugin.api.TimeAmount; -import com.djrapitops.plugin.task.RunnableFactory; - -import java.util.concurrent.TimeUnit; - -/** - * Abstracted TaskSystem implementation for both Bukkit and Sponge. - * - * @author Rsl1122 - */ -public abstract class ServerTaskSystem extends TaskSystem { - - protected final PlanConfig config; - private final BootAnalysisTask bootAnalysisTask; - private final PeriodicAnalysisTask periodicAnalysisTask; - private final LogsFolderCleanTask logsFolderCleanTask; - private final PlayersPageRefreshTask playersPageRefreshTask; - - public ServerTaskSystem( - RunnableFactory runnableFactory, - TPSCountTimer tpsCountTimer, - PlanConfig config, - BootAnalysisTask bootAnalysisTask, - PeriodicAnalysisTask periodicAnalysisTask, - LogsFolderCleanTask logsFolderCleanTask, - PlayersPageRefreshTask playersPageRefreshTask) { - super(runnableFactory, tpsCountTimer); - this.config = config; - this.bootAnalysisTask = bootAnalysisTask; - this.periodicAnalysisTask = periodicAnalysisTask; - this.logsFolderCleanTask = logsFolderCleanTask; - this.playersPageRefreshTask = playersPageRefreshTask; - } - - @Override - public void enable() { - registerTasks(); - } - - private void registerTasks() { - // Analysis refresh settings - long analysisRefreshMs = config.get(TimeSettings.ANALYSIS_REFRESH_PERIOD); - boolean analysisRefreshTaskIsEnabled = analysisRefreshMs > 0; - long analysisPeriod = TimeAmount.toTicks(analysisRefreshMs, TimeUnit.MILLISECONDS); - - registerTask(tpsCountTimer).runTaskTimer(1000, TimeAmount.toTicks(1L, TimeUnit.SECONDS)); - registerTask(bootAnalysisTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS)); - - if (analysisRefreshTaskIsEnabled) { - registerTask(periodicAnalysisTask).runTaskTimerAsynchronously(analysisPeriod, analysisPeriod); - } - - registerTask(logsFolderCleanTask).runTaskLaterAsynchronously(TimeAmount.toTicks(30L, TimeUnit.SECONDS)); - registerTask(playersPageRefreshTask) - .runTaskTimerAsynchronously(TimeAmount.toTicks(5L, TimeUnit.MINUTES), TimeAmount.toTicks(5L, TimeUnit.MINUTES)); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/proxy/NetworkPageRefreshTask.java b/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/proxy/NetworkPageRefreshTask.java deleted file mode 100644 index bb477b574..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/proxy/NetworkPageRefreshTask.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.tasks.proxy; - -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plugin.task.AbsRunnable; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class NetworkPageRefreshTask extends AbsRunnable { - - private final ServerInfo serverInfo; - - @Inject - public NetworkPageRefreshTask(ServerInfo serverInfo) { - this.serverInfo = serverInfo; - } - - @Override - public void run() { - ResponseCache.clearResponse(PageId.SERVER.of(serverInfo.getServerUUID())); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/BootAnalysisTask.java b/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/BootAnalysisTask.java deleted file mode 100644 index ed29802be..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/BootAnalysisTask.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.tasks.server; - -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.connection.WebExceptionLogger; -import com.djrapitops.plan.system.info.request.InfoRequestFactory; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plugin.task.AbsRunnable; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class BootAnalysisTask extends AbsRunnable { - - private final InfoSystem infoSystem; - private final InfoRequestFactory infoRequestFactory; - private final ServerInfo serverInfo; - private final WebExceptionLogger webExceptionLogger; - - @Inject - public BootAnalysisTask( - InfoSystem infoSystem, - InfoRequestFactory infoRequestFactory, - ServerInfo serverInfo, - WebExceptionLogger webExceptionLogger - ) { - this.infoSystem = infoSystem; - this.infoRequestFactory = infoRequestFactory; - this.serverInfo = serverInfo; - this.webExceptionLogger = webExceptionLogger; - } - - @Override - public void run() { - try { - webExceptionLogger.logIfOccurs(this.getClass(), () -> - infoSystem.sendRequest(infoRequestFactory.generateAnalysisPageRequest(serverInfo.getServerUUID())) - ); - } catch (IllegalStateException ignore) { - /* Plugin was reloading */ - } finally { - cancel(); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/PeriodicAnalysisTask.java b/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/PeriodicAnalysisTask.java deleted file mode 100644 index 29a3f1d1d..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/tasks/server/PeriodicAnalysisTask.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.tasks.server; - -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.connection.WebExceptionLogger; -import com.djrapitops.plan.system.info.request.InfoRequestFactory; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plugin.logging.L; -import com.djrapitops.plugin.logging.console.PluginLogger; -import com.djrapitops.plugin.logging.error.ErrorHandler; -import com.djrapitops.plugin.task.AbsRunnable; - -import javax.inject.Inject; -import javax.inject.Singleton; - -@Singleton -public class PeriodicAnalysisTask extends AbsRunnable { - - private final InfoSystem infoSystem; - private final InfoRequestFactory infoRequestFactory; - private final ServerInfo serverInfo; - private final PluginLogger logger; - private final ErrorHandler errorHandler; - private final WebExceptionLogger webExceptionLogger; - - @Inject - public PeriodicAnalysisTask( - InfoSystem infoSystem, - InfoRequestFactory infoRequestFactory, ServerInfo serverInfo, - PluginLogger logger, - ErrorHandler errorHandler, - WebExceptionLogger webExceptionLogger - ) { - this.infoSystem = infoSystem; - this.infoRequestFactory = infoRequestFactory; - this.serverInfo = serverInfo; - this.logger = logger; - this.errorHandler = errorHandler; - this.webExceptionLogger = webExceptionLogger; - } - - @Override - public void run() { - try { - webExceptionLogger.logIfOccurs(this.getClass(), () -> - infoSystem.sendRequest(infoRequestFactory.generateAnalysisPageRequest(serverInfo.getServerUUID())) - ); - } catch (IllegalStateException ignore) { - /* Plugin was reloading */ - } catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) { - logger.error("Periodic Analysis Task Disabled due to error, reload Plan to re-enable."); - errorHandler.log(L.ERROR, this.getClass(), e); - cancel(); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java deleted file mode 100644 index a48c51d68..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/cache/ResponseCache.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.cache; - -import com.djrapitops.plan.system.webserver.response.Response; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; - -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -/** - * This class contains the page cache. - *

    - * It caches all Responses with their matching identifiers. - * This reduces CPU cycles and the time to wait for loading the pages. - * This is especially useful in situations where multiple clients are accessing the server. - * - * @author Fuzzlemann - */ -public class ResponseCache { - - private static final Cache cache = Caffeine.newBuilder() - .expireAfterWrite(5, TimeUnit.MINUTES) - .build(); - - /** - * Constructor used to hide the public constructor - */ - private ResponseCache() { - throw new IllegalStateException("Utility class"); - } - - /** - * Loads the response from the response cache. - *

    - * If the {@link Response} isn't cached, {@link Supplier#get()} in the {@code loader} - * is called to create the Response. - *

    - * If the Response is created, it's automatically cached. - * - * @param identifier The identifier of the page - * @param loader The The {@link Response} {@link Supplier} (How should it load the page if it's not cached) - * @return The Response that was cached or created by the the {@link Response} {@link Supplier} - */ - public static Response loadResponse(String identifier, Supplier loader) { - return cache.get(identifier, k -> loader.get()); - } - - /** - * Loads the page from the page cache. - * - * @param identifier The identifier of the page - * @return The Response that was cached or {@code null} if it wasn't - */ - public static Response loadResponse(String identifier) { - return cache.getIfPresent(identifier); - } - - /** - * Puts the page into the page cache. - *

    - * If the cache already inherits that {@code identifier}, it's renewed. - * - * @param identifier The identifier of the page - * @param loader The {@link Response} {@link Supplier} (How it should load the page) - */ - public static void cacheResponse(String identifier, Supplier loader) { - Response response = loader.get(); - if (response != null) { - cache.put(identifier, response); - } - } - - /** - * Checks if the page is cached. - * - * @param identifier The identifier of the page - * @return true if the page is cached - */ - public static boolean isCached(String identifier) { - return cache.getIfPresent(identifier) != null; - } - - /** - * Clears the cache from all its contents. - */ - public static void clearCache() { - cache.invalidateAll(); - } - - public static Set getCacheKeys() { - return cache.asMap().keySet(); - } - - public static long getEstimatedSize() { - return cache.estimatedSize(); - } - - public static void clearResponse(String identifier) { - cache.invalidate(identifier); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java deleted file mode 100644 index 0d449836e..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/PlayerPageHandler.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.pages; - -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.api.exceptions.connection.ForbiddenException; -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.data.WebUser; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.PlayerFetchQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; -import com.djrapitops.plan.system.webserver.response.pages.InspectPageResponse; -import com.djrapitops.plan.utilities.uuid.UUIDUtility; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.UUID; - -/** - * PageHandler for /player/PlayerName pages. - * - * @author Rsl1122 - */ -@Singleton -public class PlayerPageHandler implements PageHandler { - - private final ResponseFactory responseFactory; - private final DBSystem dbSystem; - private final InfoSystem infoSystem; - private final UUIDUtility uuidUtility; - - @Inject - public PlayerPageHandler( - ResponseFactory responseFactory, - DBSystem dbSystem, - InfoSystem infoSystem, - UUIDUtility uuidUtility - ) { - this.responseFactory = responseFactory; - this.dbSystem = dbSystem; - this.infoSystem = infoSystem; - this.uuidUtility = uuidUtility; - } - - @Override - public Response getResponse(Request request, RequestTarget target) throws WebException { - if (target.isEmpty()) { - return responseFactory.pageNotFound404(); - } - - String playerName = target.get(0); - UUID uuid = uuidUtility.getUUIDOf(playerName); - - boolean raw = target.size() >= 2 && target.get(1).equalsIgnoreCase("raw"); - - if (uuid == null) { - return responseFactory.uuidNotFound404(); - } - try { - 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"); - } - // TODO Move this Database dependency to PlayerPage generation in PageFactory instead. - if (dbSystem.getDatabase().query(PlayerFetchQueries.isPlayerRegistered(uuid))) { - if (raw) { - return ResponseCache.loadResponse(PageId.RAW_PLAYER.of(uuid), () -> responseFactory.rawPlayerPageResponse(uuid)); - } - return playerResponseOrNotFound(uuid); - } else { - return responseFactory.playerNotFound404(); - } - } catch (NoServersException e) { - ResponseCache.loadResponse(PageId.PLAYER.of(uuid), () -> responseFactory.notFound404(e.getMessage())); - } - return responseFactory.serverNotFound404(); - } - - private Response playerResponseOrNotFound(UUID uuid) throws WebException { - Response response = ResponseCache.loadResponse(PageId.PLAYER.of(uuid)); - if (!(response instanceof InspectPageResponse)) { - infoSystem.generateAndCachePlayerPage(uuid); - response = ResponseCache.loadResponse(PageId.PLAYER.of(uuid)); - } - return response != null ? response : responseFactory.playerNotFound404(); - } - - @Override - public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { - WebUser webUser = auth.getWebUser(); - return webUser.getPermLevel() <= 1 || webUser.getName().equalsIgnoreCase(target.get(target.size() - 1)); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java deleted file mode 100644 index b947bdbf1..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/ServerPageHandler.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.pages; - -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.api.exceptions.connection.ConnectionFailException; -import com.djrapitops.plan.api.exceptions.connection.ForbiddenException; -import com.djrapitops.plan.api.exceptions.connection.NoServersException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.processing.Processing; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Optional; -import java.util.UUID; - -/** - * PageHandler for /server and /network pages. - * - * @author Rsl1122 - */ -@Singleton -public class ServerPageHandler implements PageHandler { - - private final Processing processing; - private final ResponseFactory responseFactory; - private final DBSystem dbSystem; - private final ServerInfo serverInfo; - private final InfoSystem infoSystem; - - @Inject - public ServerPageHandler( - Processing processing, - ResponseFactory responseFactory, - DBSystem dbSystem, - ServerInfo serverInfo, - InfoSystem infoSystem - ) { - this.processing = processing; - this.responseFactory = responseFactory; - this.dbSystem = dbSystem; - this.serverInfo = serverInfo; - this.infoSystem = infoSystem; - } - - @Override - public Response getResponse(Request request, RequestTarget target) throws WebException { - UUID serverUUID = getServerUUID(target); - - boolean raw = target.size() >= 2 && target.get(1).equalsIgnoreCase("raw"); - if (raw) { - checkDBState(); - return ResponseCache.loadResponse(PageId.RAW_SERVER.of(serverUUID), () -> responseFactory.rawServerPageResponse(serverUUID)); - } - - Response response = ResponseCache.loadResponse(PageId.SERVER.of(serverUUID)); - - if (response != null) { - return response; - } else { - checkDBState(); - if (serverInfo.getServer().isProxy() && serverInfo.getServerUUID().equals(serverUUID)) { - return ResponseCache.loadResponse(PageId.SERVER.of(serverUUID), responseFactory::networkPageResponse); - } - return refreshNow(serverUUID); - } - } - - private void checkDBState() throws ForbiddenException { - 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"); - } - } - - // TODO Split responsibility so that this method does not call system to refresh and also render a refresh page. - private Response refreshNow(UUID serverUUID) { - processing.submitNonCritical(() -> { - try { - infoSystem.generateAnalysisPage(serverUUID); - } catch (NoServersException | ConnectionFailException e) { - ResponseCache.cacheResponse(PageId.SERVER.of(serverUUID), () -> responseFactory.notFound404(e.getMessage())); - } catch (WebException e) { - ResponseCache.cacheResponse(PageId.SERVER.of(serverUUID), () -> responseFactory.internalErrorResponse(e, "Failed to generate Analysis Page")); - } - }); - return responseFactory.refreshingAnalysisResponse(); - } - - private UUID getServerUUID(RequestTarget target) { - // Default to current server's page - UUID serverUUID = serverInfo.getServerUUID(); - - if (!target.isEmpty()) { - try { - String serverName = target.get(0); - Optional serverUUIDOptional = dbSystem.getDatabase() - .query(ServerQueries.fetchServerMatchingIdentifier(serverName)) - .map(Server::getUuid); - if (serverUUIDOptional.isPresent()) { - serverUUID = serverUUIDOptional.get(); - } - } catch (IllegalArgumentException ignore) { - /*ignored*/ - } - } - return serverUUID; - } - - @Override - public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { - return auth.getWebUser().getPermLevel() <= 0; - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java deleted file mode 100644 index f42363709..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.pages.json; - -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.containers.ServerPlayersTableContainersQuery; -import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerPlayerDataTableQuery; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DisplaySettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.UUID; - -/** - * Factory with different JSON parsing placed to a single class. - * - * @author Rsl1122 - */ -@Singleton -public class JSONFactory { - - private final PlanConfig config; - private final DBSystem dbSystem; - private final Formatters formatters; - - @Inject - public JSONFactory( - PlanConfig config, - DBSystem dbSystem, - Formatters formatters - ) { - this.config = config; - this.dbSystem = dbSystem; - this.formatters = formatters; - } - - public String serverPlayersTableJSON(UUID serverUUID) { - Integer xMostRecentPlayers = config.get(DisplaySettings.PLAYERS_PER_SERVER_PAGE); - Integer loginThreshold = config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD); - Long playtimeThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); - Boolean openPlayerLinksInNewTab = config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB); - - Database database = dbSystem.getDatabase(); - - return new PlayersTableJSONParser( - database.query(new ServerPlayersTableContainersQuery(serverUUID)), - database.query(new ExtensionServerPlayerDataTableQuery(serverUUID, xMostRecentPlayers)), - xMostRecentPlayers, playtimeThreshold, loginThreshold, openPlayerLinksInNewTab, - formatters - ).toJSONString(); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java deleted file mode 100644 index c426605db..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.pages.json; - -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.api.exceptions.connection.BadRequestException; -import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.db.access.queries.objects.ServerQueries; -import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.info.server.Server; -import com.djrapitops.plan.system.webserver.Request; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.pages.PageHandler; -import com.djrapitops.plan.system.webserver.response.Response; -import com.djrapitops.plan.system.webserver.response.data.JSONResponse; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Optional; -import java.util.UUID; - -/** - * JSON handler for different Player table JSON requests. - * - * @author Rsl1122 - * @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For JSON parsing of /server players table. - */ -@Singleton -public class PlayersTableJSONHandler implements PageHandler { - - private final DBSystem dbSystem; - private final JSONFactory jsonFactory; - - @Inject - public PlayersTableJSONHandler( - DBSystem dbSystem, - JSONFactory jsonFactory - ) { - this.jsonFactory = jsonFactory; - this.dbSystem = dbSystem; - } - - @Override - public Response getResponse(Request request, RequestTarget target) throws WebException { - UUID serverUUID = getServerUUID(target); // Can throw BadRequestException - return new JSONResponse(jsonFactory.serverPlayersTableJSON(serverUUID)); - } - - private UUID getServerUUID(RequestTarget target) throws BadRequestException { - Optional serverUUID = target.getParameter("serverUUID"); - if (serverUUID.isPresent()) { - return getServerUUIDDirectly(serverUUID.get()); - } else { - return getServerUUIDFromName(target); // Preferred - } - } - - private UUID getServerUUIDFromName(RequestTarget target) throws BadRequestException { - String serverName = target.getParameter("serverName") - .orElseThrow(() -> new BadRequestException("'serverName' parameter was not defined.")); - return dbSystem.getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverName)) - .map(Server::getUuid) - .orElseThrow(() -> new BadRequestException("'serverName' was not found in the database.: '" + serverName + "'")); - } - - private UUID getServerUUIDDirectly(String serverUUIDString) throws BadRequestException { - try { - return UUID.fromString(serverUUIDString); - } catch (IllegalArgumentException malformedUUIDException) { - throw new BadRequestException("'serverName' was not a valid UUID: " + malformedUUIDException.getMessage()); - } - } - - @Override - public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { - return auth.getWebUser().getPermLevel() <= 0; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java deleted file mode 100644 index b5690a138..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.pages.json; - -import com.djrapitops.plan.api.exceptions.WebUserAuthException; -import com.djrapitops.plan.system.webserver.RequestTarget; -import com.djrapitops.plan.system.webserver.auth.Authentication; -import com.djrapitops.plan.system.webserver.pages.TreePageHandler; -import com.djrapitops.plan.system.webserver.response.ResponseFactory; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * Root handler for different JSON end points. - * - * @author Rsl1122 - */ -@Singleton -public class RootJSONHandler extends TreePageHandler { - - @Inject - public RootJSONHandler( - ResponseFactory responseFactory, - PlayersTableJSONHandler playersTableJSONHandler - ) { - super(responseFactory); - - registerPage("players", playersTableJSONHandler); - } - - @Override - public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { - return true; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/GatewayErrorResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/GatewayErrorResponse.java deleted file mode 100644 index b126fff00..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/GatewayErrorResponse.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.response.errors; - -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; - -import java.io.IOException; - -/** - * ErrorResponse for GatewayException. - * - * @author Rsl1122 - */ -public class GatewayErrorResponse extends ErrorResponse { - - public GatewayErrorResponse(String message, VersionCheckSystem versionCheckSystem, PlanFiles files) throws IOException { - super(versionCheckSystem, files); - super.setHeader("HTTP/1.1 504 Gateway Error"); - super.setTitle("Failed to Connect (Gateway Error)"); - super.setParagraph(message); - super.replacePlaceholders(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/RefreshingAnalysisResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/RefreshingAnalysisResponse.java deleted file mode 100644 index 96c4ba2dd..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/RefreshingAnalysisResponse.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.response.errors; - -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; - -import java.io.IOException; - -/** - * This response is used when Analysis is being refreshed and the user needs some feedback. - * - * @author Rsl1122 - */ -public class RefreshingAnalysisResponse extends ErrorResponse { - - public RefreshingAnalysisResponse(VersionCheckSystem versionCheckSystem, PlanFiles files) throws IOException { - super(versionCheckSystem, files); - - setTitle("Analysis is being refreshed.."); - setParagraph(" Analysis is being run, refresh the page after a few seconds.. (F5)"); - replacePlaceholders(); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/UnauthorizedServerResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/UnauthorizedServerResponse.java deleted file mode 100644 index d92c778fe..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/errors/UnauthorizedServerResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.response.errors; - -import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.update.VersionCheckSystem; - -import java.io.IOException; - -/** - * Response when Server is not found in database when attempting to InfoRequest. - * - * @author Rsl1122 - */ -public class UnauthorizedServerResponse extends ErrorResponse { - public UnauthorizedServerResponse(String message, VersionCheckSystem versionCheckSystem, PlanFiles files) throws IOException { - super(versionCheckSystem, files); - super.setHeader("HTTP/1.1 412 Unauthorized"); - super.setTitle("Unauthorized Server"); - super.setParagraph(message); - super.replacePlaceholders(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/InspectPageResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/InspectPageResponse.java deleted file mode 100644 index ecea30248..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/InspectPageResponse.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.response.pages; - -import com.djrapitops.plan.system.webserver.cache.PageId; -import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.response.pages.parts.InspectPagePluginsContent; -import org.apache.commons.text.StringSubstitutor; - -import java.util.*; - -/** - * @author Rsl1122 - */ -public class InspectPageResponse extends PageResponse { - - private final UUID uuid; - - public InspectPageResponse(UUID uuid, String html) { - super.setHeader("HTTP/1.1 200 OK"); - super.setContent(html); - this.uuid = uuid; - } - - @Override - public String getContent() { - Map replaceMap = new HashMap<>(); - // PluginData compatibility - Optional pluginsTab = Optional.ofNullable((InspectPagePluginsContent) ResponseCache.loadResponse(PageId.PLAYER_PLUGINS_TAB.of(uuid))) - .map(InspectPagePluginsContent::getContents); - - replaceMap.put("navPluginsTabs", pluginsTab.map(nav -> nav[0]).orElse("")); - replaceMap.put("pluginsTabs", pluginsTab.map(tab -> tab[1]).orElse("")); - - return StringSubstitutor.replace(super.getContent(), replaceMap); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof InspectPageResponse)) return false; - if (!super.equals(o)) return false; - InspectPageResponse that = (InspectPageResponse) o; - return Objects.equals(uuid, that.uuid); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), uuid); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/NetworkPageResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/NetworkPageResponse.java deleted file mode 100644 index 57b00144c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/NetworkPageResponse.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.response.pages; - -import com.djrapitops.plan.api.exceptions.ParseException; -import com.djrapitops.plan.utilities.html.pages.NetworkPage; - -/** - * Response for /network page. - * - * @author Rsl1122 - */ -public class NetworkPageResponse extends PageResponse { - - public NetworkPageResponse(NetworkPage networkPage) throws ParseException { - setHeader("HTTP/1.1 200 OK"); - setContent(networkPage.toHtml()); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawServerDataResponse.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawServerDataResponse.java deleted file mode 100644 index 1befb0166..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/RawServerDataResponse.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.response.pages; - -import com.djrapitops.plan.data.store.containers.ServerContainer; - -/** - * Raw Data JSON response for a Server. - * - * @author Rsl1122 - * @deprecated Marked for removal in 5.0.0 - */ -@Deprecated -public class RawServerDataResponse extends RawDataResponse { - - public RawServerDataResponse(ServerContainer serverContainer) { - super(serverContainer); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/InspectPagePluginsContent.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/InspectPagePluginsContent.java deleted file mode 100644 index a5ec5e577..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/response/pages/parts/InspectPagePluginsContent.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.system.webserver.response.pages.parts; - -import com.djrapitops.plan.data.element.InspectContainer; -import com.djrapitops.plan.data.plugin.HookHandler; -import com.djrapitops.plan.data.plugin.PluginData; -import com.djrapitops.plan.system.info.server.ServerInfo; -import com.djrapitops.plan.system.webserver.response.pages.PageResponse; -import com.djrapitops.plan.utilities.comparators.PluginDataNameComparator; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.HtmlStructure; -import com.djrapitops.plan.utilities.html.structure.AnalysisPluginsTabContentCreator; - -import java.util.*; - -/** - * Represents Plugins tabs on Inspect page. - *

    - * Extends Response so that it can be stored in ResponseCache. - * - * @deprecated PluginData API has been deprecated - see https://github.com/plan-player-analytics/Plan/wiki/APIv5---DataExtension-API for new API. - * @author Rsl1122 - */ -@Deprecated -public class InspectPagePluginsContent extends PageResponse { - - // ServerUUID, {nav, html} - private final Map pluginsTab; - - public InspectPagePluginsContent() { - pluginsTab = new HashMap<>(); - } - - public InspectPagePluginsContent(String nav, String html) { - pluginsTab = new HashMap<>(); - addTab(nav, html); - } - - public static InspectPagePluginsContent generateForThisServer(UUID playerUUID, ServerInfo serverInfo, HookHandler hookHandler) { - String serverName = serverInfo.getServer().getName(); - String actualServerName = "Plan".equals(serverName) ? "Server " + serverInfo.getServer().getId() : serverName; - - Map containers = hookHandler.getInspectContainersFor(playerUUID); - if (containers.isEmpty()) { - return new InspectPagePluginsContent("

  • " + actualServerName + " (No Data)
  • ", - "
    " + - "
    " + Html.CARD.parse("

    No Data (" + actualServerName + ")

    ") + - "
    "); - } - - String nav = "
  • " + actualServerName + " (Legacy)
  • "; - String tab = createTab(containers); - - return new InspectPagePluginsContent(nav, tab); - } - - private static String createTab(Map containers) { - StringBuilder tab = new StringBuilder(); - tab.append("
    "); - - List order = new ArrayList<>(containers.keySet()); - order.sort(new PluginDataNameComparator()); - - for (PluginData pluginData : order) { - InspectContainer container = containers.get(pluginData); - AnalysisPluginsTabContentCreator.appendThird(pluginData, container, tab); - } - - tab.append("
    "); - return tab.toString(); - } - - public void addTab(String nav, String html) { - pluginsTab.put(nav, new String[]{nav, html}); - } - - public void addTab(InspectPagePluginsContent content) { - pluginsTab.putAll(content.pluginsTab); - } - - public String[] getContents() { - if (pluginsTab.isEmpty()) { - return HtmlStructure.createInspectPageTabContentCalculating(); - } - - List order = new ArrayList<>(pluginsTab.values()); - // Sort serverNames alphabetically - order.sort(Comparator.comparing(name -> name[0])); - - StringBuilder nav = new StringBuilder(); - StringBuilder tabs = new StringBuilder(); - for (String[] tab : order) { - nav.append(tab[0]); - tabs.append(tab[1]); - } - return new String[]{nav.toString(), tabs.toString()}; - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/MiscUtils.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/MiscUtils.java index 2dd51af50..1e7e038f7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/MiscUtils.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/MiscUtils.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.utilities; -import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.settings.Permissions; import com.djrapitops.plugin.command.CommandUtils; import com.djrapitops.plugin.command.Sender; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/PassEncryptUtil.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/PassEncryptUtil.java index e46ad8635..89ace6b39 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/PassEncryptUtil.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/PassEncryptUtil.java @@ -16,7 +16,8 @@ */ package com.djrapitops.plan.utilities; -import com.djrapitops.plan.api.exceptions.PassEncryptException; +import com.djrapitops.plan.exceptions.PassEncryptException; +import org.apache.commons.lang3.StringUtils; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; @@ -82,7 +83,7 @@ public class PassEncryptUtil { private static boolean verifyPassword(char[] password, String correctHash) throws CannotPerformOperationException, InvalidHashException { // Decode the hash into its parameters - String[] params = correctHash.split(":"); + String[] params = StringUtils.split(correctHash, ':'); if (params.length != HASH_SECTIONS) { throw new InvalidHashException( "Fields are missing from the password hash." diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestWithVariables.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/Predicates.java similarity index 61% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestWithVariables.java rename to Plan/common/src/main/java/com/djrapitops/plan/utilities/Predicates.java index b7bb0a104..030f7543b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/InfoRequestWithVariables.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/Predicates.java @@ -14,27 +14,28 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.request; +package com.djrapitops.plan.utilities; -import java.util.HashMap; -import java.util.Map; +import com.djrapitops.plan.delivery.domain.DateHolder; + +import java.util.function.Predicate; /** - * Abstract InfoRequest that contains variables in request body. - *

    - * Used to send request differently. + * Utility class for different Predicates used in the plugin. * * @author Rsl1122 */ -public abstract class InfoRequestWithVariables implements InfoRequest { +public class Predicates { - protected final Map variables; - - public InfoRequestWithVariables() { - this.variables = new HashMap<>(); + private Predicates() { + /* static method class */ } - public Map getVariables() { - return variables; + public static Predicate within(long after, long before) { + return holder -> { + long date = holder.getDate(); + return after < date && date <= before; + }; } + } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/DateHolderRecentComparator.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/DateHolderRecentComparator.java index a94d9dcdb..1d2c34c45 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/DateHolderRecentComparator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/DateHolderRecentComparator.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.utilities.comparators; -import com.djrapitops.plan.data.store.objects.DateHolder; +import com.djrapitops.plan.delivery.domain.DateHolder; import java.util.Comparator; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/LocaleEntryComparator.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/LocaleEntryComparator.java index 759b269f8..84d45838d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/LocaleEntryComparator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/LocaleEntryComparator.java @@ -16,8 +16,8 @@ */ package com.djrapitops.plan.utilities.comparators; -import com.djrapitops.plan.system.locale.Message; -import com.djrapitops.plan.system.locale.lang.Lang; +import com.djrapitops.plan.settings.locale.Message; +import com.djrapitops.plan.settings.locale.lang.Lang; import java.util.Comparator; import java.util.Map; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PieSliceComparator.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PieSliceComparator.java index 191229eae..6c74a5d4a 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PieSliceComparator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PieSliceComparator.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.utilities.comparators; -import com.djrapitops.plan.utilities.html.graphs.pie.PieSlice; +import com.djrapitops.plan.delivery.rendering.json.graphs.pie.PieSlice; import java.util.Comparator; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PlayerContainerLastPlayedComparator.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PlayerContainerLastPlayedComparator.java index 6080fdd11..f07c50223 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PlayerContainerLastPlayedComparator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PlayerContainerLastPlayedComparator.java @@ -16,8 +16,8 @@ */ package com.djrapitops.plan.utilities.comparators; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; +import com.djrapitops.plan.delivery.domain.container.PlayerContainer; +import com.djrapitops.plan.delivery.domain.keys.PlayerKeys; import java.util.Comparator; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PluginDataNameComparator.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PluginDataNameComparator.java deleted file mode 100644 index 3d093c50f..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PluginDataNameComparator.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.comparators; - -import com.djrapitops.plan.data.plugin.PluginData; - -import java.util.Comparator; - -/** - * Comparator for PluginData for Alphabetical Name order. - * - * @author Rsl1122 - */ -@Deprecated -public class PluginDataNameComparator implements Comparator { - - @Override - public int compare(PluginData u1, PluginData u2) { - return u1.getSourcePlugin().compareTo(u2.getSourcePlugin()); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PointComparator.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PointComparator.java index db159e59c..82e6cd4f3 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PointComparator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/PointComparator.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.utilities.comparators; -import com.djrapitops.plan.utilities.html.graphs.line.Point; +import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point; import java.util.Comparator; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/TPSComparator.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/TPSComparator.java index 1c23f5328..b10f2cc22 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/TPSComparator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/TPSComparator.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.utilities.comparators; -import com.djrapitops.plan.data.container.TPS; +import com.djrapitops.plan.gathering.domain.TPS; import java.util.Comparator; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/WebUserComparator.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/WebUserComparator.java index 173bfced0..856274b15 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/WebUserComparator.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/comparators/WebUserComparator.java @@ -16,7 +16,7 @@ */ package com.djrapitops.plan.utilities.comparators; -import com.djrapitops.plan.data.WebUser; +import com.djrapitops.plan.delivery.domain.WebUser; import java.util.Comparator; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/HtmlStructure.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/HtmlStructure.java deleted file mode 100644 index 0a7629da0..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/HtmlStructure.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html; - -import com.djrapitops.plan.utilities.html.icon.Color; -import com.djrapitops.plan.utilities.html.icon.Icon; -import com.djrapitops.plan.utilities.html.icon.Icons; -import org.apache.commons.text.TextStringBuilder; - -/** - * Class for parsing layout components of the websites. - * - * @author Rsl1122 - */ -public class HtmlStructure { - - private HtmlStructure() { - /* Static method class */ - } - - public static String separateWithDots(String... elements) { - TextStringBuilder builder = new TextStringBuilder(); - builder.appendWithSeparators(elements, " • "); - return builder.toString(); - } - - public static String createDotList(String... elements) { - if (elements.length == 0) { - return ""; - } - StringBuilder builder = new StringBuilder(); - for (String element : elements) { - if (element.isEmpty()) { - continue; - } - builder.append("• "); - builder.append(element); - builder.append("
    "); - } - return builder.toString(); - } - - @Deprecated - public static String[] createInspectPageTabContentCalculating() { - String tab = "

    " + - "
    " + - "
    " + - "
    " + - "

    Plugin Data

    " + - "
    " + - "

    Calculating Plugins tab, refresh (F5) shortly..

    " + - "
    " + - "
    "; - return new String[]{"
  • Calculating... Refresh shortly
  • ", tab}; - } - - public static String playerStatus(boolean online, boolean banned, boolean op) { - StringBuilder html = new StringBuilder("

    "); - if (online) { - html.append(Icon.called("circle").of(Color.GREEN)).append(" Online"); - } else { - html.append(Icon.called("circle").of(Color.RED)).append(" Offline"); - } - html.append("

    "); - if (op) { - html.append("

    ").append(Icons.OPERATOR).append(" Operator

    "); - } - if (banned) { - html.append("

    ").append(Icons.BANNED).append(" Banned

    "); - } - return html.toString(); - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/HtmlUtils.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/HtmlUtils.java deleted file mode 100644 index 870087f87..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/HtmlUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html; - -/** - * @author Rsl1122 - */ -public class HtmlUtils { - - /** - * Constructor used to hide the public constructor - */ - private HtmlUtils() { - throw new IllegalStateException("Utility class"); - } - - /** - * Attempts to remove XSS components. - * - * @param string String to remove XSS components from - * @return String but with the components removed - */ - public static String removeXSS(String string) { - return string.replace("", "").replace("", "").replace("" + - ""; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/RecentLoginList.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/RecentLoginList.java deleted file mode 100644 index b0ca88385..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/RecentLoginList.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.structure; - -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.utilities.comparators.SessionStartComparator; -import com.djrapitops.plan.utilities.formatting.Formatter; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -/** - * Utility class for creating recent login list html. - *

    - * This item can be seen on the Information tab on Server page. - * - * @author Rsl1122 - */ -public class RecentLoginList { - - private final List players; - - private final Formatter secondLongFormatter; - - public RecentLoginList(List players, Formatter secondLongFormatter) { - this.players = players; - this.secondLongFormatter = secondLongFormatter; - } - - public String toHtml() { - List recentLogins = getMostRecentLogins(); - - if (recentLogins.isEmpty()) { - return "

  • No Recent Logins
  • "; - } - - StringBuilder html = new StringBuilder(); - int i = 0; - for (RecentLogin recentLogin : recentLogins) { - if (i >= 20) { - break; - } - - String name = recentLogin.name; - String url = PlanAPI.getInstance().getPlayerInspectPageLink(name); - boolean isNew = recentLogin.isNew; - String start = secondLongFormatter.apply(recentLogin.date); - - html.append("
  • ").append(name).append("").append(start).append("
  • "); - - i++; - } - - return html.toString(); - } - - private List getMostRecentLogins() { - List recentLogins = new ArrayList<>(); - for (PlayerContainer player : players) { - if (!player.supports(PlayerKeys.NAME) - || !player.supports(PlayerKeys.SESSIONS)) { - continue; - } - String name = player.getUnsafe(PlayerKeys.NAME); - long registerDate = player.getValue(PlayerKeys.REGISTERED).orElse(0L); - - List sessions = player.getValue(PlayerKeys.SESSIONS).orElse(Collections.emptyList()); - if (sessions.isEmpty()) { - continue; - } - sessions.sort(new SessionStartComparator()); - Session session = sessions.get(0); - - if (!session.supports(SessionKeys.START)) { - continue; - } - long mostRecentStart = session.getUnsafe(SessionKeys.START); - boolean isFirstSession = Math.abs(registerDate - mostRecentStart) < TimeUnit.SECONDS.toMillis(10L); - recentLogins.add(new RecentLogin(mostRecentStart, isFirstSession, name)); - } - return recentLogins; - } - - class RecentLogin { - final long date; - final boolean isNew; - final String name; - - RecentLogin(long date, boolean isNew, String name) { - this.date = date; - this.isNew = isNew; - this.name = name; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof RecentLogin)) return false; - RecentLogin that = (RecentLogin) o; - return date == that.date && - isNew == that.isNew && - Objects.equals(name, that.name); - } - - @Override - public int hashCode() { - return Objects.hash(date, isNew, name); - } - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/ServerAccordion.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/ServerAccordion.java deleted file mode 100644 index 2f052c905..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/ServerAccordion.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.structure; - -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.containers.PerServerContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PerServerKeys; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.settings.theme.ThemeVal; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.graphs.Graphs; -import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie; -import com.djrapitops.plan.utilities.html.icon.Color; -import com.djrapitops.plan.utilities.html.icon.Icon; -import com.djrapitops.plan.utilities.html.icon.Icons; -import com.djrapitops.plugin.utilities.Format; - -import java.util.Map; -import java.util.Optional; -import java.util.UUID; - -/** - * HTML utility class for creating a Server Accordion. - * - * @author Rsl1122 - */ -public class ServerAccordion extends Accordion { - - private final StringBuilder viewScript; - - private final Map serverNames; - private PerServerContainer perServer; - - private final Theme theme; - private final Graphs graphs; - private final Formatter yearLongFormatter; - private final Formatter timeAmountFormatter; - - public ServerAccordion( - PlayerContainer container, Map serverNames, - Theme theme, - Graphs graphs, - Formatter yearLongFormatter, - Formatter timeAmountFormatter - ) { - super("server_accordion"); - this.theme = theme; - this.graphs = graphs; - this.yearLongFormatter = yearLongFormatter; - this.timeAmountFormatter = timeAmountFormatter; - - viewScript = new StringBuilder(); - - this.serverNames = serverNames; - Optional perServerData = container.getValue(PlayerKeys.PER_SERVER); - if (perServerData.isPresent()) { - perServer = perServerData.get(); - } else { - return; - } - - addElements(); - } - - public String toViewScript() { - return viewScript.toString(); - } - - private void addElements() { - int i = 0; - - for (Map.Entry entry : perServer.entrySet()) { - UUID serverUUID = entry.getKey(); - DataContainer container = entry.getValue(); - String serverName = serverNames.getOrDefault(serverUUID, "Unknown"); - WorldTimes worldTimes = container.getValue(PerServerKeys.WORLD_TIMES).orElse(new WorldTimes()); - SessionsMutator sessionsMutator = SessionsMutator.forContainer(container); - - boolean banned = container.getValue(PerServerKeys.BANNED).orElse(false); - boolean operator = container.getValue(PerServerKeys.OPERATOR).orElse(false); - long registered = container.getValue(PerServerKeys.REGISTERED).orElse(0L); - - long playtime = sessionsMutator.toPlaytime(); - long afkTime = sessionsMutator.toAfkTime(); - int sessionCount = sessionsMutator.count(); - long sessionMedian = sessionsMutator.toMedianSessionLength(); - long longestSession = sessionsMutator.toLongestSessionLength(); - - long mobKills = sessionsMutator.toMobKillCount(); - long playerKills = sessionsMutator.toPlayerKillCount(); - long deaths = sessionsMutator.toDeathCount(); - - String play = timeAmountFormatter.apply(playtime); - String afk = timeAmountFormatter.apply(afkTime); - String median = timeAmountFormatter.apply(sessionMedian); - String longest = timeAmountFormatter.apply(longestSession); - - String sanitizedServerName = new Format(serverName) - .removeSymbols() - .removeWhitespace().toString() + i; - String htmlID = "server_" + sanitizedServerName; - - String worldId = "worldPieServer" + sanitizedServerName; - - WorldPie worldPie = graphs.pie().worldPie(worldTimes); - - String title = serverName + "" + play + ""; - - String leftSide = new AccordionElementContentBuilder() - .addRowBold(Icons.OPERATOR, "Operator", operator ? "Yes" : "No") - .addRowBold(Icons.BANNED, "Banned", banned ? "Yes" : "No") - .addRowBold(Icon.called("user-plus").of(Color.LIGHT_GREEN), "Registered", yearLongFormatter.apply(registered)) - .addBreak() - .addRowBold(Icons.SESSION_COUNT, "Sessions", sessionCount) - .addRowBold(Icons.PLAYTIME, "Server Playtime", play) - .addRowBold(Icons.AFK_LENGTH, "Time AFK", afk) - .addRowBold(Icons.SESSION_LENGTH, "Longest Session", longest) - .addRowBold(Icons.SESSION_LENGTH, "Session Median", median) - .addBreak() - .addRowBold(Icons.PLAYER_KILLS, "Player Kills", playerKills) - .addRowBold(Icons.MOB_KILLS, "Mob Kills", mobKills) - .addRowBold(Icons.DEATHS, "Deaths", deaths) - .toHtml(); - - String rightSide = "
    " + - ""; - - addElement(new AccordionElement(htmlID, title) - .setColor(theme.getValue(ThemeVal.PARSED_SERVER_ACCORDION)) - .setLeftSide(leftSide) - .setRightSide(rightSide)); - - viewScript.append("worldPie(") - .append(worldId).append(", ") - .append(worldId).append("series, ") - .append(worldId).append("gmseries") - .append(");"); - - i++; - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/SessionAccordion.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/SessionAccordion.java deleted file mode 100644 index d4c57c874..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/structure/SessionAccordion.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.structure; - -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.data.time.WorldTimes; -import com.djrapitops.plan.system.settings.config.WorldAliasSettings; -import com.djrapitops.plan.system.settings.theme.Theme; -import com.djrapitops.plan.system.settings.theme.ThemeVal; -import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.HtmlStructure; -import com.djrapitops.plan.utilities.html.graphs.Graphs; -import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie; -import com.djrapitops.plan.utilities.html.icon.Icons; -import com.djrapitops.plan.utilities.html.tables.HtmlTables; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.function.Supplier; - -/** - * Utility for creating Session accordion html and javascript from Session objects. - * - * @author Rsl1122 - * @see com.djrapitops.plan.data.container.Session for object - */ -public class SessionAccordion extends Accordion { - - private final boolean forPlayer; - private final List sessions; - private final Supplier> serverNamesSupplier; - private final Supplier> playerNamesSupplier; - - private final StringBuilder viewScript; - private final boolean appendWorldPercentage; - private final int maxSessions; - - private final WorldAliasSettings worldAliasSettings; - private final Theme theme; - private final Graphs graphs; - private final HtmlTables tables; - private final Formatter yearFormatter; - private final Formatter timeAmountFormatter; - - SessionAccordion(boolean forPlayer, List sessions, - Supplier> serverNamesSupplier, - Supplier> playerNamesSupplier, - boolean appendWorldPercentage, - int maxSessions, - WorldAliasSettings worldAliasSettings, - Theme theme, - Graphs graphs, - HtmlTables tables, - Formatter yearFormatter, - Formatter timeAmountFormatter - ) { - super("session_accordion"); - - this.forPlayer = forPlayer; - this.sessions = sessions; - this.serverNamesSupplier = serverNamesSupplier; - this.playerNamesSupplier = playerNamesSupplier; - this.appendWorldPercentage = appendWorldPercentage; - this.maxSessions = maxSessions; - this.worldAliasSettings = worldAliasSettings; - this.theme = theme; - this.graphs = graphs; - this.tables = tables; - this.yearFormatter = yearFormatter; - this.timeAmountFormatter = timeAmountFormatter; - viewScript = new StringBuilder(); - - addElements(); - } - - public String toViewScript() { - return viewScript.toString(); - } - - private void addElements() { - if (forPlayer) { - addElementsForPlayer(); - } else { - addElementsForServer(); - } - // Requires refactoring of Session object to contain information about player and server - } - - private void addElementsForServer() { - Map serverNames = serverNamesSupplier.get(); - Map playerNames = playerNamesSupplier.get(); - sessions.sort(new DateHolderRecentComparator()); - - int i = 0; - for (Session session : sessions) { - if (i >= maxSessions) { - break; - } - - String serverName = serverNames.getOrDefault(session.getValue(SessionKeys.SERVER_UUID).orElse(null), "Unknown"); - String playerName = playerNames.getOrDefault(session.getValue(SessionKeys.UUID).orElse(null), "Unknown"); - String sessionStart = yearFormatter.apply(session); - - WorldTimes worldTimes = session.getValue(SessionKeys.WORLD_TIMES).orElse(new WorldTimes()); - WorldPie worldPie = graphs.pie().worldPie(worldTimes); - String longestWorldPlayed = worldAliasSettings.getLongestWorldPlayed(session); - - boolean hasEnded = session.supports(SessionKeys.END); - String sessionEnd = hasEnded ? yearFormatter.apply(() -> session.getUnsafe(SessionKeys.END)) : "Online"; - - String length = (hasEnded ? "" : "(Online) ") + timeAmountFormatter.apply(session.getValue(SessionKeys.LENGTH).orElse(0L)); - String afk = (hasEnded ? "" : "(Inaccurate) ") + timeAmountFormatter.apply(session.getValue(SessionKeys.AFK_TIME).orElse(0L)); - - int playerKillCount = session.getValue(SessionKeys.PLAYER_KILL_COUNT).orElse(0); - int mobKillCount = session.getValue(SessionKeys.MOB_KILL_COUNT).orElse(0); - int deaths = session.getValue(SessionKeys.DEATH_COUNT).orElse(0); - - String info = appendWorldPercentage - ? HtmlStructure.separateWithDots(sessionStart, longestWorldPlayed) - : sessionStart; - String title = HtmlStructure.separateWithDots(playerName, info) + "" + length + ""; - String htmlID = "" + session.getValue(SessionKeys.START).orElse(0L) + i; - String worldHtmlID = "worldPieSession" + htmlID; - - String leftSide = new AccordionElementContentBuilder() - .addRowBold(Icons.SESSION_LENGTH, "Session Ended", sessionEnd) - .addRowBold(Icons.PLAYTIME, "Session Length", length) - .addRowBold(Icons.AFK_LENGTH, "AFK", afk) - .addRowBold(Icons.SERVER, "Server", serverName) - .addBreak() - .addRowBold(Icons.PLAYER_KILLS, "Player Kills", playerKillCount) - .addRowBold(Icons.MOB_KILLS, "Mob Kills", mobKillCount) - .addRowBold(Icons.DEATHS, "Deaths", deaths) - .toHtml(); - - String rightSide = "
    " + - ""; - viewScript.append("worldPie(") - .append(worldHtmlID).append(", ") - .append(worldHtmlID).append("series, ") - .append(worldHtmlID).append("gmseries") - .append(");"); - - List kills = session.getValue(SessionKeys.PLAYER_KILLS).orElse(new ArrayList<>()); - String leftBottom = tables.killsTable(kills, null).parseHtml(); - - String link = PlanAPI.getInstance().getPlayerInspectPageLink(playerName); - String rightBottom = ""; - - addElement(new AccordionElement(htmlID, title) - .setColor(theme.getValue(ThemeVal.PARSED_SESSION_ACCORDION)) - .setLeftSide(leftSide + leftBottom) - .setRightSide(rightSide + rightBottom)); - i++; - } - } - - private void addElementsForPlayer() { - Map serverNames = serverNamesSupplier.get(); - sessions.sort(new DateHolderRecentComparator()); - - int i = 0; - for (Session session : new ArrayList<>(sessions)) { - if (i >= maxSessions) { - break; - } - - String serverName = serverNames.getOrDefault(session.getValue(SessionKeys.SERVER_UUID).orElse(null), "Unknown"); - String sessionStart = yearFormatter.apply(session); - - WorldTimes worldTimes = session.getValue(SessionKeys.WORLD_TIMES).orElse(new WorldTimes()); - WorldPie worldPie = graphs.pie().worldPie(worldTimes); - String longestWorldPlayed = worldAliasSettings.getLongestWorldPlayed(session); - - boolean hasEnded = session.supports(SessionKeys.END); - String sessionEnd = hasEnded ? yearFormatter.apply(() -> session.getValue(SessionKeys.END).orElse(0L)) : "Online"; - - String length = (hasEnded ? "" : "(Online) ") + timeAmountFormatter.apply(session.getValue(SessionKeys.LENGTH).orElse(0L)); - String afk = (hasEnded ? "" : "(Inaccurate) ") + timeAmountFormatter.apply(session.getValue(SessionKeys.AFK_TIME).orElse(0L)); - - int playerKillCount = session.getValue(SessionKeys.PLAYER_KILL_COUNT).orElse(0); - int mobKillCount = session.getValue(SessionKeys.MOB_KILL_COUNT).orElse(0); - int deaths = session.getValue(SessionKeys.DEATH_COUNT).orElse(0); - - String info = appendWorldPercentage - ? HtmlStructure.separateWithDots(sessionStart, longestWorldPlayed) - : sessionStart; - String title = HtmlStructure.separateWithDots(serverName, info) + "" + length + ""; - String htmlID = "" + session.getValue(SessionKeys.START).orElse(0L) + i; - String worldHtmlID = "worldPieSession" + htmlID; - - String leftSide = new AccordionElementContentBuilder() - .addRowBold(Icons.SESSION_LENGTH, "Session Ended", sessionEnd) - .addRowBold(Icons.PLAYTIME, "Session Length", length) - .addRowBold(Icons.AFK_LENGTH, "AFK", afk) - .addRowBold(Icons.SERVER, "Server", serverName) - .addBreak() - .addRowBold(Icons.PLAYER_KILLS, "Player Kills", playerKillCount) - .addRowBold(Icons.MOB_KILLS, "Mob Kills", mobKillCount) - .addRowBold(Icons.DEATHS, "Deaths", deaths) - .toHtml(); - - String rightSide = "
    " + - ""; - viewScript.append("worldPie(") - .append(worldHtmlID).append(", ") - .append(worldHtmlID).append("series, ") - .append(worldHtmlID).append("gmseries") - .append(");"); - - List kills = session.getValue(SessionKeys.PLAYER_KILLS).orElse(new ArrayList<>()); - String leftBottom = tables.killsTable(kills, null).parseHtml(); - - addElement(new AccordionElement(htmlID, title) - .setColor(theme.getValue(ThemeVal.PARSED_SESSION_ACCORDION)) - .setLeftSide(leftSide + leftBottom) - .setRightSide(rightSide)); - - i++; - } - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/CommandUseTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/CommandUseTable.java deleted file mode 100644 index 371bf4e09..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/CommandUseTable.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.keys.ServerKeys; -import com.djrapitops.plan.utilities.html.HtmlUtils; -import com.djrapitops.plan.utilities.html.icon.Icon; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * Html table that displays how many times each command is used. - * - * @author Rsl1122 - */ -class CommandUseTable extends TableContainer { - - CommandUseTable(DataContainer container) { - super(Icon.called("terminal") + " Command", Icon.called("list-ol") + "Times Used"); - - Map commandUse = container.getValue(ServerKeys.COMMAND_USAGE).orElse(new HashMap<>()); - - setColor("lime"); - if (commandUse.isEmpty()) { - addRow("No Commands"); - } else { - addValues(commandUse); - } - } - - private List> sortByValue(Map map) { - return map.entrySet().stream() - .sorted((one, two) -> Integer.compare(two.getValue(), one.getValue())) - .collect(Collectors.toList()); - } - - private void addValues(Map commandUse) { - List> sorted = sortByValue(commandUse); - - int i = 0; - for (Map.Entry entry : sorted) { - if (i >= 500) { - break; - } - String command = HtmlUtils.removeXSS(entry.getKey()); - addRow(command, entry.getValue()); - - i++; - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/DeathsTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/DeathsTable.java deleted file mode 100644 index f3d50c4b3..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/DeathsTable.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.container.PlayerDeath; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.icon.Family; -import com.djrapitops.plan.utilities.html.icon.Icon; - -import java.util.List; - -/** - * Html table that displays Deaths of a single player. - * - * @author Rsl1122 - */ -class DeathsTable extends TableContainer { - - private final Formatter yearFormatter; - - DeathsTable(List playerPlayerDeaths, Formatter yearFormatter) { - super(Icon.called("clock").of(Family.REGULAR) + " Time", "Killed by", "With"); - this.yearFormatter = yearFormatter; - setColor("red"); - - if (playerPlayerDeaths.isEmpty()) { - addRow("No Player caused Deaths"); - } else { - addValues(playerPlayerDeaths); - } - } - - private void addValues(List playerPlayerDeaths) { - playerPlayerDeaths.sort(new DateHolderRecentComparator()); - - int i = 0; - for (PlayerDeath death : playerPlayerDeaths) { - if (i >= 40) { - break; - } - - String killerName = death.getKillerName(); - addRow( - yearFormatter.apply(death), - Html.LINK.parse(PlanAPI.getInstance().getPlayerInspectPageLink(killerName), killerName), - death.getWeapon() - ); - - i++; - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/GeoInfoTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/GeoInfoTable.java deleted file mode 100644 index 21ca1884f..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/GeoInfoTable.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; -import com.djrapitops.plan.utilities.formatting.Formatter; - -import java.util.List; - -/** - * Utility Class for creating IP Table for inspect page. - * - * @author Rsl1122 - */ -class GeoInfoTable extends TableContainer { - - private final boolean displayIP; - private final Formatter yearFormatter; - - GeoInfoTable(List geoInfo, boolean displayIP, Formatter yearFormatter) { - super("IP", "Geolocation", "Last Used"); - this.displayIP = displayIP; - this.yearFormatter = yearFormatter; - - if (geoInfo.isEmpty()) { - addRow("No Connections"); - } else { - addValues(geoInfo); - } - } - - private void addValues(List geoInfo) { - geoInfo.sort(new DateHolderRecentComparator()); - - for (GeoInfo info : geoInfo) { - addRow( - displayIP ? info.getIp() : "Hidden (Config)", - info.getGeolocation(), - yearFormatter.apply(info) - ); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/HtmlTables.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/HtmlTables.java deleted file mode 100644 index d0b8acb41..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/HtmlTables.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.data.container.*; -import com.djrapitops.plan.data.element.AnalysisContainer; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.plugin.PluginData; -import com.djrapitops.plan.data.store.containers.DataContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.DisplaySettings; -import com.djrapitops.plan.system.settings.paths.TimeSettings; -import com.djrapitops.plan.utilities.formatting.Formatters; - -import javax.inject.Inject; -import javax.inject.Singleton; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * Factory class for objects that represent HTML tables. - * - * @author Rsl1122 - */ -@Singleton -public class HtmlTables { - - private final PlanConfig config; - private final Formatters formatters; - - @Inject - public HtmlTables( - PlanConfig config, - Formatters formatters - ) { - this.config = config; - this.formatters = formatters; - } - - /** - * Create a new Command usage table. - * - * @param container Container that supports ServerKeys.COMMAND_USAGE. - * @return a new {@link CommandUseTable}. - */ - public TableContainer commandUseTable(DataContainer container) { - return new CommandUseTable(container); - } - - /** - * Create a new Deaths table. - * - * @param deaths List of {@link PlayerDeath}s to be added to the table. - * @return a new {@link DeathsTable}. - */ - public TableContainer deathsTable(List deaths) { - return new DeathsTable(deaths, formatters.year()); - } - - /** - * Create a new GeoInfo table. - * - * @param geoInfo List of {@link GeoInfo} to be added to the table. - * @return a new {@link GeoInfoTable}. - */ - public TableContainer geoInfoTable(List geoInfo) { - return new GeoInfoTable(geoInfo, config.isTrue(DisplaySettings.PLAYER_IPS), formatters.year()); - } - - /** - * Create a new Kill table. - * - * @param kills List of {@link PlayerKill}s to be added to the table. - * @param color Color the table header should be. - * @return a new {@link KillsTable}. - */ - public TableContainer killsTable(List kills, String color) { - return new KillsTable(kills, color, formatters.year()); - } - - /** - * Create a new Nickname table. - * - * @param nicknames List of {@link Nickname}s to be added to the table. - * @param serverNames Names of the servers, for the server column. - * @return a new {@link NicknameTable}. - */ - public TableContainer nicknameTable(List nicknames, Map serverNames) { - return new NicknameTable(nicknames, serverNames, formatters.year()); - } - - /** - * Create a new Country - Ping table. - * - * @param pingPerCountry Map of {@link Ping}s sorted by country names. - * @return a new {@link PingTable}. - */ - public TableContainer pingTable(Map> pingPerCountry) { - return new PingTable(pingPerCountry, formatters.decimals()); - } - - /** - * Create a new Session table for a player. - * - * @param playerName Name of the player. - * @param sessions List of {@link Session}s the player has. - * @return a new {@link PlayerSessionTable}. - */ - public TableContainer playerSessionTable(String playerName, List sessions) { - return new PlayerSessionTable( - playerName, sessions, - config.get(DisplaySettings.SESSIONS_PER_PAGE), config.getWorldAliasSettings(), formatters.year(), formatters.timeAmount() - ); - } - - /** - * Create a new Session table for a server. - * - * @param playerNames Map of UUID - Name pairs of the players. - * @param sessions List of {@link Session}s that occurred on the server. - * @return a new {@link ServerSessionTable}. - */ - public TableContainer serverSessionTable(Map playerNames, List sessions) { - return new ServerSessionTable( - playerNames, sessions, - config.get(DisplaySettings.SESSIONS_PER_PAGE), config.getWorldAliasSettings(), formatters.year(), formatters.timeAmount() - ); - } - - /** - * Create a Player table for a server. - * - * @param players List of {@link PlayerContainer}s of players who have played on the server. - * @return a new {@link PlayersTable}. - */ - public TableContainer playerTableForServerPage(List players) { - return new PlayersTable( - players, - config.get(DisplaySettings.PLAYERS_PER_SERVER_PAGE), - config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), - config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD), - config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB), - formatters.timeAmount(), formatters.yearLong(), formatters.decimals() - ); - } - - /** - * Create a Player table for a players page. - * - * @param players List of {@link PlayerContainer}s of players. - * @return a new {@link PlayersTable}. - */ - public TableContainer playerTableForPlayersPage(List players) { - return new PlayersTable( - players, config.get(DisplaySettings.PLAYERS_PER_PLAYERS_PAGE), - config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD), - config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD), - config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB), - formatters.timeAmount(), formatters.yearLong(), formatters.decimals() - ); - } - - /** - * Create a new Player table that contains Plugin Data. - * - * @param containers PluginData AnalysisContainers. - * @param players List of {@link PlayerContainer}s of players. - * @return a new {@link PluginPlayersTable}. - */ - public TableContainer pluginPlayersTable(Map containers, Collection players) { - return new PluginPlayersTable( - containers, players, - config.get(DisplaySettings.PLAYERS_PER_SERVER_PAGE), - config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB) - ); - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/KillsTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/KillsTable.java deleted file mode 100644 index 41e3e3f33..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/KillsTable.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.container.PlayerKill; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.icon.Family; -import com.djrapitops.plan.utilities.html.icon.Icon; - -import java.util.List; - -/** - * Html table that displays kills Player has performed. - * - * @author Rsl1122 - */ -class KillsTable extends TableContainer { - - private final Formatter yearFormatter; - - KillsTable(List playerKills, String color, Formatter yearFormatter) { - super(Icon.called("clock").of(Family.REGULAR) + " Time", "Killed", "With"); - setColor(color); - - this.yearFormatter = yearFormatter; - - if (playerKills.isEmpty()) { - addRow("No Kills"); - } else { - addValues(playerKills); - } - } - - private void addValues(List playerKills) { - playerKills.sort(new DateHolderRecentComparator()); - - int i = 0; - for (PlayerKill kill : playerKills) { - if (i >= 40) { - break; - } - - String victimName = kill.getVictimName().orElse("Unknown"); - addRow( - yearFormatter.apply(kill), - Html.LINK.parse(PlanAPI.getInstance().getPlayerInspectPageLink(victimName), victimName), - kill.getWeapon() - ); - - i++; - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/NicknameTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/NicknameTable.java deleted file mode 100644 index 59ede0f0c..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/NicknameTable.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.data.store.objects.Nickname; -import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.HtmlUtils; - -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * Html table that displays player's nicknames and where they were seen. - * - * @author Rsl1122 - */ -class NicknameTable extends TableContainer { - - private final Formatter yearFormatter; - - NicknameTable(List nicknames, Map serverNames, Formatter yearFormatter) { - super("Nickname", "Server", "Last Seen"); - this.yearFormatter = yearFormatter; - - if (nicknames.isEmpty()) { - addRow("No Nicknames"); - } else { - addValues(nicknames, serverNames); - } - } - - private void addValues(List nicknames, Map serverNames) { - nicknames.sort(new DateHolderRecentComparator()); - - for (Nickname nickname : nicknames) { - UUID serverUUID = nickname.getServerUUID(); - String serverName = serverNames.getOrDefault(serverUUID, "Unknown"); - addRow( - Html.swapColorCodesToSpan(HtmlUtils.removeXSS(nickname.getName())), - serverName, - yearFormatter.apply(nickname) - ); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PingTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PingTable.java deleted file mode 100644 index 79ad8e0ac..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PingTable.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.data.container.Ping; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.mutators.PingMutator; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.icon.Icon; -import com.djrapitops.plan.utilities.html.icon.Icons; - -import java.util.*; - -/** - * Html table that displays countries and their average, worst and best pings. - * - * @author Rsl1122 - */ -class PingTable extends TableContainer { - - private final Formatter decimalFormatter; - - PingTable(Map> pingPerCountry, Formatter decimalFormatter) { - super( - Icon.called("globe") + " Country", - Icons.SIGNAL + " Average Ping", - Icons.SIGNAL + " Worst Ping", - Icons.SIGNAL + " Best Ping" - ); - this.decimalFormatter = decimalFormatter; - setColor("amber"); - - addRows(pingPerCountry); - } - - private void addRows(Map> pingPerCountry) { - Map avg = new HashMap<>(); - Map max = new HashMap<>(); - Map min = new HashMap<>(); - - for (Map.Entry> entry : pingPerCountry.entrySet()) { - PingMutator pingMutator = new PingMutator(entry.getValue()); - String country = entry.getKey(); - avg.put(country, pingMutator.average()); - max.put(country, pingMutator.max()); - min.put(country, pingMutator.min()); - } - - List sortedKeys = new ArrayList<>(avg.keySet()); - Collections.sort(sortedKeys); - - for (String country : sortedKeys) { - Double average = avg.get(country); - Integer maximum = max.get(country); - Integer minimum = min.get(country); - addRow( - country, - average >= 0 ? decimalFormatter.apply(average) + " ms" : "-", - maximum >= 0 ? maximum + " ms" : "-", - minimum >= 0 ? minimum + " ms" : "-" - ); - } - } -} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayerSessionTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayerSessionTable.java deleted file mode 100644 index a197ed755..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayerSessionTable.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.system.settings.config.WorldAliasSettings; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.Html; - -import java.util.List; - -/** - * Html table that can be used to replace a {@link com.djrapitops.plan.utilities.html.structure.SessionAccordion}. - * - * @author Rsl1122 - */ -class PlayerSessionTable extends TableContainer { - - private final int maxSessions; - private final WorldAliasSettings worldAliasSettings; - private final Formatter yearFormatter; - private final Formatter timeAmountFormatter; - - private final String playerName; - private final List sessions; - - PlayerSessionTable(String playerName, List sessions, - int maxSessions, - WorldAliasSettings worldAliasSettings, - Formatter yearFormatter, - Formatter timeAmountFormatter - ) { - super("Player", "Start", "Length", "World"); - this.playerName = playerName; - this.sessions = sessions; - this.maxSessions = maxSessions; - this.worldAliasSettings = worldAliasSettings; - this.yearFormatter = yearFormatter; - this.timeAmountFormatter = timeAmountFormatter; - - addRows(); - } - - private void addRows() { - String inspectUrl = PlanAPI.getInstance().getPlayerInspectPageLink(playerName); - - int i = 0; - for (Session session : sessions) { - if (i >= maxSessions) { - break; - } - - String start = yearFormatter.apply(session); - String length = session.supports(SessionKeys.END) - ? timeAmountFormatter.apply(session.getValue(SessionKeys.LENGTH).orElse(0L)) - : "Online"; - String world = worldAliasSettings.getLongestWorldPlayed(session); - - String toolTip = "Session ID: " + session.getValue(SessionKeys.DB_ID) - .map(Object::toString) - .orElse("Not Saved."); - addRow(Html.LINK_TOOLTIP.parse(inspectUrl, playerName, toolTip), start, length, world); - - i++; - } - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTable.java deleted file mode 100644 index 4e2708ec6..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTable.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.container.GeoInfo; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.mutators.ActivityIndex; -import com.djrapitops.plan.data.store.mutators.GeoInfoMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.utilities.comparators.PlayerContainerLastPlayedComparator; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plan.utilities.html.icon.Family; -import com.djrapitops.plan.utilities.html.icon.Icon; - -import java.util.List; - -/** - * Html table that displays a lot of information about players. - * - * @author Rsl1122 - */ -class PlayersTable extends TableContainer { - - private final List players; - private final int maxPlayers; - private final long activeMsThreshold; - private final int activeLoginThreshold; - private final boolean openPlayerPageInNewTab; - - private final Formatter decimalFormatter; - - PlayersTable( - List players, - int maxPlayers, - long activeMsThreshold, - int activeLoginThreshold, - boolean openPlayerPageInNewTab, - Formatter timeAmountFormatter, - Formatter yearLongFormatter, - Formatter decimalFormatter - ) { - super( - Icon.called("user") + " Name", - Icon.called("check") + " Activity Index", - Icon.called("clock").of(Family.REGULAR) + " Playtime", - Icon.called("calendar-plus").of(Family.REGULAR) + " Sessions", - Icon.called("user-plus") + " Registered", - Icon.called("calendar-check").of(Family.REGULAR) + " Last Seen", - Icon.called("globe") + " Geolocation" - ); - this.players = players; - this.maxPlayers = maxPlayers; - this.activeMsThreshold = activeMsThreshold; - this.activeLoginThreshold = activeLoginThreshold; - this.openPlayerPageInNewTab = openPlayerPageInNewTab; - this.decimalFormatter = decimalFormatter; - useJqueryDataTables("player-table"); - - setFormatter(2, timeAmountFormatter); - setFormatter(4, yearLongFormatter); - setFormatter(5, yearLongFormatter); - addRows(); - } - - private void addRows() { - PlanAPI planAPI = PlanAPI.getInstance(); - long now = System.currentTimeMillis(); - - players.sort(new PlayerContainerLastPlayedComparator()); - - int i = 0; - for (PlayerContainer player : players) { - if (i >= maxPlayers) { - break; - } - String name = player.getValue(PlayerKeys.NAME).orElse("Unknown"); - String url = planAPI.getPlayerInspectPageLink(name); - - SessionsMutator sessionsMutator = SessionsMutator.forContainer(player); - int loginTimes = sessionsMutator.count(); - long playtime = sessionsMutator.toPlaytime(); - long registered = player.getValue(PlayerKeys.REGISTERED).orElse(0L); - long lastSeen = sessionsMutator.toLastSeen(); - - ActivityIndex activityIndex = player.getActivityIndex(now, activeMsThreshold, activeLoginThreshold); - boolean isBanned = player.getValue(PlayerKeys.BANNED).orElse(false); - String activityString = activityIndex.getFormattedValue(decimalFormatter) - + (isBanned ? " (Banned)" : " (" + activityIndex.getGroup() + ")"); - - String geolocation = GeoInfoMutator.forContainer(player).mostRecent().map(GeoInfo::getGeolocation).orElse("-"); - - Html link = openPlayerPageInNewTab ? Html.LINK_EXTERNAL : Html.LINK; - - addRow( - link.parse(url, name), - activityString, - playtime, - loginTimes, - registered, - lastSeen, - geolocation - ); - - i++; - } - - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PluginPlayersTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PluginPlayersTable.java deleted file mode 100644 index c68499443..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PluginPlayersTable.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.element.AnalysisContainer; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.plugin.PluginData; -import com.djrapitops.plan.data.store.containers.PlayerContainer; -import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.utilities.html.Html; -import com.djrapitops.plugin.utilities.ArrayUtil; - -import java.io.Serializable; -import java.util.*; - -/** - * Html table that displays players data in various plugins. - * - * @author Rsl1122 - */ -class PluginPlayersTable extends TableContainer { - - private Collection players; - - private final int maxPlayers; - private final boolean openPlayerPageInNewTab; - - PluginPlayersTable( - Map containers, - Collection players, - int maxPlayers, - boolean openPlayerPageInNewTab - ) { - this(getPluginDataSet(containers), players, maxPlayers, openPlayerPageInNewTab); - } - - private PluginPlayersTable( - TreeMap> pluginDataSet, - Collection players, - int maxPlayers, - boolean openPlayerPageInNewTab - ) { - super(true, getHeaders(pluginDataSet.keySet())); - - this.players = players; - this.maxPlayers = maxPlayers; - this.openPlayerPageInNewTab = openPlayerPageInNewTab; - - useJqueryDataTables("player-plugin-table"); - - if (players.isEmpty()) { - addRow("No Players"); - } else { - Map rows = getRows(pluginDataSet); - addValues(rows); - } - } - - private static String[] getHeaders(Set columnNames) { - List header = new ArrayList<>(columnNames); - Collections.sort(header); - return header.toArray(new String[0]); - } - - private static TreeMap> getPluginDataSet(Map containers) { - TreeMap> data = new TreeMap<>(); - for (AnalysisContainer container : containers.values()) { - if (!container.hasPlayerTableValues()) { - continue; - } - data.putAll(container.getPlayerTableValues()); - } - return data; - } - - private void addValues(Map rows) { - int i = 0; - for (PlayerContainer profile : players) { - if (i >= maxPlayers) { - break; - } - - UUID uuid = profile.getUnsafe(PlayerKeys.UUID); - String name = profile.getValue(PlayerKeys.NAME).orElse("Unknown"); - Html link = openPlayerPageInNewTab ? Html.LINK_EXTERNAL : Html.LINK; - String linkHtml = link.parse(PlanAPI.getInstance().getPlayerInspectPageLink(name), name); - - Serializable[] playerData = ArrayUtil.merge(new Serializable[]{linkHtml}, rows.getOrDefault(uuid, new Serializable[]{})); - addRow(playerData); - - i++; - } - } - - private Map getRows(TreeMap> data) { - Map rows = new HashMap<>(); - - int size = header.length - 1; - for (PlayerContainer profile : players) { - UUID uuid = profile.getUnsafe(PlayerKeys.UUID); - - Serializable[] row = new Serializable[size]; - for (int i = 0; i < size; i++) { - String label = header[i + 1]; - - Map playerSpecificData = data.getOrDefault(label, new HashMap<>()); - Serializable value = playerSpecificData.get(uuid); - if (value != null) { - row[i] = value; - } - } - rows.put(uuid, row); - } - return rows; - } - -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/ServerSessionTable.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/ServerSessionTable.java deleted file mode 100644 index a5b0f25cb..000000000 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/ServerSessionTable.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 . - */ -package com.djrapitops.plan.utilities.html.tables; - -import com.djrapitops.plan.api.PlanAPI; -import com.djrapitops.plan.data.container.Session; -import com.djrapitops.plan.data.element.TableContainer; -import com.djrapitops.plan.data.store.keys.SessionKeys; -import com.djrapitops.plan.data.store.objects.DateHolder; -import com.djrapitops.plan.system.settings.config.WorldAliasSettings; -import com.djrapitops.plan.utilities.formatting.Formatter; -import com.djrapitops.plan.utilities.html.Html; - -import java.util.List; -import java.util.Map; -import java.util.UUID; - -/** - * Html table that can be used to replace a {@link com.djrapitops.plan.utilities.html.structure.SessionAccordion}. - * - * @author Rsl1122 - */ -class ServerSessionTable extends TableContainer { - - private final int maxSessions; - private final WorldAliasSettings worldAliasSettings; - private final Formatter yearFormatter; - private final Formatter timeAmountFormatter; - - private final List sessions; - private Map playerNames; - - ServerSessionTable( - Map playerNames, List sessions, - int maxSessions, - WorldAliasSettings worldAliasSettings, - Formatter yearFormatter, - Formatter timeAmountFormatter - ) { - super("Player", "Start", "Length", "World"); - this.playerNames = playerNames; - this.sessions = sessions; - this.maxSessions = maxSessions; - this.worldAliasSettings = worldAliasSettings; - this.yearFormatter = yearFormatter; - this.timeAmountFormatter = timeAmountFormatter; - - addRows(); - } - - private void addRows() { - int i = 0; - for (Session session : sessions) { - if (i >= maxSessions) { - break; - } - - String start = yearFormatter.apply(session); - String length = session.supports(SessionKeys.END) - ? timeAmountFormatter.apply(session.getValue(SessionKeys.LENGTH).orElse(0L)) - : "Online"; - String world = worldAliasSettings.getLongestWorldPlayed(session); - - String toolTip = "Session ID: " + session.getValue(SessionKeys.DB_ID) - .map(Object::toString) - .orElse("Not Saved."); - - String playerName = playerNames.getOrDefault(session.getValue(SessionKeys.UUID).orElse(null), "Unknown"); - String inspectUrl = PlanAPI.getInstance().getPlayerInspectPageLink(playerName); - - addRow(Html.LINK_TOOLTIP.parse(inspectUrl, playerName, toolTip), start, length, world); - - i++; - } - } -} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowingConsumer.java similarity index 71% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheRequest.java rename to Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowingConsumer.java index 36008c654..4d515c3d5 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/CacheRequest.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowingConsumer.java @@ -14,12 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.request; +package com.djrapitops.plan.utilities.java; /** - * Interface for all InfoRequests that cache something into RequestCache. + * Functional interface that performs an operation that might throw an exception. + *

    + * Follows naming scheme of Java 8 functional interfaces. * * @author Rsl1122 */ -public interface CacheRequest extends InfoRequest { -} +public interface ThrowingConsumer { + + void accept(T consume) throws K; + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SetupRequest.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowingSupplier.java similarity index 73% rename from Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SetupRequest.java rename to Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowingSupplier.java index 90d7ee759..7e85de597 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/info/request/SetupRequest.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThrowingSupplier.java @@ -14,13 +14,17 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.info.request; +package com.djrapitops.plan.utilities.java; /** - * InfoRequest that is related to initial communications. + * Functional interface that performs an operation that might throw an exception. + *

    + * Follows naming scheme of Java 8 functional interfaces. * * @author Rsl1122 */ -public interface SetupRequest extends InfoRequest { +public interface ThrowingSupplier { -} + T get() throws K; + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionCheckSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/version/VersionCheckSystem.java similarity index 55% rename from Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionCheckSystem.java rename to Plan/common/src/main/java/com/djrapitops/plan/version/VersionCheckSystem.java index db5378398..da603820f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionCheckSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/version/VersionCheckSystem.java @@ -14,13 +14,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.update; +package com.djrapitops.plan.version; -import com.djrapitops.plan.system.SubSystem; -import com.djrapitops.plan.system.locale.Locale; -import com.djrapitops.plan.system.locale.lang.PluginLang; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.PluginSettings; +import com.djrapitops.plan.SubSystem; +import com.djrapitops.plan.settings.config.PlanConfig; +import com.djrapitops.plan.settings.config.paths.PluginSettings; +import com.djrapitops.plan.settings.locale.Locale; +import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plugin.api.utility.Version; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.console.PluginLogger; @@ -103,10 +103,43 @@ public class VersionCheckSystem implements SubSystem { return Optional.ofNullable(newVersionAvailable); } - public Optional getUpdateHtml() { + public Optional getUpdateButton() { return getNewVersionAvailable() - .map(v -> v.isTrusted() ? "" + - "

    v" + v.getVersion().getVersionString() + " available!

    " : ""); + .map(v -> "" + ); + } + + public String getCurrentVersionButton() { + return ""; + } + + public String getUpdateModal() { + return getNewVersionAvailable() + .map(v -> "
    " + + "
    " + + " Version " + v.getVersion().getVersionString() + " is Available!" + + "
    " + + "
    " + // Close modal-header + "
    " + + "

    A new version has been released and is now available for download." + + (v.isRelease() ? "" : "
    This version is a DEV release.") + "

    " + + "" + + " View Changelog" + + "" + + " Download Plan-" + v.getVersion().getVersionString() + ".jar" + + "
    ") // Close modal-body + .orElse("
    " + + "
    " + + " You have version " + getCurrentVersion() + "" + + "
    " + + "
    " + // Close modal-header + "
    " + + "

    You are running the latest version.

    " + + "
    "); // Close modal-body } public String getCurrentVersion() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionInfo.java b/Plan/common/src/main/java/com/djrapitops/plan/version/VersionInfo.java similarity index 85% rename from Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionInfo.java rename to Plan/common/src/main/java/com/djrapitops/plan/version/VersionInfo.java index 018639d6c..940b0c77b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionInfo.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/version/VersionInfo.java @@ -14,10 +14,11 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.update; +package com.djrapitops.plan.version; import com.djrapitops.plugin.api.utility.Version; -import com.google.common.base.Objects; + +import java.util.Objects; /** * Data class for reading version.txt in https://github.com/Rsl1122/Plan-PlayerAnalytics. @@ -54,22 +55,18 @@ public class VersionInfo implements Comparable { return changeLogUrl; } - public boolean isTrusted() { - return downloadUrl.startsWith("https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/"); - } - @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; VersionInfo that = (VersionInfo) o; return release == that.release && - Objects.equal(version, that.version); + Objects.equals(version, that.version); } @Override public int hashCode() { - return Objects.hashCode(release, version); + return Objects.hash(release, version); } @Override diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionInfoLoader.java b/Plan/common/src/main/java/com/djrapitops/plan/version/VersionInfoLoader.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionInfoLoader.java rename to Plan/common/src/main/java/com/djrapitops/plan/version/VersionInfoLoader.java index 335e5ed4f..e3e093056 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/update/VersionInfoLoader.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/version/VersionInfoLoader.java @@ -14,9 +14,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.system.update; +package com.djrapitops.plan.version; import com.djrapitops.plugin.api.utility.Version; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.net.URL; @@ -70,7 +71,7 @@ public class VersionInfoLoader { if (!line.startsWith("REL") && !line.startsWith("DEV")) { return Optional.empty(); } - String[] parts = line.split("\\|"); + String[] parts = StringUtils.split(line, '|'); if (parts.length < 4) { return Optional.empty(); } diff --git a/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml b/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml index f63dc3d63..7740523d2 100644 --- a/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml +++ b/Plan/common/src/main/resources/assets/plan/bungeeconfig.yml @@ -81,7 +81,6 @@ Time: # The index is a number from 0 to 5. # These numbers were calibrated with data of 250 players (Small sample size). Activity_index: - Login_threshold: 2 Playtime_threshold: 30 Unit: MINUTES Remove_inactive_player_data_after: 180 @@ -91,13 +90,13 @@ Time: Unit: DAYS Remove_ping_data_after: 14 Unit: DAYS + Remove_disabled_extension_data_after: 2 + Unit: DAYS Periodic_tasks: Extension_data_refresh_every: 1 Unit: HOURS Check_DB_for_server_config_files_every: 1 Unit: MINUTES - Clean_caches_every: 10 - Unit: MINUTES Clean_Database_every: 1 Unit: HOURS # ----------------------------------------------------- @@ -106,8 +105,6 @@ Display_options: # https://github.com/Rsl1122/Plan-PlayerAnalytics/wiki/Themes Theme: default Sessions: - Replace_accordion_with_table: false - Show_most_played_world_in_title: true Show_on_page: 50 # By Default World playtime pie is ordered alphabetically. # Colors will be determined alphabetically in any case. @@ -116,9 +113,14 @@ Display_options: Show_on_server_page: 2500 Show_on_players_page: 25000 Open_player_links_in_new_tab: false - Show_player_IPs: true Graphs: Show_gaps_in_data: false + TPS: + High_threshold: 18 + Medium_threshold: 10 + Disk_space: + High_threshold: 500 + Medium_threshold: 100 Command_colors: Main: '&2' Secondary: '&7' @@ -157,12 +159,12 @@ World_aliases: # These settings will make Plan write .js, .css, .json and .html files to some location on disk. # Relative path will render to /plugins/Plan/path # Make sure user running the server has write permissions to the path. +# On networks export is disabled on Bukkit/Sponge servers. # ----------------------------------------------------- Export: HTML_Export_path: 'Analysis Results' JSON_Export_path: 'Raw JSON' Parts: - JavaScript_and_CSS: false # Player pages/JSON are only written on join/leave. Player_pages: false Player_JSON: false @@ -170,11 +172,15 @@ Export: Server_page: false Server_JSON: false Export_player_on_login_and_logout: false + # If there are multiple servers the period is divided evenly to avoid export of all servers at once + # Also affects Players page export + Server_refresh_period: 20 + Unit: MINUTES # ----------------------------------------------------- # These settings affect Plugin data integration. # If a plugin is causing issues the integration can be disabled by setting Plugin_name.Enabled: false # ----------------------------------------------------- Plugins: - BuyCraft: + Buycraft: # http://help.buycraft.net/article/36-where-to-find-the-secret-key Secret: "-" \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/config.yml b/Plan/common/src/main/resources/assets/plan/config.yml index 32d49a735..a490a965f 100644 --- a/Plan/common/src/main/resources/assets/plan/config.yml +++ b/Plan/common/src/main/resources/assets/plan/config.yml @@ -20,7 +20,7 @@ Plugin: Check_for_updates: true Notify_about_DEV_releases: false Configuration: - Allow_bungeecord_to_manage_settings: true + Allow_proxy_to_manage_settings: true # ----------------------------------------------------- # Supported databases: SQLite, H2, MySQL # ----------------------------------------------------- @@ -86,7 +86,6 @@ Time: # The index is a number from 0 to 5. # These numbers were calibrated with data of 250 players (Small sample size). Activity_index: - Login_threshold: 2 Playtime_threshold: 30 Unit: MINUTES Remove_inactive_player_data_after: 180 @@ -96,15 +95,13 @@ Time: Unit: DAYS Remove_ping_data_after: 14 Unit: DAYS + Remove_disabled_extension_data_after: 2 + Unit: DAYS Periodic_tasks: - Analysis_refresh_every: 60 - Unit: MINUTES Extension_data_refresh_every: 1 Unit: HOURS Check_DB_for_server_config_files_every: 1 Unit: MINUTES - Clean_caches_every: 10 - Unit: MINUTES Clean_Database_every: 1 Unit: HOURS # ----------------------------------------------------- @@ -113,8 +110,6 @@ Display_options: # https://github.com/Rsl1122/Plan-PlayerAnalytics/wiki/Themes Theme: default Sessions: - Replace_accordion_with_table: false - Show_most_played_world_in_title: true Show_on_page: 50 # By Default World playtime pie is ordered alphabetically. # Colors will be determined alphabetically in any case. @@ -123,7 +118,6 @@ Display_options: Show_on_server_page: 2500 Show_on_players_page: 25000 Open_player_links_in_new_tab: false - Show_player_IPs: true Graphs: Show_gaps_in_data: false TPS: @@ -170,12 +164,12 @@ World_aliases: # These settings will make Plan write .js, .css, .json and .html files to some location on disk. # Relative path will render to /plugins/Plan/path # Make sure user running the server has write permissions to the path. +# On networks export is disabled on Bukkit/Sponge servers. # ----------------------------------------------------- Export: HTML_Export_path: 'Analysis Results' JSON_Export_path: 'Raw JSON' Parts: - JavaScript_and_CSS: false # Player pages/JSON are only written on join/leave. Player_pages: false Player_JSON: false @@ -184,12 +178,16 @@ Export: Server_JSON: false # All player pages/JSON can be exported by using /plan m export players Export_player_on_login_and_logout: false + # If there are multiple servers the period is divided evenly to avoid export of all servers at once + # Also affects Players page export + Server_refresh_period: 20 + Unit: MINUTES # ----------------------------------------------------- # These settings affect Plugin data integration. # If a plugin is causing issues the integration can be disabled by setting Plugin_name.Enabled: false # ----------------------------------------------------- Plugins: - BuyCraft: + Buycraft: # http://help.buycraft.net/article/36-where-to-find-the-secret-key Secret: '-' Factions: diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_CN.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_CN.txt index b8e38af5a..0fe381b14 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_CN.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_CN.txt @@ -1,12 +1,13 @@ Cmd - Click Me || 请点击此处 Cmd - Link || §7 •§2 链接:§f Cmd Disable - Disabled || §a现已禁用计划系统。您仍可使用 /planbungee reload 重启插件。 -Cmd FAIL - Invalid Username || §c[计划] 此玩家不存在。 +Cmd FAIL - Database not open || §c数据库状态为 ${0} - 请稍后再试. +Cmd FAIL - Invalid Username || §c[数据统计] 此玩家不存在。 Cmd FAIL - No Feature || §e请设置要禁用的功能!(当前支持 ${0}) -Cmd FAIL - No Permission || §c[计划] 您没有所需权限。 -Cmd FAIL - Require only one Argument || §c[计划] 命令需要一个参数。 -Cmd FAIL - Requires Arguments || §c[计划] 命令缺少参数。${0} -Cmd FAIL - Unknown Username || §c[计划] 未在数据库中找到此玩家。 +Cmd FAIL - No Permission || §c[数据统计] 您没有所需权限。 +Cmd FAIL - Require only one Argument || §c[数据统计] 命令需要一个参数。 +Cmd FAIL - Requires Arguments || §c[数据统计] 命令缺少参数。${0} +Cmd FAIL - Unknown Username || §c[数据统计] 未在数据库中找到此玩家。 Cmd FAIL - WebUser does not exists || §c用户不存在! Cmd FAIL - WebUser exists || §c用户已存在! Cmd Header - Analysis || §f»§2 玩家统计 - 统计结果 @@ -17,9 +18,9 @@ Cmd Header - Players || > §2玩家 Cmd Header - Search || §f»§2 玩家统计 - 搜索结果: Cmd Header - Servers || > §2服务器 Cmd Header - Web Users || > §2${0} 个网页用户 -Cmd Info - Bungee Connection || §2已连接至 Bungee:§f${0} +Cmd Info - Bungee Connection || §2已连接至Bungee:§f${0} Cmd Info - Database || §2可用数据库:§f${0} -Cmd Info - Reload Complete || §a重新载入完毕 +Cmd Info - Reload Complete || §a成功重载插件 Cmd Info - Reload Failed || §c重载插件时发生错误,建议重新启动服务器。 Cmd Info - Update || §2更新可用:§f${0} Cmd Info - Version || §2版本:§f${0} @@ -35,29 +36,9 @@ Cmd Qinspect - Player Kills || §2玩家击杀数:§f${0 Cmd Qinspect - Playtime || §2游玩时间:§f${0} Cmd Qinspect - Registered || §2注册时间:§f${0} Cmd Qinspect - Times Kicked || §2踢出次数:§f${0} -Cmd Setup - Allowed || §a现已允许安装 -Cmd Setup - Bad Request || §e连接成功,但接收服务器不是 Bungee 服务器。请使用 Bungee 地址。 -Cmd Setup - Disallowed || §c现已禁止安装 -Cmd Setup - Forbidden || §e连接成功,但 Bungee 已禁用安装模式 - 请使用 '/planbungee setup' 启用。 -Cmd Setup - Gateway Error || §e连接成功,但 Bungee 无法与此服务器建立连接(是否重启了当前网页服务器?)请使用 /plan m con 与 /planbungee con 进行调试。 -Cmd Setup - Generic Fail || §e连接失败:${0} -Cmd Setup - Internal Error || §e连接成功。${0},请检查接收服务器调试页面的错误日志。 -Cmd Setup - Success || §a连接成功,计划可能将在几秒内重启··· -Cmd Setup - Unauthorized || §e连接成功,但接收服务器并未授权此服务器。请在 Discord 上联系以寻求帮助 -Cmd Setup - Url mistake || §c请确保您所输入的是完整地址(以 http:// 或 https:// 开头)- 请检查 Bungee 启用日志获取完整地址。 -Cmd Setup - WebServer not Enabled || §c未在此服务器上启用网页服务器!请确保其在开机时启用! Cmd SUCCESS - Feature disabled || §a已在下次插件重载前暂时禁用 '${0}'。 Cmd SUCCESS - WebUser register || §a已成功添加新用户(${0})!您可以在以下链接中查看Web面板。 -Cmd Update - Cancel Success || §a已完成取消操作。 -Cmd Update - Cancelled || §c已取消更新。 -Cmd Update - Change log || 更新日志 版本${0}: -Cmd Update - Fail Cacnel || §c某个服务器更新失败,正在取消所有服务器的更新··· -Cmd Update - Fail Force Notify || §e${0} 无法更新,但使用了 -force,正在继续更新。 -Cmd Update - Fail Not Online || §c并非所有服务器均在线或可访问,您仍可通过使用 /plan update -u -force 的方式强制更新。 -Cmd Update - Notify Cancel || §a您可以使用 /plan update cancel 以取消尚未重启的服务器上的更新。 -Cmd Update - Online Check || 正在检查是否所有服务器均在线··· -Cmd Update - Scheduled || §a已为 ${0} 计划了更新。 -Cmd Update - Url mismatch || §c版本下载网址不以 ${0} 开头故可能不受信任。您可手动下载此版本(直接下载): +Cmd WARN - Database not open || §e数据库状态为 ${0} - 可能需要花费更多的时间.. Cmd Web - Permission Levels || >\§70:访问所有页面\§71:访问 '/players' 及所有玩家页\§72:访问用户名与网页用户名一致的玩家页\§73+:无权限 Command Help - /plan analyze || 查看服务器分析 Command Help - /plan dev || 开发模式命令 @@ -67,14 +48,15 @@ Command Help - /plan inspect || 检视玩家数据 Command Help - /plan manage || 数据库管理命令 Command Help - /plan manage backup || 备份数据库至 .db 文件 Command Help - /plan manage clear || 从数据库中清空所有数据 -Command Help - /plan manage con || 调试服务器至 Bungee 的连接 Command Help - /plan manage disable || 暂时禁用功能 +Command Help - /plan manage export || 手动导出 Command Help - /plan manage hotswap || 热插拔数据库并重启插件 Command Help - /plan manage import || 从插件中导入数据 Command Help - /plan manage move || 在数据库间移动数据 +Command Help - /plan manage raw || 查看玩家数据的raw JSON Command Help - /plan manage remove || 从活跃数据库中移除玩家数据 Command Help - /plan manage restore || 恢复数据库 -Command Help - /plan manage setup || 设置服务器至 Bungee 的连接 +Command Help - /plan manage uninstalled || 在数据库中将一个服务器标记为未安装. Command Help - /plan network || 查看网络页面 Command Help - /plan players || 列出所有已缓存玩家名单 Command Help - /plan qinspect || 在游戏内检视玩家数据 @@ -82,15 +64,12 @@ Command Help - /plan register || 注册网页用户 Command Help - /plan reload || 重新启动插件(重载配置) Command Help - /plan search || 搜索玩家 Command Help - /plan servers || 列出数据库中的服务器 -Command Help - /plan update || 获取更新日志链接或更新插件 Command Help - /plan web check || 检查网页用户的权限级别。 Command Help - /plan web delete || 删除网页用户 Command Help - /plan web level || 权限级别信息 Command Help - /plan web list || 列出网页用户 Command Help - /plan webuser || 管理网页用户 -Command Help - /planbungee con || 调试 Bungee 至服务器的连接 Command Help - /planbungee disable || 暂时禁用插件 -Command Help - /planbungee setup || 开关设置功能 Database - Apply Patch || 正在应用更新:${0}··· Database - Patches Applied || 已成功应用所有数据库补丁。 Database - Patches Applied Already || 已应用所有数据库补丁。 @@ -100,10 +79,10 @@ Database Notify - SQLite No WAL || 此服务器版本不支持 S Disable || 已禁用插件。 Disable - Processing || 正在处理未处理的关键任务。(${0}) Disable - Processing Complete || 处理完毕。 +Disable - Unsaved Session Save || 保存未完成的时域中.. Disable - WebServer || 正在关闭网页服务器··· Enable || 已启用插件。 Enable - Database || ${0}-已建立数据库连接。 -Enable - Notify Address Confirmation || 确保此地址指向此服务器:${0} Enable - Notify Empty IP || server.properties 中的 IP 为空且未使用替代 IP。这将会导致地址出错! Enable - Notify Geolocations disabled || 已关闭地理位置收集。(Data.Geolocations: false) Enable - Notify Geolocations Internet Required || 插件需要在首次运行时访问互联网以下载 GeoLite2 地理位置数据库。 @@ -112,139 +91,205 @@ Enable - WebServer || 正在初始化网页服务 Enable FAIL - Database || ${0}-连接数据库失败:${1} Enable FAIL - Database Patch || 数据库补丁失败,插件必须被禁用。请汇报此问题 Enable FAIL - GeoDB Write || 保存已下载的 GeoLite2 地理位置数据库时发生问题 -Enable FAIL - WebServer (Bungee) || 网页服务器未初始化 +Enable FAIL - WebServer (Proxy) || 网页服务器未初始化 Enable FAIL - Wrong Database Type || ${0} 此数据类型不存在。 -HTML - ACTIVITY_INDEX || 活跃指数 -HTML - ALL || 全部 -HTML - ALL_TIME_PEAK || 所有时间峰值 -HTML - AVERAGE_PING || 平均延迟 -HTML - AVG || 平均 -HTML - BANNED || 已封禁 -HTML - BEST_PING || 最低延迟 -HTML - CALENDAR || 日历 -HTML - CALENDAR_TEXT || 日历 -HTML - CHUNKS || 区块 -HTML - COMMAND || 命令 -HTML - COMMNAND_USAGE || 使用命令 -HTML - CONNECTION_INFORMATION || 连接信息 -HTML - COUNTRY || 国家 -HTML - CURRENT_PLAYERBASE || 当前玩家 -HTML - DEATHS || 死亡数 -HTML - ENTITIES || 实体数 +HTML - COMPARING_15_DAYS || 比较15天前 +HTML - COMPARING_60_DAYS || 比较30天前 +HTML - COMPARING_7_DAYS || 比较7天前 +HTML - DATABASE_NOT_OPEN || 数据库未开放, 请输入 /plan info 查看数据库状态 HTML - ERROR || 认证时发生错误 -HTML - FAVORITE_SERVER || 喜爱的服务器 -HTML - GEOLOCATION || 地理位置 -HTML - GEOLOCATION_TEXT || 地理位置 -HTML - HEALTH_ESTIMATE || 预计健康度 HTML - INDEX_ACTIVE || 活跃 HTML - INDEX_INACTIVE || 不活跃 HTML - INDEX_IRREGULAR || 偶尔上线 HTML - INDEX_REGULAR || 经常上线 HTML - INDEX_VERY_ACTIVE || 非常活跃 -HTML - IP_ADDRESS || IP 地址 HTML - KILLED || 被击杀 -HTML - KILLED_BY || 击杀者 -HTML - LAST_24_HOURS || 过去 24 小时 -HTML - LAST_30_DAYS || 过去 30 天 -HTML - LAST_30_DAYS_TEXT || 过去 30 天 -HTML - LAST_7_DAYS || 过去 7 天 -HTML - LAST_CONNECTED || 最后连接 -HTML - LAST_PEAK || 上次峰值 -HTML - LAST_SEEN || 最后在线 -HTML - LAST_SEEN_TEXT || 最后在线 -HTML - LOADED_CHUNKS || 已加载的区块 -HTML - LOADED_ENTITIES || 已加载的实体 +HTML - LABEL_1ST_WEAPON || 最致命的PVP武器 +HTML - LABEL_2ND_WEAPON || 更致命的武器 +HTML - LABEL_3RD_WEAPON || 致命的PvP武器 +HTML - LABEL_ACTIVITY_INDEX || 活跃指数 +HTML - LABEL_AFK || AFK +HTML - LABEL_AFK_TIME || 挂机时间 +HTML - LABEL_AVG || 平均 +HTML - LABEL_AVG_KDR || 平均击杀比 +HTML - LABEL_AVG_MOB_KDR || 平均怪物击杀比 +HTML - LABEL_AVG_PLAYTIME || 平均游戏时间 +HTML - LABEL_AVG_SESSION_LENGTH || 平均时域长度 +HTML - LABEL_AVG_TPS || 平均TPS +HTML - LABEL_BANNED || 已封禁 +HTML - LABEL_BEST_PEAK || 所有时间峰值 +HTML - LABEL_DAY_OF_WEEK || Day of the Week +HTML - LABEL_DEATHS || 死亡数 +HTML - LABEL_DOWNTIME || 停机时间 +HTML - LABEL_DURING_LOW_TPS || TPS Spikes: +HTML - LABEL_ENTITIES || 实体数 +HTML - LABEL_FAVORITE_SERVER || 喜爱的服务器 +HTML - LABEL_FIRST_SESSION_LENGTH || 第一个时域的长度 +HTML - LABEL_FREE_DISK_SPACE || 剩余磁盘空间 +HTML - LABEL_INACTIVE || 未激活 +HTML - LABEL_LAST_PEAK || 上次峰值 +HTML - LABEL_LAST_SEEN || 最后在线 +HTML - LABEL_LOADED_CHUNKS || 已加载的区块 +HTML - LABEL_LOADED_ENTITIES || 已加载的实体 +HTML - LABEL_LONE_JOINS || Lone joins +HTML - LABEL_LONE_NEW_JOINS || Lone newbie joins +HTML - LABEL_LONGEST_SESSION || 最长时域 +HTML - LABEL_LOW_TPS || 最低TPS Spikes +HTML - LABEL_MAX_FREE_DISK || Max Free Disk +HTML - LABEL_MIN_FREE_DISK || Min Free Disk +HTML - LABEL_MOB_DEATHS || 被怪物击杀数 +HTML - LABEL_MOB_KDR || 怪物击杀比 +HTML - LABEL_MOB_KILLS || 怪物击杀数 +HTML - LABEL_MOST_ACTIVE_GAMEMODE || 最常用的游戏模式 +HTML - LABEL_NAME || 名称 +HTML - LABEL_NEW || 新 +HTML - LABEL_NEW_PLAYERS || 新玩家 +HTML - LABEL_NICKNAME || 昵称 +HTML - LABEL_NO_SESSION_KILLS || None +HTML - LABEL_ONLINE_FIRST_JOIN || 第一次加入服务器的在线玩家 +HTML - LABEL_OPERATOR || 管理员 +HTML - LABEL_PER_PLAYER || / 玩家 +HTML - LABEL_PER_REGULAR_PLAYER || / 普通玩家 +HTML - LABEL_PLAYER_DEATHS || 被玩家击杀数 +HTML - LABEL_PLAYER_KILLS || 玩家击杀数 +HTML - LABEL_PLAYERS_ONLINE || 在线玩家 +HTML - LABEL_PLAYTIME || 游玩时间 +HTML - LABEL_REGISTERED || 已注册 +HTML - LABEL_REGISTERED_PLAYERS || 已注册的玩家 +HTML - LABEL_REGULAR || Regular +HTML - LABEL_REGULAR_PLAYERS || 普通玩家 +HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity +HTML - LABEL_RETENTION || 新玩家留坑率 +HTML - LABEL_SERVER_DOWNTIME || 关服时间 +HTML - LABEL_SERVER_OCCUPIED || 开服时间 +HTML - LABEL_SESSION_ENDED || 时域之末 +HTML - LABEL_SESSION_MEDIAN || 平均时域 +HTML - LABEL_TIMES_KICKED || 被踢次数 +HTML - LABEL_TOTAL_PLAYERS || 总玩家数 +HTML - LABEL_TOTAL_PLAYTIME || 总游戏时间 +HTML - LABEL_UNIQUE_PLAYERS || 普通玩家 +HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' +HTML - LINK_BACK_NETWORK || 网络页面 +HTML - LINK_BACK_SERVER || 服务器页面 +HTML - LINK_CHANGELOG || 查看更新日志 +HTML - LINK_DISCORD || Discord支持 +HTML - LINK_DOWNLOAD || 下载 +HTML - LINK_ISSUES || 提问 +HTML - LINK_NIGHT_MODE || 夜晚模式 +HTML - LINK_PLAYER_PAGE || Player Page +HTML - LINK_QUICK_VIEW || 快速检索 +HTML - LINK_SERVER_ANALYSIS || 服务器分析 +HTML - LINK_WIKI || Plan Wiki,教程 & 文档 HTML - LOCAL_MACHINE || 本机 -HTML - LONGEST || 最长 -HTML - LOW_TPS_SPIKES || 最低 TPS 值 -HTML - MOB_CAUSED_DEATHS || 被怪物击杀数 -HTML - MOB_KDR || 怪物击杀比 -HTML - MOB_KILLS || 怪物击杀数 -HTML - MOST_RECENT_SESSIONS || 最近时域 -HTML - NAME || 名称 -HTML - NAV_COMMAND_USAGE || 使用指令 -HTML - NAV_GEOLOCATIONS || 地理位置 -HTML - NAV_INFORMATION || 信息 -HTML - NAV_NETWORK_PLAYERS || 网络玩家 -HTML - NAV_ONLINE_ACTIVITY || 在线玩家 -HTML - NAV_OVERVIEW || 预览 -HTML - NAV_PERFORMANCE || 性能 -HTML - NAV_PLAYERS || 玩家 HTML - NAV_PLUGINS || 插件 -HTML - NAV_SESSIONS || 时域 -HTML - NAV_SEVER_HEALTH || 服务器健康度 -HTML - NETWORK || 服务器网络 -HTML - NETWORK_INFORMATION || 服务器网络信息 -HTML - NEW || 新建 HTML - NEW_CALENDAR || 新: -HTML - NEW_PLAYERS_TEXT || 新玩家 -HTML - NEW_RETENTION || 新玩家留坑率 -HTML - NEW_TEXT || 新建 -HTML - NICKNAME || 昵称 HTML - NO_KILLS || 无击杀数 -HTML - NO_PLAYER_CAUSED_DEATHS || 无被玩家击杀数 HTML - OFFLINE || 离线 HTML - ONLINE || 在线 -HTML - ONLINE_ACTIVITY || 在线活动 -HTML - OPERATOR || 管理员 -HTML - OVERVIEW || 预览 HTML - PER_DAY || / 天 -HTML - PLAYER_CAUSED_DEATHS || 被玩家击杀数 -HTML - PLAYER_KILLS || 玩家击杀数 -HTML - PLAYER_LIST || 玩家列表 -HTML - PLAYERBASE_DEVELOPMENT || 玩家发展 -HTML - PLAYERS || 玩家 -HTML - PLAYERS_ONLINE || 在线玩家 -HTML - PLAYERS_ONLINE_TEXT || 在线玩家 HTML - PLAYERS_TEXT || 玩家 -HTML - PLAYTIME || 游玩时间 -HTML - PLEASE_WAIT || 请稍候··· -HTML - PREDICETED_RETENTION || 预计留坑率 -HTML - PUNCH_CARD || 打卡签到 -HTML - PUNCHCARD || 打卡签到 -HTML - RECENT_LOGINS || 近期登陆 -HTML - REGISTERED || 已注册 -HTML - REGISTERED_TEXT || 已注册 -HTML - REGULAR || 经常 -HTML - SEEN_NICKNAMES || 可视昵称 -HTML - SERVER || 服务器 -HTML - SERVER_ANALYSIS || 服务器分析 -HTML - SERVER_HEALTH_ESTIMATE || 服务器健康估计 -HTML - SERVER_INFORMATION || 服务器信息 -HTML - SERVER_PREFERENCE || 服务器偏好设置 -HTML - SERVERS || 服务器 HTML - SESSION || 时域 -HTML - SESSION_ENDED || 时域之末 -HTML - SESSION_LENGTH || 时域长度 -HTML - SESSION_MEDIAN || 平均时域 -HTML - SESSIONS || 时域 -# Not sure if this is the time or count of numbers. -HTML - TIME || 次数 -HTML - TIMES_KICKED || 被踢次数 -HTML - TIMES_USED || 占用 +HTML - SIDE_GEOLOCATIONS || 地理位置 +HTML - SIDE_INFORMATION || 信息 +HTML - SIDE_NETWORK_OVERVIEW || 网络预览 +HTML - SIDE_OVERVIEW || 预览 +HTML - SIDE_PERFORMANCE || 性能 +HTML - SIDE_PLAYER_LIST || 玩家列表 +HTML - SIDE_PLAYERBASE || Playerbase +HTML - SIDE_PLAYERBASE_OVERVIEW || Playerbase预览 +HTML - SIDE_PLUGINS || 插件 +HTML - SIDE_PVP_PVE || PvP & PvE +HTML - SIDE_SERVERS || 服务器 +HTML - SIDE_SERVERS_TITLE || 服务器 +HTML - SIDE_SESSIONS || 时域 +HTML - SIDE_TO_MAIN_PAGE || 到主页面 +HTML - TEXT_CLICK_TO_EXPAND || 点击拓展 +HTML - TEXT_CONTRIBUTORS_CODE || 码农 +HTML - TEXT_CONTRIBUTORS_LOCALE || 译者 +HTML - TEXT_CONTRIBUTORS_MONEY || 特别感谢那些在经济上支持该插件发展的人. +HTML - TEXT_CONTRIBUTORS_THANKS || 另外以下 awesome people 也为该插件作出了贡献: +HTML - TEXT_DEV_VERSION || 该版本为开发版本. +HTML - TEXT_DEVELOPED_BY || 的开发者为 +HTML - TEXT_FIRST_SESSION || First session +HTML - TEXT_LICENSED_UNDER || is developed and licensed under +HTML - TEXT_METRICS || bStats Metrics +HTML - TEXT_NO_EXTENSION_DATA || 没有拓展数据 +HTML - TEXT_NO_LOW_TPS || No low tps spikes +HTML - TEXT_NO_SERVER || No server to display online activity for +HTML - TEXT_NO_SERVERS || No servers found in the database +HTML - TEXT_PLUGIN_INFORMATION || 插件信息 +HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players +HTML - TEXT_VERSION || 有新版本的插件可用. +HTML - TITLE_30_DAYS || 30 天 +HTML - TITLE_30_DAYS_AGO || 30 天前 +HTML - TITLE_ALL || 全部 +HTML - TITLE_ALL_TIME || 所有时间 +HTML - TITLE_AS_NUMBERS || 作为数字 +HTML - TITLE_AVG_PING || 平均延迟 +HTML - TITLE_BEST_PING || 最低延迟 +HTML - TITLE_CALENDAR || 日历 +HTML - TITLE_CONNECTION_INFO || 连接信息 +HTML - TITLE_COUNTRY || 国家 +HTML - TITLE_CPU_RAM || CPU & RAM +HTML - TITLE_CURRENT_PLAYERBASE || 当前玩家 +HTML - TITLE_DISK || 磁盘空间 +HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day +HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Network Online Activity +HTML - TITLE_GRAPH_PUNCHCARD || 打卡签到30天 +HTML - TITLE_INSIGHTS || Insights30天 +HTML - TITLE_IS_AVAILABLE || 可用 +HTML - TITLE_LAST_24_HOURS || 过去 24 小时 +HTML - TITLE_LAST_30_DAYS || 过去 30 天 +HTML - TITLE_LAST_7_DAYS || 过去 7 天 +HTML - TITLE_LAST_CONNECTED || 最后连接 +HTML - TITLE_LENGTH || 长度 +HTML - TITLE_MOST_PLAYED_WORLD || 最多玩家的世界 +HTML - TITLE_NETWORK || 服务器网络 +HTML - TITLE_NETWORK_AS_NUMBERS || Network as Numbers +HTML - TITLE_NOW || 现在 +HTML - TITLE_ONLINE_ACTIVITY || 在线活动 +HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || Online Activity as Numbers +HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || Online Activity Overview +HTML - TITLE_PERFORMANCE_AS_NUMBERS || Performance as Numbers +HTML - TITLE_PING || 延迟 +HTML - TITLE_PLAYER || 玩家 +HTML - TITLE_PLAYER_OVERVIEW || 玩家预览 +HTML - TITLE_PLAYERBASE_DEVELOPMENT || 玩家发展 +HTML - TITLE_PVP_DEATHS || 最近的PVP死亡 +HTML - TITLE_PVP_KILLS || 最近的PVP击杀 +HTML - TITLE_PVP_PVE_NUMBERS || PvP & PvE as Numbers +HTML - TITLE_RECENT_KILLS || 最近击杀 +HTML - TITLE_RECENT_SESSIONS || 最近时域 +HTML - TITLE_SEEN_NICKNAMES || 可视昵称 +HTML - TITLE_SERVER || 服务器 +HTML - TITLE_SERVER_AS_NUMBERS || Server as Numbers +HTML - TITLE_SERVER_OVERVIEW || 服务器预览 +HTML - TITLE_SERVER_PLAYTIME || 服务器游戏时间 +HTML - TITLE_SERVER_PLAYTIME_30 || 服务器30天的游戏时间 +HTML - TITLE_SESSION_START || 开始的时域 +HTML - TITLE_THEME_SELECT || 选择主题 +HTML - TITLE_TITLE_PLAYER_PUNCHCARD || Punchcard +HTML - TITLE_TPS || TPS +HTML - TITLE_TREND || 趋势 +HTML - TITLE_TRENDS || 30天趋势 +HTML - TITLE_VERSION || 版本 +HTML - TITLE_WEEK_COMPARISON || 每周对比 +HTML - TITLE_WORLD || 加载世界 +HTML - TITLE_WORLD_PLAYTIME || 世界游玩时间 +HTML - TITLE_WORST_PING || 最高延迟 HTML - TOTAL_ACTIVE_TEXT || 总活跃玩家 HTML - TOTAL_AFK || 总挂机玩家 HTML - TOTAL_PLAYERS || 总玩家 -HTML - TOTAL_PLAYTIME || 总在线时长 -HTML - UNIQUE || 普通 HTML - UNIQUE_CALENDAR || 普通玩家: -HTML - UNIQUE_PLAYERS || 普通玩家 -HTML - UNIQUE_PLAYERS_TEXT || 普通玩家 -HTML - UNIQUE_TEXT || 普通玩家 -HTML - USAGE || 占用 -HTML - USED_COMMANDS || 使用的命令 +HTML - UNIT_CHUNKS || Chunks +HTML - UNIT_ENTITIES || Entities +HTML - UNIT_NO_DATA || 没有数据 +HTML - UNIT_THE_PLAYERS || 玩家 HTML - USER_AND_PASS_NOT_SPECIFIED || 未指定用户名与密码 HTML - USER_DOES_NOT_EXIST || 用户不存在 -HTML - USER_INFORMATION || 用户信息 HTML - USER_PASS_MISMATCH || 用户名与密码不匹配 HTML - WITH ||
    与 -HTML - WORLD || 世界 -HTML - WORLD_LOAD || 世界加载 -HTML - WORLD_PLAYTIME || 世界游玩时间 -HTML - WORST_PING || 最高延迟 HTML ERRORS - ACCESS_DENIED_403 || 拒绝访问 -HTML ERRORS - ANALYSIS_REFRESH || 正在刷新分析··· -HTML ERRORS - ANALYSIS_REFRESH_LONG || 正在执行分析,请在几秒后刷新页面··· HTML ERRORS - AUTH_FAIL_TIPS_401 || - 确保您已使用 /plan register 注册用户
    - 检查用户名与密码是否正确
    - 用户名与密码区分大小写

    若您忘记了密码,请让工作人员删除您的旧密码并重新注册。 HTML ERRORS - AUTHENTICATION_FAILED_401 || 认证失败。 HTML ERRORS - FORBIDDEN_403 || 禁止访问 @@ -261,51 +306,49 @@ In Depth Help - /plan inspect ? || §2检视命令\§f 用于 In Depth Help - /plan manage ? || §2管理命令\§f 用于管理插件数据库。\§7 别名:/plan m\§7 /plan m - Auflistung der Unterbefehle\§7 /plan m <子命令> ? - 详细帮助 In Depth Help - /plan manage backup ? || > §2备份命令\ 用于建立新 SQLite 数据库(.db 文件)并包含当前在计划插件文件夹中所有的活跃数据库内容。 In Depth Help - /plan manage clear ? || §2管理清除命令\§f 用于清除活跃数据库中的所有数据。\§7 插件在清理完毕后应被重载。\§7 别名:/plan pl -In Depth Help - /plan manage con ? || > §2调试连接命令\ 用于调试网络间的连接。\ 向数据库中的每个服务器发送请求。 -In Depth Help - /plan manage disable ? || > §2禁用命令\ 用于在下次重新载入之前禁用插件的部分特性。\ 接受的参数:\ §2kickcount §f禁用在关闭服务器时 /kickall 的踢出计数。 +In Depth Help - /plan manage disable ? || > §2关闭子命令\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. +In Depth Help - /plan manage export ? || > §2导出子命令\ Trigger export to result folders.\ Accepted Arguments:\ §2list §fList possible arguments.\ §2players §fExport /players, /player pages + /player/raw json depending on config values.\ §2server_json §fExport /server/raw JSON if enabled in config. In Depth Help - /plan manage import ? || §2管理导入命令\§f 用于从其它来源导入数据\§7 在导入过程中将禁用数据分析。 In Depth Help - /plan manage move ? || > §2移动指令\ 将数据从 SQLite 移动至 MySQL,反之亦然。\ 目标数据库将在转移前被清除。 +In Depth Help - /plan manage raw ? || > §2Raw数据子命令\ Displays link to raw JSON data page.\ Not available if Plan webserver is not enabled. In Depth Help - /plan manage remove ? || §2管理移除命令\§f 用于从活跃数据库中移除用户数据。 In Depth Help - /plan manage restore ? || > §2恢复命令\ 从先前备份的 SQLite 数据库中恢复数据(.db 文件)\ 您也可以从其他服务器中恢复 database.db 到 MySQL。\ 目标数据库将在传输前被清除。 -In Depth Help - /plan manage setup ? || > §2安装命令\ 设置 Bungee 与此服务器之间的网络连接。\ Bungee 地址可在计划于 Bungee 上启动时的控制台日志中找到。 +In Depth Help - /plan manage uninstalled ? || > §2卸载服务器的子命令\ 在数据库中将一个服务器视为未安装。\ Can not mark the server the command is being used on as uninstalled.\ 将会影响连接系统。 In Depth Help - /plan network ? || > §2网络命令\ 显示到网络页的链接。\ 若不在群组网络上,此页面将显示为服务器页面。 In Depth Help - /plan players ? || > §2玩家命令\ 显示到玩家页的链接。 In Depth Help - /plan qinspect ? || §2快速检视命令\§f 用于获取游戏内关于检视的信息。\§7 相比检视网页有着更少的信息。\§7 别名:/plan qi In Depth Help - /plan reload ? || > §2重载命令\ 使用 onDisable 与 onEnable 重新载入插件。\ §b不支持运行时热切换插件 In Depth Help - /plan search ? || §2搜索命令\§f 用于获取匹配特定参数的玩家名列表。\§7 示例:/plan search 123 - 搜索名称中包含 123 的所有玩家。 In Depth Help - /plan servers ? || > §2服务器命令\ 显示数据库中的计划服务器列表。\ 可用于调试在网络上数据库注册的问题。 -In Depth Help - /plan update ? || > §2更新命令\ 用于在下次关闭服务器时更新插件\ /plan update - 更新日志链接\ /plan update -u - 在所有在线的网络服务器上预定在下次重新启动时更新。\ /plan update cancel - 取消尚未重启的服务器上的更新。 In Depth Help - /plan web ? || < §2网页用户管理命令。\ §2/plan web §f列出子命令\ §2/plan web <子命令> ? §f详细帮助 In Depth Help - /plan web register ? || > §2注册命令\ 注册一位新的网页用户。\ 为其他用户注册需要 plan.webmanage 权限。\ 密码通过使用随机密码盐的 PBKDF2 哈希方法加密(重复 64000 次 SHA1)。 In Depth Help - /planbungee disable ? || > §2禁用命令\ 在 PlanBungee 上运行 onDisable。\ 插件之后需要运行 /planbungee reload 以重新启用。\ §b不支持运行时热切换插件 -In Depth Help - /planbungee setup ? || > §2设置开关命令\ 开关 Bungee 上的安装功能。\ 用来防止与其他服务器之间未授权的 MySQL 监听。 Manage - Confirm Overwrite || 数据库 ${0} 中的数据将被覆盖! Manage - Confirm Removal || 数据库 ${0} 中的数据将被移除! -Manage - Fail || > §c[计划] 处理数据时发生错误! ${0} -Manage - Fail File not found || > §c[计划] 备份文件不存在! ${0} +Manage - Fail || > §c[数据统计] 处理数据时发生错误! ${0} +Manage - Fail File not found || > §c[数据统计] 备份文件不存在! ${0} Manage - Fail Incorrect Database || > §c'${0}' 不是一个支持的数据库。 +Manage - Fail No Exporter || §e导出器 '${0}' 不存在 Manage - Fail No Importer || §e导入器 '${0}' 不存在 -Manage - Fail Same Database || > §c[计划] 无法移动至相同的数据库! -Manage - Fail, Confirmation || > §c[计划] 请添加 -a 以确认执行!${0} -Manage - Fail, Connection Exception || §e失败原因: -Manage - Fail, No Servers || §c未在数据库中找到服务器。 -Manage - Fail, Old version || §e失败原因:接收服务器使用了旧版本的插件。 -Manage - Fail, Unauthorized || §e失败原因:未获得授权。服务器可能使用不同的数据库。 -Manage - Fail, Unexpected Exception || §e异常例外:${0} +Manage - Fail No Server || 找不到这个服务器. +Manage - Fail Same Database || > §c[数据统计] 无法移动至相同的数据库! +Manage - Fail Same server || 无法将服务器标记为未安装 (你已在该服务器) +Manage - Fail, Confirmation || > §c[数据统计] 请添加 -a 以确认执行!${0} Manage - List Importers || 导入器: -Manage - Notify External Url || §e非本地地址,请检查端口是否开放 -Manage - Remind HotSwap || §e[计划] 请记住要切换至新数据库并重新载入插件(/plan m hotswap ${0}) -Manage - Start || »§7 正在处理数据··· +Manage - Remind HotSwap || §e[数据统计] 请切换至新数据库并重载插件(/plan m hotswap ${0}) +Manage - Start || »§7 正在处理数据... Manage - Success || §f» §2 成功! Negative || 否 Positive || 是 +Today || '今天' +Unavailable || Unavailable Unknown || 未知 -Version - DEV || 这是开发者版本。 +Version - DEV || 这是开发版本。 Version - Latest || 您正使用最新版本。 -Version - New || 新版本(${0})可用 ${1} +Version - New || 有新版本(${0})可用 ${1} Version - New (old) || 新版本在 ${0} 可用 Version FAIL - Read info (old) || 无法检查最新版本号 -Version FAIL - Read versions.txt || 无法从 Github/versions.txt 载入版本信息 +Version FAIL - Read versions.txt || 无法从 Github/versions.txt 加载版本信息 Web User Listing || §2${0} §7:§f${1} WebServer - Notify HTTP || 网页服务器:无证书 -> 正使用 HTTP 服务器提供可视化效果。 WebServer - Notify HTTP User Auth || 网页服务器:已禁用用户认证!(HTTP 方式不安全) @@ -314,24 +357,3 @@ WebServer FAIL - Port Bind || 未成功初始化网页服 WebServer FAIL - SSL Context || 网页服务器:SSL 环境初始化失败。 WebServer FAIL - Store Load || 网页服务器:SSL 证书载入失败。 Yesterday || '昨天' -Today || '今天' -Health - Active Playtime Comparison Decrease || 玩家可能闲得没事干了 (活跃玩家数 ${0} vs ${1}, 最近2周 vs 2-4周) -Health - Active Playtime Comparison Increase || 玩家可能有很多事要做 (活跃玩家数 ${0} vs ${1}, 最近2周 vs 2-4周) -Health - Downtime || 服务器停机时间为 (无数据传输的时间) ${0} -Health - New Player Join Players, No || 新玩家里也许很少有人继续玩下去 (平均为 ${0} ) -Health - New Player Join Players, Yes || 新玩家里有人玩了下去 (平均为 ${0} ) -Health - New Player Stickiness || ${0} 的新玩家留了下来 (${1}/${2}) -Health - No Servers Inaccuracy || 没有可收集数据的 Bukkit/Sponge 服务器 - 这些数据不准确. -Health - Player Play on Network || 在这个服务器网络内游玩的玩家: -Health - Player Register Server || 平均每日每服务器注册玩家数. -Health - Player Visit Server || 平均每日每服务器登录服务器玩家数. -Health - Regular Activity Change || 普通玩家数 -Health - Regular Activity Change Decrease || 减少了 (${0}) -Health - Regular Activity Change Increase || 增加了 (+${0}) -Health - Regular Activity Change Zero || 保持不变 (+${0}) -Health - Regular Activity Remain || ${0} 普通玩家仍保持活跃 (${1}/${2}) -Health - Single Servers Inaccuracy || 收集单个 Bukkit/Sponge 服务器的时域数据. -Health - TPS Above Low Threshold || ${0} 的平均TPS超过了最低门槛 -Health - TPS Low Dips || 平均TPS低于最低门槛 (${0}) ${1} 此 -HTML - FREE_DISK_SPACE || 剩余磁盘空间 -HTML - DISK_SPACE || 磁盘空间 \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_DE.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_DE.txt index 548b8a099..889afd65a 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_DE.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_DE.txt @@ -1,6 +1,7 @@ Cmd - Click Me || Klicke hier Cmd - Link || §2Link: §f Cmd Disable - Disabled || §aPlan ist nun deaktiviert. Nutze /planbungee reload um das Plugin neu zu starten. +Cmd FAIL - Database not open || §cDatabase is ${0} - Please try again a bit later. Cmd FAIL - Invalid Username || §cDieser Benutzer besitzt keine UUID. Cmd FAIL - No Feature || §eWelches Feature soll deaktiviert werden? (momentan unterstützt: ${0}) Cmd FAIL - No Permission || §cDafür fehlt dir die Berechtigung. @@ -35,29 +36,9 @@ Cmd Qinspect - Player Kills || §2Getötete Spieler: §f${ Cmd Qinspect - Playtime || §Spielzeit: §f${0} Cmd Qinspect - Registered || §2Registrierung: §f${0} Cmd Qinspect - Times Kicked || §2Kicks: §f${0} -Cmd Setup - Allowed || §aSetupmodus wurde aktiviert. -Cmd Setup - Bad Request || §eVerbindung hergestellt. Der empfangende Server ist ein Bukkit- oder Sponge-server. Nutze stattdessen die andere Adresse. -Cmd Setup - Disallowed || §cSet-up wurde deaktiviert. -Cmd Setup - Forbidden || §eVerbindung hergestellt aber der Bungeecordserver hat den Setupmodus nicht aktiviert. Nutze '/planbungee setup' um ihn zu aktivieren. -Cmd Setup - Gateway Error || §eVerbindung hergestellt, aber der Bungeecordserver konnte sich nicht mit diesem Server verbinden (Wurde der aktuelle Server neugestartet?). Nutze /plan m con & /planbungee con zum debuggen. -Cmd Setup - Generic Fail || §eVerbindung fehlgeschlagen: ${0} -Cmd Setup - Internal Error || §eVerbindung hergestellt. ${0}, eventuelle Fehler kannst du dem Error-Log auf der Debugseite des empfangenden Servers entnehmen. -Cmd Setup - Success || §aVerbindung erfolgreich, Plan startet eventuell in ein paar Sekunden neu. -Cmd Setup - Unauthorized || §eVerbindung erfolgreich, aber der empfangende Server hat diesen Server nicht autorisiert. Auf dem Plan-Discord bekommst du Hilfe. -Cmd Setup - Url mistake || §cNutze die gesamte Adresse (Beginnend mit http:// oder https://) - Diese kannst du dem Bungee enable log entnehmen. -Cmd Setup - WebServer not Enabled || §cWebServer ist auf diesem Server deaktiviert! Dies sollte beim Start aktiviert werden! Cmd SUCCESS - Feature disabled || §a'${0}' wurde bis zum nächsten Reload des Plugins deaktiviert. Cmd SUCCESS - WebUser register || §aNeuer Account (${0}) erfolgreich hinzugefügt! Sie können das Web-Panel über den folgenden Link anzeigen. -Cmd Update - Cancel Success || §aErfolgreich abgebrochen. -Cmd Update - Cancelled || §cUpdate abgebrochen. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cAuf einem Server war das Update nicht erfolgreich. Das Update wird auf allen Servern abgebrochen -Cmd Update - Fail Force Notify || §e${0} wurde nicht geupdated, -force wurde angegeben, fahre mit Updates fort. -Cmd Update - Fail Not Online || §cNicht alle Server sind online oder erreichbar. Erreichbare Server kannst du mit /plan update -u -force updaten -Cmd Update - Notify Cancel || §aDu kannst das Update auf Servern, die noch nicht neugestartet wurden, verwerfen mit: /plan update cancel. -Cmd Update - Online Check || Überprüfe ob alle Server online sind. -Cmd Update - Scheduled || §aUpdate für ${0} geplant. -Cmd Update - Url mismatch || §cDie Download-URL beginnt nicht mit ${0} und ist evtl. nicht vertrauenswürdig. Du kannst diese Version manuell hier downloaden (direkter Download): +Cmd WARN - Database not open || §eDatabase is ${0} - This might take longer than expected.. Cmd Web - Permission Levels || >\§70: Zugriff auf alle Seiten\§71: Zugriff auf '/players' Und alle Spielerseiten\§72: Zugriff auf alle Spielerseiten mit dem gleichen Username wie der Web-Account\§73+: Keine Berechtigung Command Help - /plan analyze || Server-Übersicht Command Help - /plan dev || Entwicklungsmodus-Befehl @@ -67,14 +48,15 @@ Command Help - /plan inspect || Zeigt eine Spielerseite an Command Help - /plan manage || Verwaltet die Plan-Datenbank Command Help - /plan manage backup || Erstellt ein Backup der Datenbank Command Help - /plan manage clear || Datenbank leeren -Command Help - /plan manage con || Debug Server-Bungee Verbindungen Command Help - /plan manage disable || Schalte eine Funktion temporär aus +Command Help - /plan manage export || Trigger export manually Command Help - /plan manage hotswap || Ändere die Datenbank schnell Command Help - /plan manage import || Daten importieren Command Help - /plan manage move || Bewege die Daten zwischen den Datenbanken +Command Help - /plan manage raw || View raw JSON of player data Command Help - /plan manage remove || Entferne die Daten eines Spielers Command Help - /plan manage restore || Spiele ein Backup ein -Command Help - /plan manage setup || Stelle die Server-Bungee-Verbindung her +Command Help - /plan manage uninstalled || Mark a server as uninstalled in the database. Command Help - /plan network || Netzwerk-Seite Command Help - /plan players || Spieler-Seite Command Help - /plan qinspect || Zeigt die Spielerinfo im Spiel @@ -82,15 +64,12 @@ Command Help - /plan register || Registriere einen Account Command Help - /plan reload || Plan neuladen Command Help - /plan search || Nach einem Spieler suchen Command Help - /plan servers || Liste die Server in der Datenbank auf -Command Help - /plan update || Zeige das Änderungsprotokoll oder update den Server Command Help - /plan web check || Infos über einen Account Command Help - /plan web delete || Lösche einen Account Command Help - /plan web level || Informationen über Rechte Command Help - /plan web list || Liste Accounts Command Help - /plan webuser || Verwalte Accounts -Command Help - /planbungee con || Debug Bungee-Server Verbindungen Command Help - /planbungee disable || Deaktiviert das Plugin temporär -Command Help - /planbungee setup || Schaltet Setup-Modus an oder aus Database - Apply Patch || Wende Patch an: ${0}.. Database - Patches Applied || Alle Datenbankpatches wurden erfolgreich angewendet. Database - Patches Applied Already || Alle Datenbankpatches wurden bereits angewendet. @@ -100,10 +79,10 @@ Database Notify - SQLite No WAL || SQLite WAL wird auf dieser Se Disable || Player Analytics ausgeschaltet. Disable - Processing || Verarbeite kritische unverarbeitete Aufgaben. (${0}) Disable - Processing Complete || Verarbeitung komplett. +Disable - Unsaved Session Save || Saving unfinished sessions.. Disable - WebServer || Webserver deaktiviert. Enable || Player Analytics eingeschaltet. Enable - Database || ${0}-dDatenbankverbindung hergestellt. -Enable - Notify Address Confirmation || Versichere dich, dass die Adresse auf DIESEN Server verweist: ${0} Enable - Notify Empty IP || IP in der server.properties ist leer & AlternativeIP ist nicht in Verwendung. Es werden falsche Links verwendet! Enable - Notify Geolocations disabled || Geolocation wird nicht aufgezeichnet (Data.Geolocations: false) Enable - Notify Geolocations Internet Required || Plan braucht einen Internetzugang um die GeoLite2 Geolocation Datenbank runterzuladen. @@ -112,138 +91,205 @@ Enable - WebServer || Webserver läuft auf PORT ${0 Enable FAIL - Database || ${0}-Datenbankverbindung fehlgeschlagen: ${1} Enable FAIL - Database Patch || Datenbank-Patch ist fehlgeschlagen. Plugin wurde deaktiviert. Wir bitten dich, uns diesen Vorfall mitzuteilen. Enable FAIL - GeoDB Write || Etwas ist beim Speichern der GeoLite2 Geolocation Datenbank fehlgeschlagen -Enable FAIL - WebServer (Bungee) || Webserver ist nicht geladen +Enable FAIL - WebServer (Proxy) || Webserver ist nicht geladen! Enable FAIL - Wrong Database Type || ${0} ist keine gültige Datenbank -HTML - ACTIVITY_INDEX || Aktivitätsindex -HTML - ALL || Gesamt -HTML - ALL_TIME_PEAK || Rekord -HTML - AVERAGE_PING || Durchschnittlicher Ping -HTML - AVG || AVG -HTML - BANNED || Gebannt -HTML - BEST_PING || Bester Ping -HTML - CALENDAR || KALENDER -HTML - CALENDAR_TEXT || Kalender -HTML - CHUNKS || Chunks -HTML - COMMAND || Befehl -HTML - COMMNAND_USAGE || Befehlsverwendung -HTML - CONNECTION_INFORMATION || Verbindungsinformationen -HTML - COUNTRY || Land -HTML - CURRENT_PLAYERBASE || Aktuelle Playerbase -HTML - DEATHS || Tode -HTML - ENTITIES || Entitäten +HTML - COMPARING_15_DAYS || Comparing 15 days +HTML - COMPARING_60_DAYS || Comparing 30d ago to Now +HTML - COMPARING_7_DAYS || Comparing 7 days +HTML - DATABASE_NOT_OPEN || Database is not open, check db status with /plan info HTML - ERROR || Authentifikation fehlgeschlagen -HTML - FAVORITE_SERVER || Lieblingsserver -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Geschätzte Gesundheit HTML - INDEX_ACTIVE || Aktiv HTML - INDEX_INACTIVE || Inaktiv HTML - INDEX_IRREGULAR || Unregelmäßig HTML - INDEX_REGULAR || Regelmäßig HTML - INDEX_VERY_ACTIVE || Sehr aktiv -HTML - IP_ADDRESS || IP-Adresse HTML - KILLED || Getötet -HTML - KILLED_BY || Getötet von -HTML - LAST_24_HOURS || Letzte 24 Stunden -HTML - LAST_30_DAYS || Letzte 30 Tage -HTML - LAST_30_DAYS_TEXT || Letzte 30 Tage -HTML - LAST_7_DAYS || Letzte 7 Tage -HTML - LAST_CONNECTED || Letzte Verbindung -HTML - LAST_PEAK || Letzter Höchststand -HTML - LAST_SEEN || Zuletzt gesehen -HTML - LAST_SEEN_TEXT || Zuletzt gesehen -HTML - LOADED_CHUNKS || Geladene Chunks -HTML - LOADED_ENTITIES || Geladenen Entitäten +HTML - LABEL_1ST_WEAPON || Deadliest PvP Weapon +HTML - LABEL_2ND_WEAPON || 2nd PvP Weapon +HTML - LABEL_3RD_WEAPON || 3rd PvP Weapon +HTML - LABEL_ACTIVITY_INDEX || Aktivitätsindex +HTML - LABEL_AFK || AFK +HTML - LABEL_AFK_TIME || AFK Time +HTML - LABEL_AVG || Average +HTML - LABEL_AVG_KDR || Average KDR +HTML - LABEL_AVG_MOB_KDR || Average Mob KDR +HTML - LABEL_AVG_PLAYTIME || Average Playtime +HTML - LABEL_AVG_SESSION_LENGTH || Average Session Length +HTML - LABEL_AVG_TPS || Average TPS +HTML - LABEL_BANNED || Gebannt +HTML - LABEL_BEST_PEAK || Rekord +HTML - LABEL_DAY_OF_WEEK || Day of the Week +HTML - LABEL_DEATHS || Tode +HTML - LABEL_DOWNTIME || Downtime +HTML - LABEL_DURING_LOW_TPS || During Low TPS Spikes: +HTML - LABEL_ENTITIES || Entitäten +HTML - LABEL_FAVORITE_SERVER || Lieblingsserver +HTML - LABEL_FIRST_SESSION_LENGTH || First session length +HTML - LABEL_FREE_DISK_SPACE || Freier Festplattenspeicher +HTML - LABEL_INACTIVE || Inactive +HTML - LABEL_LAST_PEAK || Letzter Höchststand +HTML - LABEL_LAST_SEEN || Zuletzt gesehen +HTML - LABEL_LOADED_CHUNKS || Geladene Chunks +HTML - LABEL_LOADED_ENTITIES || Geladenen Entitäten +HTML - LABEL_LONE_JOINS || Lone joins +HTML - LABEL_LONE_NEW_JOINS || Lone newbie joins +HTML - LABEL_LONGEST_SESSION || Longest Session +HTML - LABEL_LOW_TPS || Low TPS Spikes +HTML - LABEL_MAX_FREE_DISK || Max Free Disk +HTML - LABEL_MIN_FREE_DISK || Min Free Disk +HTML - LABEL_MOB_DEATHS || Tode durch Mobs +HTML - LABEL_MOB_KDR || Mob KDR +HTML - LABEL_MOB_KILLS || Mob Kills +HTML - LABEL_MOST_ACTIVE_GAMEMODE || Most Active Gamemode +HTML - LABEL_NAME || Name +HTML - LABEL_NEW || New +HTML - LABEL_NEW_PLAYERS || Neue Spieler +HTML - LABEL_NICKNAME || Nickname +HTML - LABEL_NO_SESSION_KILLS || None +HTML - LABEL_ONLINE_FIRST_JOIN || Players online on first join +HTML - LABEL_OPERATOR || Operator +HTML - LABEL_PER_PLAYER || / Player +HTML - LABEL_PER_REGULAR_PLAYER || / Regular Player +HTML - LABEL_PLAYER_DEATHS || Tode durch Spieler +HTML - LABEL_PLAYER_KILLS || Getötete Spieler +HTML - LABEL_PLAYERS_ONLINE || Spieler Online +HTML - LABEL_PLAYTIME || Spielzeit +HTML - LABEL_REGISTERED || Registriert +HTML - LABEL_REGISTERED_PLAYERS || Registered Players +HTML - LABEL_REGULAR || Regular +HTML - LABEL_REGULAR_PLAYERS || Regular Players +HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity +HTML - LABEL_RETENTION || Erhaltung neuer Spieler +HTML - LABEL_SERVER_DOWNTIME || Server Downtime +HTML - LABEL_SERVER_OCCUPIED || Server occupied +HTML - LABEL_SESSION_ENDED || Session beendet +HTML - LABEL_SESSION_MEDIAN || Session Durchschnitt +HTML - LABEL_TIMES_KICKED || Mal gekickt +HTML - LABEL_TOTAL_PLAYERS || Total Players +HTML - LABEL_TOTAL_PLAYTIME || Total Playtime +HTML - LABEL_UNIQUE_PLAYERS || Einzigartige Spieler +HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' +HTML - LINK_BACK_NETWORK || Network page +HTML - LINK_BACK_SERVER || Server page +HTML - LINK_CHANGELOG || View Changelog +HTML - LINK_DISCORD || General Support on Discord +HTML - LINK_DOWNLOAD || Download +HTML - LINK_ISSUES || Report Issues +HTML - LINK_NIGHT_MODE || Night Mode +HTML - LINK_PLAYER_PAGE || Player Page +HTML - LINK_QUICK_VIEW || Quick view +HTML - LINK_SERVER_ANALYSIS || Server Analyse +HTML - LINK_WIKI || Plan Wiki, Tutorials & Documentation HTML - LOCAL_MACHINE || Lokale Maschine -HTML - LONGEST || Längste -HTML - LOW_TPS_SPIKES || Geringe TPS Spikes -HTML - MOB_CAUSED_DEATHS || Tode durch Mobs -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Letzte Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Befehlsverwendung -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Netzwerk Spieler -HTML - NAV_ONLINE_ACTIVITY || Onlineaktivität -HTML - NAV_OVERVIEW || Übersicht -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Spieler HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Servergesundheit -HTML - NETWORK || Netzwerk -HTML - NETWORK_INFORMATION || Netzwerkinformationen -HTML - NEW || Neu HTML - NEW_CALENDAR || Neu: -HTML - NEW_PLAYERS_TEXT || Neue Spieler -HTML - NEW_RETENTION || Erhaltung neuer Spieler -HTML - NEW_TEXT || Neu -HTML - NICKNAME || Nickname HTML - NO_KILLS || Keine Kills -HTML - NO_PLAYER_CAUSED_DEATHS || Keine Tode durch Spieler HTML - OFFLINE || Offline HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE AKTIVITÄT -HTML - OPERATOR || Operator -HTML - OVERVIEW || ÜBERSICHT HTML - PER_DAY || / Tag -HTML - PLAYER_CAUSED_DEATHS || Tode durch Spieler -HTML - PLAYER_KILLS || Getötete Spieler -HTML - PLAYER_LIST || Spielerliste -HTML - PLAYERBASE_DEVELOPMENT || Entwicklung der Playerbase -HTML - PLAYERS || SPIELER -HTML - PLAYERS_ONLINE || SPIELER ONLINE -HTML - PLAYERS_ONLINE_TEXT || Spieler Online HTML - PLAYERS_TEXT || Spieler -HTML - PLAYTIME || Spielzeit -HTML - PLEASE_WAIT || Bitte warten... -HTML - PREDICETED_RETENTION || Vorhersage Erhaltung -HTML - PUNCH_CARD || Lochkarte -HTML - PUNCHCARD || LOCHKARTE -HTML - RECENT_LOGINS || Letzte LOGINS -HTML - REGISTERED || REGISTRIERT -HTML - REGISTERED_TEXT || Registriert -HTML - REGULAR || REGELMÄSSIGE -HTML - SEEN_NICKNAMES || Registrierte Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analyse -HTML - SERVER_HEALTH_ESTIMATE || Server Gesundheitsschätzung -HTML - SERVER_INFORMATION || SERVER Information -HTML - SERVER_PREFERENCE || Bevorzugter Server -HTML - SERVERS || Server HTML - SESSION || Session -HTML - SESSION_ENDED || Session beendet -HTML - SESSION_LENGTH || Session Länge -HTML - SESSION_MEDIAN || Session Durchschnitt -HTML - SESSIONS || Sessions -HTML - TIME || Zeit -HTML - TIMES_KICKED || Mal gekickt -HTML - TIMES_USED || Mal benutzt +HTML - SIDE_GEOLOCATIONS || Geolocations +HTML - SIDE_INFORMATION || INFORMATION +HTML - SIDE_NETWORK_OVERVIEW || Network Overview +HTML - SIDE_OVERVIEW || Übersicht +HTML - SIDE_PERFORMANCE || Performance +HTML - SIDE_PLAYER_LIST || Spielerliste +HTML - SIDE_PLAYERBASE || Playerbase +HTML - SIDE_PLAYERBASE_OVERVIEW || Playerbase Overview +HTML - SIDE_PLUGINS || PLUGINS +HTML - SIDE_PVP_PVE || PvP & PvE +HTML - SIDE_SERVERS || Server +HTML - SIDE_SERVERS_TITLE || SERVERS +HTML - SIDE_SESSIONS || Sessions +HTML - SIDE_TO_MAIN_PAGE || to main page +HTML - TEXT_CLICK_TO_EXPAND || Click to expand +HTML - TEXT_CONTRIBUTORS_CODE || code contributor +HTML - TEXT_CONTRIBUTORS_LOCALE || translator +HTML - TEXT_CONTRIBUTORS_MONEY || Extra special thanks to those who have monetarily supported the development. +HTML - TEXT_CONTRIBUTORS_THANKS || In addition following awesome people have contributed: +HTML - TEXT_DEV_VERSION || This version is a DEV release. +HTML - TEXT_DEVELOPED_BY || is developed by +HTML - TEXT_FIRST_SESSION || First session +HTML - TEXT_LICENSED_UNDER || is developed and licensed under +HTML - TEXT_METRICS || bStats Metrics +HTML - TEXT_NO_EXTENSION_DATA || No Extension Data +HTML - TEXT_NO_LOW_TPS || No low tps spikes +HTML - TEXT_NO_SERVER || No server to display online activity for +HTML - TEXT_NO_SERVERS || No servers found in the database +HTML - TEXT_PLUGIN_INFORMATION || Information about the plugin +HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players +HTML - TEXT_VERSION || A new version has been released and is now available for download. +HTML - TITLE_30_DAYS || 30 days +HTML - TITLE_30_DAYS_AGO || 30 days ago +HTML - TITLE_ALL || Gesamt +HTML - TITLE_ALL_TIME || All Time +HTML - TITLE_AS_NUMBERS || as Numbers +HTML - TITLE_AVG_PING || Average Ping +HTML - TITLE_BEST_PING || Best Ping +HTML - TITLE_CALENDAR || Kalender +HTML - TITLE_CONNECTION_INFO || Connection Information +HTML - TITLE_COUNTRY || Land +HTML - TITLE_CPU_RAM || CPU & RAM +HTML - TITLE_CURRENT_PLAYERBASE || Aktuelle Playerbase +HTML - TITLE_DISK || Festplattenspeicher +HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day +HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Network Online Activity +HTML - TITLE_GRAPH_PUNCHCARD || Lochkarte for 30 days +HTML - TITLE_INSIGHTS || Insights for 30 days +HTML - TITLE_IS_AVAILABLE || is Available +HTML - TITLE_LAST_24_HOURS || Letzte 24 Stunden +HTML - TITLE_LAST_30_DAYS || Letzte 30 Tage +HTML - TITLE_LAST_7_DAYS || Letzte 7 Tage +HTML - TITLE_LAST_CONNECTED || Letzte Verbindung +HTML - TITLE_LENGTH || Length +HTML - TITLE_MOST_PLAYED_WORLD || Most played World +HTML - TITLE_NETWORK || Netzwerk +HTML - TITLE_NETWORK_AS_NUMBERS || Network as Numbers +HTML - TITLE_NOW || Now +HTML - TITLE_ONLINE_ACTIVITY || Online Activity +HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || Online Activity as Numbers +HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || Online Activity Overview +HTML - TITLE_PERFORMANCE_AS_NUMBERS || Performance as Numbers +HTML - TITLE_PING || Ping +HTML - TITLE_PLAYER || Player +HTML - TITLE_PLAYER_OVERVIEW || Player Overview +HTML - TITLE_PLAYERBASE_DEVELOPMENT || Entwicklung der Playerbase +HTML - TITLE_PVP_DEATHS || Recent PvP Deaths +HTML - TITLE_PVP_KILLS || Recent PvP Kills +HTML - TITLE_PVP_PVE_NUMBERS || PvP & PvE as Numbers +HTML - TITLE_RECENT_KILLS || Recent Kills +HTML - TITLE_RECENT_SESSIONS || Letzte Sessions +HTML - TITLE_SEEN_NICKNAMES || Registrierte Nicknames +HTML - TITLE_SERVER || Server +HTML - TITLE_SERVER_AS_NUMBERS || Server as Numbers +HTML - TITLE_SERVER_OVERVIEW || Server Overview +HTML - TITLE_SERVER_PLAYTIME || Server Playtime +HTML - TITLE_SERVER_PLAYTIME_30 || Server Playtime for 30 days +HTML - TITLE_SESSION_START || Session Started +HTML - TITLE_THEME_SELECT || Theme Select +HTML - TITLE_TITLE_PLAYER_PUNCHCARD || Punchcard +HTML - TITLE_TPS || TPS +HTML - TITLE_TREND || Trend +HTML - TITLE_TRENDS || Trends for 30 days +HTML - TITLE_VERSION || Version +HTML - TITLE_WEEK_COMPARISON || Week Comparison +HTML - TITLE_WORLD || World Load +HTML - TITLE_WORLD_PLAYTIME || Spielzeit in der Welt +HTML - TITLE_WORST_PING || Worst Ping HTML - TOTAL_ACTIVE_TEXT || Gesamte Aktive Spielzeit HTML - TOTAL_AFK || Gesamte AFK-Zeit HTML - TOTAL_PLAYERS || Gesamte Spieler -HTML - TOTAL_PLAYTIME || Gesamte Spielzeit -HTML - UNIQUE || EINZIGARTIG HTML - UNIQUE_CALENDAR || Einzigartig: -HTML - UNIQUE_PLAYERS || EINZIGARTIGE SPIELER -HTML - UNIQUE_PLAYERS_TEXT || Einzigartige Spieler -HTML - UNIQUE_TEXT || Einzigartig -HTML - USAGE || Nutzung -HTML - USED_COMMANDS || Benutzte Befehle +HTML - UNIT_CHUNKS || Chunks +HTML - UNIT_ENTITIES || Entities +HTML - UNIT_NO_DATA || No Data +HTML - UNIT_THE_PLAYERS || Players HTML - USER_AND_PASS_NOT_SPECIFIED || User und Passwort nicht spezifiziert HTML - USER_DOES_NOT_EXIST || User existiert nicht -HTML - USER_INFORMATION || USER INFORMATION HTML - USER_PASS_MISMATCH || User und Password stimmen nicht überein HTML - WITH ||
    Breite -HTML - WORLD || Welt -HTML - WORLD_LOAD || Weltenladung -HTML - WORLD_PLAYTIME || Spielzeit in der Welt -HTML - WORST_PING || Schlechtester Ping HTML ERRORS - ACCESS_DENIED_403 || Zugriff verweigert -HTML ERRORS - ANALYSIS_REFRESH || Plan wird aktualisiert... -HTML ERRORS - ANALYSIS_REFRESH_LONG || Plan wird ausgeführt. Es wird in ein paar Sekunden neu geladen. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Stelle sicher, dass du einen Account mit /plan register hinzugefügt hast.
    - Überprüfe, ob Passwort und Benutzername korrekt sind
    - Bei Benutzername und Passwort auf Groß- und Kleinschreibung achten!

    - Wenn du dein Passwort vergessen hast, bitte ein Teammitglied deinen Account zu löschen und neu zu erstellen. HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentifizierung fehlgeschlagen. HTML ERRORS - FORBIDDEN_403 || Verboten @@ -260,44 +306,42 @@ In Depth Help - /plan inspect ? || > §2Inspect-Befehl\ Aktuali In Depth Help - /plan manage ? || > §2Manage-Befehl\ Verwalte die MySQL und SQLite Datenbank von Plan.\ §2/plan m §fListe Unterbefehle\ §2/plan m ? §fAusführliche Hilfe In Depth Help - /plan manage backup ? || > §2Backup-Unterbefehl\ Erstellt eine neue SQLite Datenbank (.db file) mit dem Inhalt der aktuell aktiven Datenbank im Plugin-Ordner In Depth Help - /plan manage clear ? || > §2Clear-Unterbefehl\ Entfernt ALLES aus der aktuellen Datenbank. Vorsicht! -In Depth Help - /plan manage con ? || > §2Verbindungs-Debug-Unterbefehl\ Zum debuggen der Verbindungen im Netzwerk.\ Schickt eine Anfrage an jeden Server in der Datenbank. -In Depth Help - /plan manage disable ? || > §2Ausschalten-Subcommand\ Damit können Teile des Plugins bis zum nächsten Neustart ausgeschaltet werden.\ Akzeptierte Argumente:\ §2kickcount §fDeaktiviert den Zähler für Kicks falls /kickall im Ausschalt-Makro genutzt wird. +In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. +In Depth Help - /plan manage export ? || > §2Export Subcommand\ Trigger export to result folders.\ Accepted Arguments:\ §2list §fList possible arguments.\ §2players §fExport /players, /player pages + /player/raw json depending on config values.\ §2server_json §fExport /server/raw JSON if enabled in config. In Depth Help - /plan manage import ? || > §2Import-Unterbefehl\ Importiere Daten aus anderen Quellen.\ Akzeptierte Argumente:\ §2offline §fBukkit Spielerdaten, registriere nur Name und Datum. In Depth Help - /plan manage move ? || > §2Move-Unterbefehl\ Verschiebe die Daten von SQLite zu MySQL oder andersherum\ Zieldatenbank wird vor dem Transfer geleert. +In Depth Help - /plan manage raw ? || > §2Raw Data Subcommand\ Displays link to raw JSON data page.\ Not available if Plan webserver is not enabled. In Depth Help - /plan manage remove ? || > §2Remove-Unterbefehl\ Löscht die Daten eines Spielers aus der Datenbank. In Depth Help - /plan manage restore ? || > §2Restore-Unterbefehl\ Stellt ein Backup einer SQLite Datenbank (.db file) wieder her\ Die database.db kann auch von einem anderen Server zu MySQL wiederhergestellt werden.\ Zieldatenbank wird vor dem Transfer geleert. -In Depth Help - /plan manage setup ? || > §2Setup Unterbefehl\ Richte eine Verbindung zwischen Bungee und diesem Server für Netzwerkfunktionalität ein.\ Die Bungee-Adresse kann dem Log in der Bungee-Konsole entnommen werden, wenn Plan aktiviert wird. +In Depth Help - /plan manage uninstalled ? || > §2Uninstalled Server Subcommand\ Marks a server as uninstalled in the database.\ Can not mark the server the command is being used on as uninstalled.\ Will affect ConnectionSystem. In Depth Help - /plan network ? || > §2Netzwerk-Befehl\ Erstellt einen Link zur Netzwerkseite. Wenn du in einem Netzwerk bist, wird die Serverseite angezeigt. In Depth Help - /plan players ? || > §2Spieler-Befehl\ Erstellt einen Link zur Spielerseite. In Depth Help - /plan qinspect ? || > §2Schnell-Untersuchungs-Befehl\ Zeigt einige Informationen zu einem Spieler im Spiel. In Depth Help - /plan reload ? || > §2Reload-Befehl\ Restartet das plugin mit onDisable and onEnable.\ §bDamit kann NICHT die jar getauscht werden In Depth Help - /plan search ? || > §2Such-Befehl\ Erstellt eine Liste mit Spielernamen, die mit der Suche übereinstimmen.\§7 Beispiel: /plan search 123 - Gibt alle Spieler mit 123 im Namen aus. In Depth Help - /plan servers ? || > §2Server-Command\ Zeigt eine Liste mit allen Servern in der datenbank.\ Kann verwendet werden, um Probleme mit Registrierungen in der Datenbank in einem Netzwerk zu beheben. -In Depth Help - /plan update ? || > §2Update-Befehl\ Updatet das Plugin beim nächsten Restart\ /plan update - Link zum Änderungsprotokoll\ /plan update -u - Plant das Update auf allen Servern im Netzwerk, die online sind, wenn sie das nächste Mal restarten.\ /plan update cancel - Bricht das geplante Update ab, solange der Server noch nicht restartet wurde. In Depth Help - /plan web ? || > §2Account-Verwaltungs-Befehl.\ §2/plan web §fListe Unterbefehle\ §2/plan web ? §fAusfürliche Hilfe In Depth Help - /plan web register ? || > §2Registrierungs-Unterbefehl\ Registriert einen neuen Account.\ Um einen Account für einen anderen Usernamen als den eigenen zu erstellen wird plan.webmanage Berechtigung benötigt.\ Passwörter werden mit PBKDF2 (64,000 iterations of SHA1) gehashed dazu wird ein cryptographically-random salt verwendet. In Depth Help - /planbungee disable ? || > §2Deaktivierungs-Befehl\ Führt onDisable auf PlanBungee aus.\ Plugin kann mit /planbungee reload wieder geladen werden.\ §bDabei kann NICHT die jar ausgetauscht werden. -In Depth Help - /planbungee setup ? || > §2Set-up-Umschalt-Befehl\ Schaltet den Set-Up Modus auf dem Bungee an oder aus.\ Stellt sicher, dass keine unautorisierte MySQL bei einem anderen Server ausschnüffelt. Manage - Confirm Overwrite || Daten in ${0} werden überschrieben! Manage - Confirm Removal || Daten in ${0} werden gelöscht! Manage - Fail || > §cEtwas ist schiefgelaufen: ${0} Manage - Fail File not found || > §cKeine Daten in ${0} gefunden Manage - Fail Incorrect Database || > §c'${0}' ist keine unterstützte Datenbank. +Manage - Fail No Exporter || §eExporter '${0}' doesn't exist Manage - Fail No Importer || §eImporter '${0}' existiert nicht +Manage - Fail No Server || No server found with given parameters. Manage - Fail Same Database || > §cKann nicht von und zu der gleichen Datenbank agieren! +Manage - Fail Same server || Can not mark this server as uninstalled (You are on it) Manage - Fail, Confirmation || > §cFüge '-a' zum Befehl hinzu um die Ausführung zu bestätigen: ${0} -Manage - Fail, Connection Exception || §eFehlgeschlagen: -Manage - Fail, No Servers || §cKein Server in der Datenbank. -Manage - Fail, Old version || §eFehlgeschlagen: Veraltete Version von Plan auf dem empfangenden Server. -Manage - Fail, Unauthorized || §eFehlgeschlagen: Zugriff verweigert. Server verwendet möglicherweise eine andere Datenbank. -Manage - Fail, Unexpected Exception || §eUnerwartete Ausnahme: ${0} -Manage - List Importers || Importer: -Manage - Notify External Url || §eNicht-lokale Adresse. Ist der Port offen? +Manage - List Importers || Importer: Manage - Remind HotSwap || §eDenk dran, zur neuen Datenbank zu wechseln (/plan m hotswap ${0}) und um das Plugin neu zu laden. Manage - Start || > §2Verarbeite Daten... Manage - Success || > §aErfolgreich! Negative || Nein Positive || Ja +Today || 'Heute' +Unavailable || Unavailable Unknown || Unbekannt Version - DEV || Dies ist eine Entwicklungsversion! Version - Latest || Du verwendest die neuste Version @@ -313,24 +357,3 @@ WebServer FAIL - Port Bind || WebServer wurde nicht erfolgr WebServer FAIL - SSL Context || WebServer: SSL Context Initialisierung fehlgeschlagen. WebServer FAIL - Store Load || WebServer: SSL Zertifikat konnte nicht geladen werden. Yesterday || 'Gestern' -Today || 'Heute' -Health - Active Playtime Comparison Decrease || Aktive Spieler haben möglicherweise nichts mehr zu tun (Vergleich der letzten zwei Wochen: ${0} zu den vorletzten zwei Wochen: ${1}. -Health - Active Playtime Comparison Increase || Aktive Spieler haben möglicherweise mehr zu tun (Vergleich der letzten zwei Wochen: ${0} ; zu den vorletzten zwei Wochen: ${1}. -Health - Downtime || Gesamte Serverdowntime (Keine Daten) war ${0} -Health - New Player Join Players, No || Neue Spieler haben möglicherweise keine anderen Spielern, mit denen sie spielen können. (${0} im Durchschnitt) -Health - New Player Join Players, Yes || Neue Spieler haben andere Spieler, mit denen Spielen können. (${0} im Durchschnitt) -Health - New Player Stickiness || ${0} von den neuen Spieler sind geblieben (${1}/${2}) -Health - No Servers Inaccuracy || Es sind keine Bukkit/Sponge-Server verfügbar um Sessiondaten zu sammeln - Diese Messungen sind ungenau. -Health - Player Play on Network || Spieler spielten im Netzwerk. Auf den Servern sieht's wie folgt aus: -Health - Player Register Server || Spieler wurden auf dem Server pro Tag/Server im Durchschnitt registriert. -Health - Player Visit Server || Spieler haben den Server am Tag/Server im Durchschnitt besucht. -Health - Regular Activity Change || Anzahl an regelmässigen Spielern -Health - Regular Activity Change Decrease || verringert um (${0}) -Health - Regular Activity Change Increase || erhöht um (+${0}) -Health - Regular Activity Change Zero || Bleibt gleich (+${0}) -Health - Regular Activity Remain || ${0} von den regelmässigen Spielern sind aktiv geblieben (${1}/${2}) -Health - Single Servers Inaccuracy || Einzelner Bukkit/Sponge um Sessiondaten zu sammeln. -Health - TPS Above Low Threshold || Durchschnittliche TPS war über der unteren Grenze ${0} in der Zeit. -Health - TPS Low Dips || Durchschnittliche TPS war unter der unteren Grenze. (${0}) ${1} male. -HTML - FREE_DISK_SPACE || Freier Festplattenspeicher -HTML - DISK_SPACE || Festplattenspeicher diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt index 58024ecce..a5f1c2556 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_EN.txt @@ -1,6 +1,7 @@ Cmd - Click Me || Click me Cmd - Link || §2Link: §f Cmd Disable - Disabled || §aPlan systems are now disabled. You can still use /planbungee reload to restart the plugin. +Cmd FAIL - Database not open || §cDatabase is ${0} - Please try again a bit later. Cmd FAIL - Invalid Username || §cUser does not have an UUID. Cmd FAIL - No Feature || §eDefine a feature to disable! (currently supports ${0}) Cmd FAIL - No Permission || §cYou do not have the required permission. @@ -35,29 +36,9 @@ Cmd Qinspect - Player Kills || §2Player Kills: §f${0} Cmd Qinspect - Playtime || §2Playtime: §f${0} Cmd Qinspect - Registered || §2Registered: §f${0} Cmd Qinspect - Times Kicked || §2Times Kicked: §f${0} -Cmd Setup - Allowed || §aSet-up is now Allowed -Cmd Setup - Bad Request || §eConnection succeeded, but Receiving server was a Bukkit or Sponge server. Use another address instead. -Cmd Setup - Disallowed || §cSet-up is now Forbidden -Cmd Setup - Forbidden || §eConnection succeeded, but Proxy has set-up mode disabled - use '/planbungee setup' to enable it. -Cmd Setup - Gateway Error || §eConnection succeeded, but Proxy failed to connect to this server (Did current web server restart?). Use /plan m con & /planbungee con to debug. -Cmd Setup - Generic Fail || §eConnection failed: ${0} -Cmd Setup - Internal Error || §eConnection succeeded. ${0}, check possible ErrorLog on receiving server's debug page. -Cmd Setup - Success || §aConnection successful, Plan may restart in a few seconds.. -Cmd Setup - Unauthorized || §eConnection succeeded, but Receiving server didn't authorize this server. Contact Discord for support -Cmd Setup - Url mistake || §cMake sure you're using the full address (Starts with http:// or https://) - Check Proxy enable log for the full address. -Cmd Setup - WebServer not Enabled || §cWebServer is not enabled on this server! Make sure it enables on boot! Cmd SUCCESS - Feature disabled || §aDisabled '${0}' temporarily until next plugin reload. Cmd SUCCESS - WebUser register || §aAdded a new user (${0}) successfully! You can view the web panel in the following link. -Cmd Update - Cancel Success || §aCancel operation performed. -Cmd Update - Cancelled || §cUpdate cancelled. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cUpdate failed on a server, cancelling update on all servers.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cNot all servers were online or accessible, you can still update available servers using /plan update -u -force -Cmd Update - Notify Cancel || §aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel. -Cmd Update - Online Check || Checking that all servers are online.. -Cmd Update - Scheduled || §a${0} scheduled for update. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): +Cmd WARN - Database not open || §eDatabase is ${0} - This might take longer than expected.. Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions Command Help - /plan analyze || View the Server Page Command Help - /plan dev || Development mode command @@ -67,14 +48,15 @@ Command Help - /plan inspect || View a Player Page Command Help - /plan manage || Manage Plan Database Command Help - /plan manage backup || Backup a Database Command Help - /plan manage clear || Clear a Database -Command Help - /plan manage con || Debug Server-Proxy connections Command Help - /plan manage disable || Disable a feature temporarily +Command Help - /plan manage export || Trigger export manually Command Help - /plan manage hotswap || Change Database quickly Command Help - /plan manage import || Import data from elsewhere Command Help - /plan manage move || Move data between Databases +Command Help - /plan manage raw || View raw JSON of player data Command Help - /plan manage remove || Remove Player's data Command Help - /plan manage restore || Restore a previous Backup -Command Help - /plan manage setup || Set-up Server-Proxy connection +Command Help - /plan manage uninstalled || Mark a server as uninstalled in the database. Command Help - /plan network || View the Network Page Command Help - /plan players || View the Players Page Command Help - /plan qinspect || View Player info in game @@ -82,15 +64,12 @@ Command Help - /plan register || Register a Web User Command Help - /plan reload || Restart Plan Command Help - /plan search || Search for a player name Command Help - /plan servers || List servers in Database -Command Help - /plan update || Get change log link or update plugin Command Help - /plan web check || Inspect a Web User Command Help - /plan web delete || Delete a Web User Command Help - /plan web level || Information about permission levels Command Help - /plan web list || List Web Users Command Help - /plan webuser || Manage Web Users -Command Help - /planbungee con || Debug Proxy-Server connections Command Help - /planbungee disable || Disable the plugin temporarily -Command Help - /planbungee setup || Toggle set-up mode Database - Apply Patch || Applying Patch: ${0}.. Database - Patches Applied || All database patches applied successfully. Database - Patches Applied Already || All database patches already applied. @@ -100,10 +79,10 @@ Database Notify - SQLite No WAL || SQLite WAL mode not supported Disable || Player Analytics Disabled. Disable - Processing || Processing critical unprocessed tasks. (${0}) Disable - Processing Complete || Processing complete. +Disable - Unsaved Session Save || Saving unfinished sessions.. Disable - WebServer || Webserver has been disabled. Enable || Player Analytics Enabled. Enable - Database || ${0}-database connection established. -Enable - Notify Address Confirmation || Make sure that this address points to THIS Server: ${0} Enable - Notify Empty IP || IP in server.properties is empty & AlternativeIP is not in use. Incorrect links will be given! Enable - Notify Geolocations disabled || Geolocation gathering is not active. (Data.Geolocations: false) Enable - Notify Geolocations Internet Required || Plan Requires internet access on first run to download GeoLite2 Geolocation database. @@ -112,138 +91,205 @@ Enable - WebServer || Webserver running on PORT ${0 Enable FAIL - Database || ${0}-Database Connection failed: ${1} Enable FAIL - Database Patch || Database Patching failed, plugin has to be disabled. Please report this issue Enable FAIL - GeoDB Write || Something went wrong saving the downloaded GeoLite2 Geolocation database -Enable FAIL - WebServer (Bungee) || WebServer did not initialize! +Enable FAIL - WebServer (Proxy) || WebServer did not initialize! Enable FAIL - Wrong Database Type || ${0} is not a supported Database -HTML - ACTIVITY_INDEX || Activity Index -HTML - ALL || ALL -HTML - ALL_TIME_PEAK || All Time Peak -HTML - AVERAGE_PING || Average Ping -HTML - AVG || AVG -HTML - BANNED || Banned -HTML - BEST_PING || Best Ping -HTML - CALENDAR || CALENDAR -HTML - CALENDAR_TEXT || Calendar -HTML - CHUNKS || Chunks -HTML - COMMAND || Command -HTML - COMMNAND_USAGE || Command Usage -HTML - CONNECTION_INFORMATION || Connection Information -HTML - COUNTRY || Country -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Deaths -HTML - ENTITIES || Entities +HTML - COMPARING_15_DAYS || Comparing 15 days +HTML - COMPARING_60_DAYS || Comparing 30d ago to Now +HTML - COMPARING_7_DAYS || Comparing 7 days +HTML - DATABASE_NOT_OPEN || Database is not open, check db status with /plan info HTML - ERROR || Authentication failed due to error -HTML - FAVORITE_SERVER || Favorite Server -HTML - GEOLOCATION || Geolocation -HTML - GEOLOCATION_TEXT || Geolocation -HTML - HEALTH_ESTIMATE || Health Estimate HTML - INDEX_ACTIVE || Active HTML - INDEX_INACTIVE || Inactive HTML - INDEX_IRREGULAR || Irregular HTML - INDEX_REGULAR || Regular HTML - INDEX_VERY_ACTIVE || Very Active -HTML - IP_ADDRESS || IP-address HTML - KILLED || Killed -HTML - KILLED_BY || Killed by -HTML - LAST_24_HOURS || LAST 24 HOURS -HTML - LAST_30_DAYS || LAST 30 DAYS -HTML - LAST_30_DAYS_TEXT || Last 30 Days -HTML - LAST_7_DAYS || LAST 7 DAYS -HTML - LAST_CONNECTED || Last Connected -HTML - LAST_PEAK || Last Peak -HTML - LAST_SEEN || LAST SEEN -HTML - LAST_SEEN_TEXT || Last Seen -HTML - LOADED_CHUNKS || Loaded Chunks -HTML - LOADED_ENTITIES || Loaded Entities +HTML - LABEL_1ST_WEAPON || Deadliest PvP Weapon +HTML - LABEL_2ND_WEAPON || 2nd PvP Weapon +HTML - LABEL_3RD_WEAPON || 3rd PvP Weapon +HTML - LABEL_ACTIVITY_INDEX || Activity Index +HTML - LABEL_AFK || AFK +HTML - LABEL_AFK_TIME || AFK Time +HTML - LABEL_AVG || Average +HTML - LABEL_AVG_KDR || Average KDR +HTML - LABEL_AVG_MOB_KDR || Average Mob KDR +HTML - LABEL_AVG_PLAYTIME || Average Playtime +HTML - LABEL_AVG_SESSION_LENGTH || Average Session Length +HTML - LABEL_AVG_TPS || Average TPS +HTML - LABEL_BANNED || Banned +HTML - LABEL_BEST_PEAK || All Time Peak +HTML - LABEL_DAY_OF_WEEK || Day of the Week +HTML - LABEL_DEATHS || Deaths +HTML - LABEL_DOWNTIME || Downtime +HTML - LABEL_DURING_LOW_TPS || During Low TPS Spikes: +HTML - LABEL_ENTITIES || Entities +HTML - LABEL_FAVORITE_SERVER || Favorite Server +HTML - LABEL_FIRST_SESSION_LENGTH || First session length +HTML - LABEL_FREE_DISK_SPACE || Free Disk Space +HTML - LABEL_INACTIVE || Inactive +HTML - LABEL_LAST_PEAK || Last Peak +HTML - LABEL_LAST_SEEN || Last Seen +HTML - LABEL_LOADED_CHUNKS || Loaded Chunks +HTML - LABEL_LOADED_ENTITIES || Loaded Entities +HTML - LABEL_LONE_JOINS || Lone joins +HTML - LABEL_LONE_NEW_JOINS || Lone newbie joins +HTML - LABEL_LONGEST_SESSION || Longest Session +HTML - LABEL_LOW_TPS || Low TPS Spikes +HTML - LABEL_MAX_FREE_DISK || Max Free Disk +HTML - LABEL_MIN_FREE_DISK || Min Free Disk +HTML - LABEL_MOB_DEATHS || Mob caused Deaths +HTML - LABEL_MOB_KDR || Mob KDR +HTML - LABEL_MOB_KILLS || Mob Kills +HTML - LABEL_MOST_ACTIVE_GAMEMODE || Most Active Gamemode +HTML - LABEL_NAME || Name +HTML - LABEL_NEW || New +HTML - LABEL_NEW_PLAYERS || New Players +HTML - LABEL_NICKNAME || Nickname +HTML - LABEL_NO_SESSION_KILLS || None +HTML - LABEL_ONLINE_FIRST_JOIN || Players online on first join +HTML - LABEL_OPERATOR || Operator +HTML - LABEL_PER_PLAYER || / Player +HTML - LABEL_PER_REGULAR_PLAYER || / Regular Player +HTML - LABEL_PLAYER_DEATHS || Player caused Deaths +HTML - LABEL_PLAYER_KILLS || Player Kills +HTML - LABEL_PLAYERS_ONLINE || Players Online +HTML - LABEL_PLAYTIME || Playtime +HTML - LABEL_REGISTERED || Registered +HTML - LABEL_REGISTERED_PLAYERS || Registered Players +HTML - LABEL_REGULAR || Regular +HTML - LABEL_REGULAR_PLAYERS || Regular Players +HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity +HTML - LABEL_RETENTION || New Player Retention +HTML - LABEL_SERVER_DOWNTIME || Server Downtime +HTML - LABEL_SERVER_OCCUPIED || Server occupied +HTML - LABEL_SESSION_ENDED || Ended +HTML - LABEL_SESSION_MEDIAN || Session Median +HTML - LABEL_TIMES_KICKED || Times Kicked +HTML - LABEL_TOTAL_PLAYERS || Total Players +HTML - LABEL_TOTAL_PLAYTIME || Total Playtime +HTML - LABEL_UNIQUE_PLAYERS || Unique Players +HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' +HTML - LINK_BACK_NETWORK || Network page +HTML - LINK_BACK_SERVER || Server page +HTML - LINK_CHANGELOG || View Changelog +HTML - LINK_DISCORD || General Support on Discord +HTML - LINK_DOWNLOAD || Download +HTML - LINK_ISSUES || Report Issues +HTML - LINK_NIGHT_MODE || Night Mode +HTML - LINK_PLAYER_PAGE || Player Page +HTML - LINK_QUICK_VIEW || Quick view +HTML - LINK_SERVER_ANALYSIS || Server Analysis +HTML - LINK_WIKI || Plan Wiki, Tutorials & Documentation HTML - LOCAL_MACHINE || Local Machine -HTML - LONGEST || Longest -HTML - LOW_TPS_SPIKES || Low TPS Spikes -HTML - MOB_CAUSED_DEATHS || Mob caused Deaths -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Mob Kills -HTML - MOST_RECENT_SESSIONS || Most Recent Sessions -HTML - NAME || Name -HTML - NAV_COMMAND_USAGE || Command Usage -HTML - NAV_GEOLOCATIONS || Geolocations -HTML - NAV_INFORMATION || Information -HTML - NAV_NETWORK_PLAYERS || Network Players -HTML - NAV_ONLINE_ACTIVITY || Online Activity -HTML - NAV_OVERVIEW || Overview -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Players HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Server Health -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK INFORMATION -HTML - NEW || NEW HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || New Players -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || New -HTML - NICKNAME || Nickname HTML - NO_KILLS || No Kills -HTML - NO_PLAYER_CAUSED_DEATHS || No Player caused Deaths HTML - OFFLINE || Offline HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ONLINE ACTIVITY -HTML - OPERATOR || Operator -HTML - OVERVIEW || OVERVIEW HTML - PER_DAY || / Day -HTML - PLAYER_CAUSED_DEATHS || Player caused Deaths -HTML - PLAYER_KILLS || Player Kills -HTML - PLAYER_LIST || Player List -HTML - PLAYERBASE_DEVELOPMENT || Playerbase Development -HTML - PLAYERS || PLAYERS -HTML - PLAYERS_ONLINE || PLAYERS ONLINE -HTML - PLAYERS_ONLINE_TEXT || Players Online HTML - PLAYERS_TEXT || Players -HTML - PLAYTIME || Playtime -HTML - PLEASE_WAIT || Please wait... -HTML - PREDICETED_RETENTION || Predicted Retention -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || RECENT LOGINS -HTML - REGISTERED || REGISTERED -HTML - REGISTERED_TEXT || Registered -HTML - REGULAR || REGULAR -HTML - SEEN_NICKNAMES || Seen Nicknames -HTML - SERVER || Server -HTML - SERVER_ANALYSIS || Server Analysis -HTML - SERVER_HEALTH_ESTIMATE || Server Health Estimate -HTML - SERVER_INFORMATION || SERVER INFORMATION -HTML - SERVER_PREFERENCE || Server Preference -HTML - SERVERS || Servers HTML - SESSION || Session -HTML - SESSION_ENDED || Session Ended -HTML - SESSION_LENGTH || Session Lenght -HTML - SESSION_MEDIAN || Session Median -HTML - SESSIONS || Sessions -HTML - TIME || Time -HTML - TIMES_KICKED || Times Kicked -HTML - TIMES_USED || Times Used +HTML - SIDE_GEOLOCATIONS || Geolocations +HTML - SIDE_INFORMATION || INFORMATION +HTML - SIDE_NETWORK_OVERVIEW || Network Overview +HTML - SIDE_OVERVIEW || Overview +HTML - SIDE_PERFORMANCE || Performance +HTML - SIDE_PLAYER_LIST || Player List +HTML - SIDE_PLAYERBASE || Playerbase +HTML - SIDE_PLAYERBASE_OVERVIEW || Playerbase Overview +HTML - SIDE_PLUGINS || PLUGINS +HTML - SIDE_PVP_PVE || PvP & PvE +HTML - SIDE_SERVERS || Servers +HTML - SIDE_SERVERS_TITLE || SERVERS +HTML - SIDE_SESSIONS || Sessions +HTML - SIDE_TO_MAIN_PAGE || to main page +HTML - TEXT_CLICK_TO_EXPAND || Click to expand +HTML - TEXT_CONTRIBUTORS_CODE || code contributor +HTML - TEXT_CONTRIBUTORS_LOCALE || translator +HTML - TEXT_CONTRIBUTORS_MONEY || Extra special thanks to those who have monetarily supported the development. +HTML - TEXT_CONTRIBUTORS_THANKS || In addition following awesome people have contributed: +HTML - TEXT_DEV_VERSION || This version is a DEV release. +HTML - TEXT_DEVELOPED_BY || is developed by +HTML - TEXT_FIRST_SESSION || First session +HTML - TEXT_LICENSED_UNDER || is developed and licensed under +HTML - TEXT_METRICS || bStats Metrics +HTML - TEXT_NO_EXTENSION_DATA || No Extension Data +HTML - TEXT_NO_LOW_TPS || No low tps spikes +HTML - TEXT_NO_SERVER || No server to display online activity for +HTML - TEXT_NO_SERVERS || No servers found in the database +HTML - TEXT_PLUGIN_INFORMATION || Information about the plugin +HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players +HTML - TEXT_VERSION || A new version has been released and is now available for download. +HTML - TITLE_30_DAYS || 30 days +HTML - TITLE_30_DAYS_AGO || 30 days ago +HTML - TITLE_ALL || All +HTML - TITLE_ALL_TIME || All Time +HTML - TITLE_AS_NUMBERS || as Numbers +HTML - TITLE_AVG_PING || Average Ping +HTML - TITLE_BEST_PING || Best Ping +HTML - TITLE_CALENDAR || Calendar +HTML - TITLE_CONNECTION_INFO || Connection Information +HTML - TITLE_COUNTRY || Country +HTML - TITLE_CPU_RAM || CPU & RAM +HTML - TITLE_CURRENT_PLAYERBASE || Current Playerbase +HTML - TITLE_DISK || Disk space +HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day +HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Network Online Activity +HTML - TITLE_GRAPH_PUNCHCARD || Punchcard for 30 days +HTML - TITLE_INSIGHTS || Insights for 30 days +HTML - TITLE_IS_AVAILABLE || is Available +HTML - TITLE_LAST_24_HOURS || Last 24 hours +HTML - TITLE_LAST_30_DAYS || Last 30 days +HTML - TITLE_LAST_7_DAYS || Last 7 days +HTML - TITLE_LAST_CONNECTED || Last Connected +HTML - TITLE_LENGTH || Length +HTML - TITLE_MOST_PLAYED_WORLD || Most played World +HTML - TITLE_NETWORK || Network +HTML - TITLE_NETWORK_AS_NUMBERS || Network as Numbers +HTML - TITLE_NOW || Now +HTML - TITLE_ONLINE_ACTIVITY || Online Activity +HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || Online Activity as Numbers +HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || Online Activity Overview +HTML - TITLE_PERFORMANCE_AS_NUMBERS || Performance as Numbers +HTML - TITLE_PING || Ping +HTML - TITLE_PLAYER || Player +HTML - TITLE_PLAYER_OVERVIEW || Player Overview +HTML - TITLE_PLAYERBASE_DEVELOPMENT || Playerbase Development +HTML - TITLE_PVP_DEATHS || Recent PvP Deaths +HTML - TITLE_PVP_KILLS || Recent PvP Kills +HTML - TITLE_PVP_PVE_NUMBERS || PvP & PvE as Numbers +HTML - TITLE_RECENT_KILLS || Recent Kills +HTML - TITLE_RECENT_SESSIONS || Recent Sessions +HTML - TITLE_SEEN_NICKNAMES || Seen Nicknames +HTML - TITLE_SERVER || Server +HTML - TITLE_SERVER_AS_NUMBERS || Server as Numbers +HTML - TITLE_SERVER_OVERVIEW || Server Overview +HTML - TITLE_SERVER_PLAYTIME || Server Playtime +HTML - TITLE_SERVER_PLAYTIME_30 || Server Playtime for 30 days +HTML - TITLE_SESSION_START || Session Started +HTML - TITLE_THEME_SELECT || Theme Select +HTML - TITLE_TITLE_PLAYER_PUNCHCARD || Punchcard +HTML - TITLE_TPS || TPS +HTML - TITLE_TREND || Trend +HTML - TITLE_TRENDS || Trends for 30 days +HTML - TITLE_VERSION || Version +HTML - TITLE_WEEK_COMPARISON || Week Comparison +HTML - TITLE_WORLD || World Load +HTML - TITLE_WORLD_PLAYTIME || World Playtime +HTML - TITLE_WORST_PING || Worst Ping HTML - TOTAL_ACTIVE_TEXT || Total Active HTML - TOTAL_AFK || Total AFK HTML - TOTAL_PLAYERS || Total Players -HTML - TOTAL_PLAYTIME || Total Playtime -HTML - UNIQUE || UNIQUE HTML - UNIQUE_CALENDAR || Unique: -HTML - UNIQUE_PLAYERS || UNIQUE PLAYERS -HTML - UNIQUE_PLAYERS_TEXT || Unique Players -HTML - UNIQUE_TEXT || Unique -HTML - USAGE || Usage -HTML - USED_COMMANDS || Used Commands +HTML - UNIT_CHUNKS || Chunks +HTML - UNIT_ENTITIES || Entities +HTML - UNIT_NO_DATA || No Data +HTML - UNIT_THE_PLAYERS || Players HTML - USER_AND_PASS_NOT_SPECIFIED || User and Password not specified HTML - USER_DOES_NOT_EXIST || User does not exist -HTML - USER_INFORMATION || USER INFORMATION HTML - USER_PASS_MISMATCH || User and Password did not match HTML - WITH ||
    With -HTML - WORLD || World -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || World Playtime -HTML - WORST_PING || Worst Ping HTML ERRORS - ACCESS_DENIED_403 || Access Denied -HTML ERRORS - ANALYSIS_REFRESH || Analysis is being refreshed.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analysis is being run, refresh the page after a few seconds.. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Ensure you have registered a user with /plan register
    - Check that the username and password are correct
    - Username and password are case-sensitive

    If you have forgotten your password, ask a staff member to delete your old user and re-register. HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentication Failed. HTML ERRORS - FORBIDDEN_403 || Forbidden @@ -260,44 +306,42 @@ In Depth Help - /plan inspect ? || > §2Inspect Command\ Refres In Depth Help - /plan manage ? || > §2Manage Command\ Manage MySQL and SQLite database of Plan.\ §2/plan m §fList subcommands\ §2/plan m ? §fIn depth help In Depth Help - /plan manage backup ? || > §2Backup Subcommand\ Creates a new SQLite database (.db file) with contents of currently active database in the Plan plugin folder. In Depth Help - /plan manage clear ? || > §2Clear Subcommand\ Removes everything in the active database. Use with caution. -In Depth Help - /plan manage con ? || > §2Connection Debug Subcommand\ Used to debug connections in the network.\ Sends a request to each server in the database. In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. +In Depth Help - /plan manage export ? || > §2Export Subcommand\ Trigger export to result folders.\ Accepted Arguments:\ §2list §fList possible arguments.\ §2players §fExport /players, /player pages + /player/raw json depending on config values.\ §2server_json §fExport /server/raw JSON if enabled in config. In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2offline §fBukkit player data, only register date and name. In Depth Help - /plan manage move ? || > §2Move Subcommand\ Move data from SQLite to MySQL or other way around.\ Target database is cleared before transfer. +In Depth Help - /plan manage raw ? || > §2Raw Data Subcommand\ Displays link to raw JSON data page.\ Not available if Plan webserver is not enabled. In Depth Help - /plan manage remove ? || > §2Remove Subcommand\ Remove player's data from the active database. In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Setup Subcommand\ Set-up a connection between Proxy and this server for network functionality.\ BungeeAddress can be found in the enable log on console when Plan enables on Bungee. +In Depth Help - /plan manage uninstalled ? || > §2Uninstalled Server Subcommand\ Marks a server as uninstalled in the database.\ Can not mark the server the command is being used on as uninstalled.\ Will affect ConnectionSystem. In Depth Help - /plan network ? || > §2Network Command\ Displays link to the network page.\ If not on a network, this page displays the server page. In Depth Help - /plan players ? || > §2Players Command\ Displays link to the players page. In Depth Help - /plan qinspect ? || > §2Quick Inspect Command\ Displays some information about the player in game. In Depth Help - /plan reload ? || > §2Reload Command\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly In Depth Help - /plan search ? || > §2Search Command\ Get a list of Player names that match the given argument.\§7 Example: /plan search 123 - Finds all users with 123 in their name. In Depth Help - /plan servers ? || > §2Servers Command\ Displays list of Plan servers in the Database.\ Can be used to debug issues with database registration on a network. -In Depth Help - /plan update ? || > §2Update Command\ Used to update the plugin on the next shutdown\ /plan update - Changelog link\ /plan update -u - Schedule update to happen on all network servers that are online, next time they reboot.\ /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet. In Depth Help - /plan web ? || < §2Web User Manage Command.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help In Depth Help - /plan web register ? || > §2Register Subcommand\ Registers a new Web User.\ Registering a user for another player requires plan.webmanage permission.\ Passwords are hashed with PBKDF2 (64,000 iterations of SHA1) using a cryptographically-random salt. In Depth Help - /planbungee disable ? || > §2Disable Command\ Runs Plan onDisable on Proxy servers.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Toggles set-up mode on Proxy.\ Safeguard against unauthorized MySQL snooping with another server. Manage - Confirm Overwrite || Data in ${0} will be overwritten! Manage - Confirm Removal || Data in ${0} will be removed! Manage - Fail || > §cSomething went wrong: ${0} Manage - Fail File not found || > §cNo File found at ${0} Manage - Fail Incorrect Database || > §c'${0}' is not a supported database. +Manage - Fail No Exporter || §eExporter '${0}' doesn't exist Manage - Fail No Importer || §eImporter '${0}' doesn't exist +Manage - Fail No Server || No server found with given parameters. Manage - Fail Same Database || > §cCan not operate on to and from the same database! +Manage - Fail Same server || Can not mark this server as uninstalled (You are on it) Manage - Fail, Confirmation || > §cAdd '-a' argument to confirm execution: ${0} -Manage - Fail, Connection Exception || §eFail reason: -Manage - Fail, No Servers || §cNo Servers found in the database. -Manage - Fail, Old version || §eFail reason: Older Plan version on receiving server -Manage - Fail, Unauthorized || §eFail reason: Unauthorized. Server might be using different database. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eNon-local address, check that port is open +Manage - List Importers || Importers: Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. Manage - Start || > §2Processing data.. Manage - Success || > §aSuccess! Negative || No Positive || Yes +Today || 'Today' +Unavailable || Unavailable Unknown || Unknown Version - DEV || This is a DEV release. Version - Latest || You're using the latest version. @@ -313,24 +357,3 @@ WebServer FAIL - Port Bind || WebServer was not initialized WebServer FAIL - SSL Context || WebServer: SSL Context Initialization Failed. WebServer FAIL - Store Load || WebServer: SSL Certificate loading Failed. Yesterday || 'Yesterday' -Today || 'Today' -Health - Active Playtime Comparison Decrease || Active players might be running out of things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Active Playtime Comparison Increase || Active players seem to have things to do (Played ${0} vs ${1}, last two weeks vs weeks 2-4) -Health - Downtime || Total Server downtime (No Data) was ${0} -Health - New Player Join Players, No || New Players may not have players to play with when they join (${0} on average) -Health - New Player Join Players, Yes || New Players have players to play with when they join (${0} on average) -Health - New Player Stickiness || ${0} of new players have stuck around (${1}/${2}) -Health - No Servers Inaccuracy || No Bukkit/Sponge servers to gather session data - These measures are inaccurate. -Health - Player Play on Network || players played on the network: -Health - Player Register Server || players register on servers per day/server on average. -Health - Player Visit Server || players visit on servers per day/server on average. -Health - Regular Activity Change || Number of regular players has -Health - Regular Activity Change Decrease || decreased (${0}) -Health - Regular Activity Change Increase || increased (+${0}) -Health - Regular Activity Change Zero || stayed the same (+${0}) -Health - Regular Activity Remain || ${0} of regular players have remained active (${1}/${2}) -Health - Single Servers Inaccuracy || Single Bukkit/Sponge server to gather session data. -Health - TPS Above Low Threshold || Average TPS was above Low Threshold ${0} of the time -Health - TPS Low Dips || Average TPS dropped below Low Threshold (${0}) ${1} times -HTML - FREE_DISK_SPACE || Free Disk Space -HTML - DISK_SPACE || DISK SPACE \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_FI.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_FI.txt index 873509163..fec70d15c 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_FI.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_FI.txt @@ -1,12 +1,13 @@ Cmd - Click Me || Klikkaa minua Cmd - Link || §2Linkki: §f Cmd Disable - Disabled || §aPlan on nyt poissa päältä. Voit käyttää /planbungee reload komentoa uudelleenkäynnistykseen. +Cmd FAIL - Database not open || §cTietokanta: ${0} - Yritä uudelleen myöhemmin. Cmd FAIL - Invalid Username || §cPelaajalla ei löytynyt UUID:ta. Cmd FAIL - No Feature || §eValitse sammutettava osa! (tällähetkellä tuetut: ${0}) Cmd FAIL - No Permission || §cSinulla ei ole lupaa. Cmd FAIL - Require only one Argument || §cAnna ainakin yksi muuttuja ${1} Cmd FAIL - Requires Arguments || §cMääritä enemmän muuttujia (${0}) ${1} -Cmd FAIL - Unknown Username || §cPleaajaa ei ole nähty tällä palvelimella. +Cmd FAIL - Unknown Username || §cPelaajaa ei ole nähty tällä palvelimella. Cmd FAIL - WebUser does not exists || §cKäyttäjää ei ole olemassa! Cmd FAIL - WebUser exists || §cKäyttäjä on jo olemassa! Cmd Header - Analysis || > §2Analyysin tulokset @@ -17,7 +18,7 @@ Cmd Header - Players || > §2Pelaajat Cmd Header - Search || > §2${0} Tulosta haulle §f${1}§2: Cmd Header - Servers || > §2Palvelimet Cmd Header - Web Users || > §2${0} Web Käyttäjät -Cmd Info - Bungee Connection || §2Yhdistetty BungeeCord:iin: §f${0} +Cmd Info - Bungee Connection || §2Yhdistetty Proxyyn: §f${0} Cmd Info - Database || §2Aktiivinen Tietokanta: §f${0} Cmd Info - Reload Complete || §aUudelleenlataus onnistui! Cmd Info - Reload Failed || §cUudelleenlatauksessa esiintyi ongelmia. Käynnistystä uudelleen suositellaan. @@ -35,29 +36,9 @@ Cmd Qinspect - Player Kills || §2Pelaajien Tappoja: §f${ Cmd Qinspect - Playtime || §2Peliaika: §f${0} Cmd Qinspect - Registered || §2Rekisteröitynyt: §f${0} Cmd Qinspect - Times Kicked || §2Potkittu Pellolle: §f${0} -Cmd Setup - Allowed || §aYhteydenotto on nyt sallittu. -Cmd Setup - Bad Request || §eYhteys onnistui, mutta Vastaanottava palvelin oli Bukkit tai Sponge-palvelin. Käytä toista osoitetta. -Cmd Setup - Disallowed || §cYhteydenotto on nyt kielletty. -Cmd Setup - Forbidden || §eYhteys onnistui, mutta BungeeCord:n set-up tila oli poissa käytöstä - käytä '/planbungee setup' laittaaksesi sen päälle. -Cmd Setup - Gateway Error || §eYhteys onnistui, mutta BungeeCord ei saanut yhteyttä tähän palvelimeen (Käynnistyikö Web palvelin uudelleen?). Käytä /plan m con & /planbungee con tutkiaksesi ongelmia. -Cmd Setup - Generic Fail || §eYhteys ei onnistunut: ${0} -Cmd Setup - Internal Error || §eYhteys onnistui. ${0}, tarkista mahdollinen virheloki vastaanottavalla palvelimella. -Cmd Setup - Success || §aYhteys onnistui, Plan saattaa käynnistyä uudelleen muutaman sekunnin kuluttua.. -Cmd Setup - Unauthorized || §eYhteys onnistui, mutta Vastaanottava palvelin ei hyväksynyt tätä palvelinta. Pyydä apua Discordissa. -Cmd Setup - Url mistake || §cVarmista käyttäväsi kokonaista osoitetta (http:// tai https:// alkuinen) - Tarkista osoite BungeeCord:in käynnistyslokista. -Cmd Setup - WebServer not Enabled || §cWeb Palevlin ei ole käynnissä tällä palvelimella! Varmista että se käynnistyy pelin mukana! Cmd SUCCESS - Feature disabled || §aSammutettiin '${0}' toistaiseksi, kunnes Plan ladataan uudelleen. Cmd SUCCESS - WebUser register || §aLisättiin uusi Web Käyttäjä (${0})! Voit tarkastella web-paneelia seuraavasta linkistä. -Cmd Update - Cancel Success || §aPeruutettu. -Cmd Update - Cancelled || §cPäivitys peruutettu. -Cmd Update - Change log || Muutosloki v${0}: -Cmd Update - Fail Cacnel || §cPäivitys epäonnistui palvelimella, peruutetaan päivitys kaikilla palvelimilla.. -Cmd Update - Fail Force Notify || §e${0} ei voitu päivittää, -force käytössä, jatketaan päivitystä. -Cmd Update - Fail Not Online || §cKaikki palvelimet eivät olleet saatavilla, voit silti päivittää käyttämällä komentoa /plan update -u -force -Cmd Update - Notify Cancel || §aVoit peruuttaa päivityksen palvelimilla, joita ei ole käynnistetty uudelleen, komennolla /plan update cancel. -Cmd Update - Online Check || Varmistetaan että palvelimet ovat päällä.. -Cmd Update - Scheduled || §a${0} astettu päivittymään. -Cmd Update - Url mismatch || §cVersio URL ei alkanut ${0} joten siihen ei voi ehkä luottaa. Voit ladata sen manuaalisesti täältä (Suora lataus): +Cmd WARN - Database not open || §eTietokanta: ${0} - Tämä voi viedä hiukan aikaa.. Cmd Web - Permission Levels || >\§70: Pääsy kaikille sivuille\§71: Pääsy '/players' ja pelaajien sivuille\§72: Pääsy pelaajan sivulle, jolla on sama nimi kuin Web Käyttäjällä\§73+: Ei pääsyä Command Help - /plan analyze || Katso Palvelimen sivua Command Help - /plan dev || Kehitys komento @@ -67,30 +48,28 @@ Command Help - /plan inspect || Katso Pelaajan sivua Command Help - /plan manage || Hallitse Plan tietokantaa Command Help - /plan manage backup || Varmuuskopioi tietokanta Command Help - /plan manage clear || Tyhjennä tietokanta -Command Help - /plan manage con || Tutki Palvelin-BungeeCord yhteys ongelmia Command Help - /plan manage disable || Sammuta osa toistaiseksi +Command Help - /plan manage export || Vie manuaalisesti Command Help - /plan manage hotswap || Vaihda tietokantaa lennosta Command Help - /plan manage import || Tuo tietoa muualta Command Help - /plan manage move || Siirrä tietoa tietokantojen välillä +Command Help - /plan manage raw || Tarkastele pelaajan tietoja JSON:na Command Help - /plan manage remove || Poista pelaajan tiedot Command Help - /plan manage restore || Palauta teitokannan varmuuskopio -Command Help - /plan manage setup || Muodosta Palvelin-BungeeCord yhteys +Command Help - /plan manage uninstalled || Merkkaa palvelin poistetuksi tietokantaan. Command Help - /plan network || Katso Verkoston sivua Command Help - /plan players || Katso Pelaajat sivua Command Help - /plan qinspect || Katso pelaajan tietoja pelissä Command Help - /plan register || Rekisteröi Web Käyttäjä Command Help - /plan reload || Käynnistä Plan uudelleen Command Help - /plan search || Etsi pelaajan nimeä -Command Help - /plan servers || Listaa tietokannassa olevat palvelimet -Command Help - /plan update || Muutosloki linkki tai Plan:in päivitys +Command Help - /plan servers || Listaa tietokannassa olevat palvelimet Command Help - /plan web check || Tarkista Web Käyttäjän tiedot Command Help - /plan web delete || Poista Web Käyttäjä Command Help - /plan web level || Tietoa Web lupatasoista Command Help - /plan web list || Listaa Web Käyttäjät Command Help - /plan webuser || Halliste Web Käyttäjiä -Command Help - /planbungee con || Tutki BungeeCord-Palvelin yhteys ongelmia Command Help - /planbungee disable || Sammuta Plan toistaiseksi -Command Help - /planbungee setup || Vaihda asennustilaa Database - Apply Patch || Muutetaan Tietokantaa: ${0}.. Database - Patches Applied || Tietokannan muutokset suoritettu onnistuneesti. Database - Patches Applied Already || Kaikki tietokannan muutokset oli jo tehty. @@ -100,10 +79,10 @@ Database Notify - SQLite No WAL || SQLite WAL tilaa ei tueta tä Disable || Player Analytics Sammutettu. Disable - Processing || Suoritetaan kriittisiä suorittamattomia toimintoja. (${0}) Disable - Processing Complete || Suoritus valmis. +Disable - Unsaved Session Save || Tallennetaan päättymättömät istunnot.. Disable - WebServer || Web palvelin on sammutettu. Enable || Player Analytics Käynnistetty. Enable - Database || ${0}-tietokanta yhteys luotu. -Enable - Notify Address Confirmation || Varmista että tämä osoite osoittaa TÄHÄN palvelimeen: ${0} Enable - Notify Empty IP || IP server.properties tiedostossa on tyhjä & AlternativeIP ei ole käytössä. Linkit ovat virheellisiä! Enable - Notify Geolocations disabled || Sijaintien keräys ei ole päällä. (Data.Geolocations: false) Enable - Notify Geolocations Internet Required || Plan Vaatii internetin ensimmäisellä käynnistyskerralla GeoLite2 tietokannan lataamiseen. @@ -112,145 +91,213 @@ Enable - WebServer || Web Palvelin pyörii PORTILLA Enable FAIL - Database || ${0}-Tietokanta yhteys epäonnistui: ${1} Enable FAIL - Database Patch || Tietokannan muutokset epäonnistuivat, Plan jouduttiin sulkemaan. Ilmoita tästä ongelmasta Enable FAIL - GeoDB Write || Jokin meni pieleen tallentaessa GeoLite2 tietokantaa -Enable FAIL - WebServer (Bungee) || Web Palvelin ei käynnistynyt! +Enable FAIL - WebServer (Proxy) || Web Palvelin ei käynnistynyt! Enable FAIL - Wrong Database Type || ${0} ei ole tuettu Tietokanta -HTML - ACTIVITY_INDEX || Aktiivisuus Indeksi -HTML - ALL || KAIKKI -HTML - ALL_TIME_PEAK || Paras Huippu -HTML - AVERAGE_PING || Keskimääräinen Ping -HTML - AVG || AVG -HTML - BANNED || Pannassa -HTML - BEST_PING || Paras Ping -HTML - CALENDAR || KALENTERI -HTML - CALENDAR_TEXT || Kalenteri -HTML - CHUNKS || Chunkit -HTML - COMMAND || Komento -HTML - COMMNAND_USAGE || Komentojen Käyttö -HTML - CONNECTION_INFORMATION || Yhteydet -HTML - COUNTRY || Maa -HTML - CURRENT_PLAYERBASE || Nykyiset pelaajat -HTML - DEATHS || Kuolemat -HTML - ENTITIES || Entiteetit -HTML - ERROR || Autentisointi epäonnistui virheen vuoksi -HTML - FAVORITE_SERVER || Lempipalvelin -HTML - GEOLOCATION || Sijainti -HTML - GEOLOCATION_TEXT || Sijainti -HTML - HEALTH_ESTIMATE || Terveysarvio +HTML - COMPARING_15_DAYS || Verrataan 15 päivää +HTML - COMPARING_60_DAYS || Verrataan 30 päivää sitten nykyhetkeen +HTML - COMPARING_7_DAYS || Verrataan 7 päivää +HTML - DATABASE_NOT_OPEN || Tietokanta ei ole auki, tarkista tietokannan status /plan info komennolla +HTML - ERROR || Todennus epäonnistui virheen vuoksi HTML - INDEX_ACTIVE || Aktiivinen HTML - INDEX_INACTIVE || Inaktiivinen HTML - INDEX_IRREGULAR || Epäsäännöllinen HTML - INDEX_REGULAR || Säännöllinen HTML - INDEX_VERY_ACTIVE || Todella Aktiivinen -HTML - IP_ADDRESS || IP-osoite HTML - KILLED || Tappanut -HTML - KILLED_BY || Tappanut -HTML - LAST_24_HOURS || VIIMEISET 24 TUNTIA -HTML - LAST_30_DAYS || VIIMEISET 30 PÄIVÄÄ -HTML - LAST_30_DAYS_TEXT || Viimeiset 30 Päivää -HTML - LAST_7_DAYS || VIIMEISET 7 PÄIVÄÄ -HTML - LAST_CONNECTED || Viimeisin yhteys -HTML - LAST_PEAK || Viimeisin huippu -HTML - LAST_SEEN || NÄHTY VIIMEKSI -HTML - LAST_SEEN_TEXT || Nähty Viimeksi -HTML - LOADED_CHUNKS || Ladatut Chunkit -HTML - LOADED_ENTITIES || Ladatut Entiteetit +HTML - LABEL_1ST_WEAPON || Tappavin PvP Ase +HTML - LABEL_2ND_WEAPON || 2. PvP Ase +HTML - LABEL_3RD_WEAPON || 3. PvP Ase +HTML - LABEL_ACTIVITY_INDEX || Aktiivisuus Indeksi +HTML - LABEL_AFK || AFK +HTML - LABEL_AFK_TIME || Aika AFK:ina +HTML - LABEL_AVG || Keskiarvoinen +HTML - LABEL_AVG_KDR || Keskiarvoinen Tapposuhde +HTML - LABEL_AVG_MOB_KDR || Keskiarvoinen Otus-Tapposuhde +HTML - LABEL_AVG_PLAYTIME || Keskiarvoinen peliaika +HTML - LABEL_AVG_SESSION_LENGTH || Keskiarvoinen istunnon pituus +HTML - LABEL_AVG_TPS || Keskiarvoinen TPS +HTML - LABEL_BANNED || Pannassa +HTML - LABEL_BEST_PEAK || Paras Huippu +HTML - LABEL_DAY_OF_WEEK || Day of the Week +HTML - LABEL_DEATHS || Kuolemat +HTML - LABEL_DOWNTIME || Poissa päältä +HTML - LABEL_DURING_LOW_TPS || Matalan TPS:n aikana: +HTML - LABEL_ENTITIES || Entiteetit +HTML - LABEL_FAVORITE_SERVER || Lempipalvelin +HTML - LABEL_FIRST_SESSION_LENGTH || Ensimmäisen istunnon pituus +HTML - LABEL_FREE_DISK_SPACE || Vapaa Levytila +HTML - LABEL_INACTIVE || Inaktiivinen +HTML - LABEL_LAST_PEAK || Viimeisin huippu +HTML - LABEL_LAST_SEEN || Nähty Viimeksi +HTML - LABEL_LOADED_CHUNKS || Ladatut Chunkit +HTML - LABEL_LOADED_ENTITIES || Ladatut Entiteetit +HTML - LABEL_LONE_JOINS || Yksinäiset pelaajien liittymiset +HTML - LABEL_LONE_NEW_JOINS || Yksinäiset uusien pelaajien liittymiset +HTML - LABEL_LONGEST_SESSION || Pisin istunto +HTML - LABEL_LOW_TPS || Matalan TPS:n piikit +HTML - LABEL_MAX_FREE_DISK || Max Free Disk +HTML - LABEL_MIN_FREE_DISK || Min Free Disk +HTML - LABEL_MOB_DEATHS || Otusten aiheuttamat Kuolemat +HTML - LABEL_MOB_KDR || Otus-Tapposuhde +HTML - LABEL_MOB_KILLS || Tapetut Otukset +HTML - LABEL_MOST_ACTIVE_GAMEMODE || Aktiivisin pelitila +HTML - LABEL_NAME || Nimi +HTML - LABEL_NEW || Uudet +HTML - LABEL_NEW_PLAYERS || Uusia pelaajia +HTML - LABEL_NICKNAME || Lempinimi +HTML - LABEL_NO_SESSION_KILLS || None +HTML - LABEL_ONLINE_FIRST_JOIN || Paikalla ekalla liittymiskerralla +HTML - LABEL_OPERATOR || Operaattori +HTML - LABEL_PER_PLAYER || / Pelaaja +HTML - LABEL_PER_REGULAR_PLAYER || / Kantapelaaja +HTML - LABEL_PLAYER_DEATHS || Pelaajien aiheuttamat Kuolemat +HTML - LABEL_PLAYER_KILLS || Tapetut Pelaajat +HTML - LABEL_PLAYERS_ONLINE || Pelaajia Paikalla +HTML - LABEL_PLAYTIME || Peliaika +HTML - LABEL_REGISTERED || Rekisteröitynyt +HTML - LABEL_REGISTERED_PLAYERS || Registered Players +HTML - LABEL_REGULAR || Kantapelaaja +HTML - LABEL_REGULAR_PLAYERS || Kantapelaajia +HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity +HTML - LABEL_RETENTION || Uusien pysyvyys +HTML - LABEL_SERVER_DOWNTIME || Palvelin pois päältä +HTML - LABEL_SERVER_OCCUPIED || Palvelin pelaajien käytössä +HTML - LABEL_SESSION_ENDED || Istunto Päättyi +HTML - LABEL_SESSION_MEDIAN || Istuntojen Mediaani +HTML - LABEL_TIMES_KICKED || Heitetty pihalle +HTML - LABEL_TOTAL_PLAYERS || Pelaajia +HTML - LABEL_TOTAL_PLAYTIME || Peliaikaa yhteensä +HTML - LABEL_UNIQUE_PLAYERS || Uniikkeja pelaajia +HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' +HTML - LINK_BACK_NETWORK || Verkosto +HTML - LINK_BACK_SERVER || Palvelin +HTML - LINK_CHANGELOG || Katso muutoslokia +HTML - LINK_DISCORD || Discord tuki +HTML - LINK_DOWNLOAD || Lataa +HTML - LINK_ISSUES || Ilmoita ongelmista +HTML - LINK_NIGHT_MODE || Yö-tila +HTML - LINK_PLAYER_PAGE || Player Page +HTML - LINK_QUICK_VIEW || Pika-katsaus +HTML - LINK_SERVER_ANALYSIS || Palvelimen Analyysi +HTML - LINK_WIKI || Plan Wiki, ohjeet ja dokumentaatio HTML - LOCAL_MACHINE || Paikallinen laite -HTML - LONGEST || Pisin -HTML - LOW_TPS_SPIKES || Matalat TPS Harjat -HTML - MOB_CAUSED_DEATHS || Otusten aiheuttamat Kuolemat -HTML - MOB_KILLS || Tapetut Otukset -HTML - MOST_RECENT_SESSIONS || Viimeisimmät Istunnot -HTML - NAME || Nimi -HTML - NAV_COMMAND_USAGE || Komentojen Käyttö -HTML - NAV_GEOLOCATIONS || Sijainnit -HTML - NAV_INFORMATION || Tiedot -HTML - NAV_NETWORK_PLAYERS || Verkoston Pelaajat -HTML - NAV_ONLINE_ACTIVITY || Aktiivisuus -HTML - NAV_OVERVIEW || Yhteenveto -HTML - NAV_PERFORMANCE || Suorituskyky -HTML - NAV_PLAYERS || Pelaajat HTML - NAV_PLUGINS || Lisäosat -HTML - NAV_SESSIONS || Istunnot -HTML - NAV_SEVER_HEALTH || Palvelimen Terveys -HTML - NETWORK || Verkosto -HTML - NETWORK_INFORMATION || VERKOSTON TIEDOT -HTML - NEW || UUDET HTML - NEW_CALENDAR || Uudet: -HTML - NEW_PLAYERS_TEXT || Uudet Pelaajat -HTML - NEW_RETENTION || Uusien Pelaajien pysyvyys -HTML - NEW_TEXT || Uudet -HTML - NICKNAME || Lempinimi HTML - NO_KILLS || Ei Tappoja -HTML - NO_PLAYER_CAUSED_DEATHS || Ei Pelaajien auheuttamia kuolemia HTML - OFFLINE || Ei Paikalla HTML - ONLINE || Paikalla -HTML - ONLINE_ACTIVITY || AKTIIVISUUS -HTML - OPERATOR || Operaattori -HTML - OVERVIEW || YHTEENVETO HTML - PER_DAY || / Päivä -HTML - PLAYER_CAUSED_DEATHS || Pelaajien aiheuttamat Kuolemat -HTML - PLAYER_KILLS || Tapetut Pelaajat -HTML - PLAYER_LIST || Pelaajalista -HTML - PLAYERBASE_DEVELOPMENT || Pelaajakunnan kehitys -HTML - PLAYERS || PELAAJAT -HTML - PLAYERS_ONLINE || PELAAJIA PAIKALLA -HTML - PLAYERS_ONLINE_TEXT || Pelaajia Paikalla HTML - PLAYERS_TEXT || Pelaajia -HTML - PLAYTIME || Peliaika -HTML - PLEASE_WAIT || Odota Hetki... -HTML - PREDICETED_RETENTION || Pysyvyyden Ennuste -HTML - PUNCH_CARD || Reikäkortti -HTML - PUNCHCARD || REIKÄKORTTI -HTML - RECENT_LOGINS || VIIMEAIKAISET KIRJAUTUMISET -HTML - REGISTERED || REKISTERÖITYNYT -HTML - REGISTERED_TEXT || Rekisteröitynyt -HTML - REGULAR || SÄÄNNÖLLISET -HTML - SEEN_NICKNAMES || Nähdyt Lempinimet -HTML - SERVER || Palvelin -HTML - SERVER_ANALYSIS || Palvelimen Analyysi -HTML - SERVER_HEALTH_ESTIMATE || Palvelimen Terveysarvio -HTML - SERVER_INFORMATION || PALVELIMEN TIEDOT -HTML - SERVER_PREFERENCE || Palvelinten mieleisyys -HTML - SERVERS || Palvelimet HTML - SESSION || Istunto -HTML - SESSION_ENDED || Istunto Päättyi -HTML - SESSION_LENGTH || Istunnon Pituus -HTML - SESSION_MEDIAN || Istuntojen Mediaani -HTML - SESSIONS || Istunnot -HTML - TIME || Aika -HTML - TIMES_KICKED || Potkimis Kerrat -HTML - TIMES_USED || Käyttökerrat -HTML - TOTAL_ACTIVE || Aktiivinen Peliaika +HTML - SIDE_GEOLOCATIONS || Sijainnit +HTML - SIDE_INFORMATION || TIETOJA +HTML - SIDE_NETWORK_OVERVIEW || Verkoston yhteenveto +HTML - SIDE_OVERVIEW || Yhteenveto +HTML - SIDE_PERFORMANCE || Suorituskyky +HTML - SIDE_PLAYER_LIST || Pelaajalista +HTML - SIDE_PLAYERBASE || Pelaajakunta +HTML - SIDE_PLAYERBASE_OVERVIEW || Pelaajakunnan yhteenveto +HTML - SIDE_PLUGINS || LISÄOSAT +HTML - SIDE_PVP_PVE || PvP & PvE +HTML - SIDE_SERVERS || Palvelimet +HTML - SIDE_SERVERS_TITLE || PALVELIMET +HTML - SIDE_SESSIONS || Istunnot +HTML - SIDE_TO_MAIN_PAGE || pääsivu +HTML - TEXT_CLICK_TO_EXPAND || Klikkaa laajentaaksesi +HTML - TEXT_CONTRIBUTORS_CODE || koodin tuottaja +HTML - TEXT_CONTRIBUTORS_LOCALE || kääntäjä +HTML - TEXT_CONTRIBUTORS_MONEY || Suuret kiitokset rahallisesti tukeneille henkilöille. +HTML - TEXT_CONTRIBUTORS_THANKS || Myös seuraavat mahtavat ihmiset ovat tukeneet kehitystä: +HTML - TEXT_DEV_VERSION || Tämä versio on KEHITYS versio. +HTML - TEXT_DEVELOPED_BY || on kehittänyt +HTML - TEXT_FIRST_SESSION || First session +HTML - TEXT_LICENSED_UNDER || kehitetään ja käyttää lisenssiä +HTML - TEXT_METRICS || bStats Metrics +HTML - TEXT_NO_EXTENSION_DATA || Ei dataa lisäosista +HTML - TEXT_NO_LOW_TPS || Ei matalan TPS:n piikkejä +HTML - TEXT_NO_SERVER || No server to display online activity for +HTML - TEXT_NO_SERVERS || No servers found in the database +HTML - TEXT_PLUGIN_INFORMATION || Tietoa lisäosasta +HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players +HTML - TEXT_VERSION || Uusi versio on julkaistu ja on nyt ladattavissa. +HTML - TITLE_30_DAYS || 30 päivää +HTML - TITLE_30_DAYS_AGO || 30 päivää sitten +HTML - TITLE_ALL || Kaikki +HTML - TITLE_ALL_TIME || Kaikkien aikojen +HTML - TITLE_AS_NUMBERS || Numeroina +HTML - TITLE_AVG_PING || Keskimääräinen Ping +HTML - TITLE_BEST_PING || Paras Ping +HTML - TITLE_CALENDAR || Kalenteri +HTML - TITLE_CONNECTION_INFO || Yhteyksien tiedot +HTML - TITLE_COUNTRY || Maa +HTML - TITLE_CPU_RAM || CPU & RAM +HTML - TITLE_CURRENT_PLAYERBASE || Nykyiset pelaajat +HTML - TITLE_DISK || Levytila +HTML - TITLE_GRAPH_DAY_BY_DAY || Päivittäinen katsaus +HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Verkoston paikallaolo +HTML - TITLE_GRAPH_PUNCHCARD || Reikäkortti 30 päivälle +HTML - TITLE_INSIGHTS || Katsauksia 30 päivälle +HTML - TITLE_IS_AVAILABLE || on saatavilla +HTML - TITLE_LAST_24_HOURS || Viimeiset 24 tuntia +HTML - TITLE_LAST_30_DAYS || Viimeiset 30 päivää +HTML - TITLE_LAST_7_DAYS || Viimeiset 7 päivää +HTML - TITLE_LAST_CONNECTED || Viimeisin yhteys +HTML - TITLE_LENGTH || Pituus +HTML - TITLE_MOST_PLAYED_WORLD || Eniten pelattu maailma +HTML - TITLE_NETWORK || Verkosto +HTML - TITLE_NETWORK_AS_NUMBERS || Verkosto Numeroina +HTML - TITLE_NOW || Nyt +HTML - TITLE_ONLINE_ACTIVITY || Paikallaolo +HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || Paikallaolo Numeroina +HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || Yhteenveto Paikallaolosta +HTML - TITLE_PERFORMANCE_AS_NUMBERS || Suorituskyky Numeroina +HTML - TITLE_PING || Ping +HTML - TITLE_PLAYER || Pelaaja +HTML - TITLE_PLAYER_OVERVIEW || Yhteenveto Pelaajasta +HTML - TITLE_PLAYERBASE_DEVELOPMENT || Pelaajakunnan kehitys +HTML - TITLE_PVP_DEATHS || Viimeaikaiset PvP Kuolemat +HTML - TITLE_PVP_KILLS || Viimeaikaiset PvP Tapot +HTML - TITLE_PVP_PVE_NUMBERS || PvP & PvE Numeroina +HTML - TITLE_RECENT_KILLS || Viimeaikaiset Tapot +HTML - TITLE_RECENT_SESSIONS || Viimeisimmät Istunnot +HTML - TITLE_SEEN_NICKNAMES || Nähdyt Lempinimet +HTML - TITLE_SERVER || Palvelin +HTML - TITLE_SERVER_AS_NUMBERS || Palvelin Numeroina +HTML - TITLE_SERVER_OVERVIEW || Yhteenveto Palvelimesta +HTML - TITLE_SERVER_PLAYTIME || Palvelimen peliaika +HTML - TITLE_SERVER_PLAYTIME_30 || Palvelimen peliaika 30 päivälle +HTML - TITLE_SESSION_START || Istunto alkoi +HTML - TITLE_THEME_SELECT || Teemavalikko +HTML - TITLE_TITLE_PLAYER_PUNCHCARD || Reikäkortti +HTML - TITLE_TPS || TPS +HTML - TITLE_TREND || Suunta +HTML - TITLE_TRENDS || Suunnat 30 päivälle +HTML - TITLE_VERSION || Versio +HTML - TITLE_WEEK_COMPARISON || Viikkojen vertaus +HTML - TITLE_WORLD || Maailmojen Resurssit +HTML - TITLE_WORLD_PLAYTIME || Maailmakohtainen Peliaika +HTML - TITLE_WORST_PING || Huonoin Ping +HTML - TOTAL_ACTIVE_TEXT || Aktiivisena HTML - TOTAL_AFK || AFK HTML - TOTAL_PLAYERS || Kaikki Pelaajat -HTML - TOTAL_PLAYTIME || Peliaika -HTML - UNIQUE || UNIIKIT HTML - UNIQUE_CALENDAR || Uniikit: -HTML - UNIQUE_PLAYERS || UNIIKIT PELAAJAT -HTML - UNIQUE_PLAYERS_TEXT || Uniikit Pelaajat -HTML - UNIQUE_TEXT || Uniikit -HTML - USAGE || Käyttö -HTML - USED_COMMANDS || Käytetyt Komennot +HTML - UNIT_CHUNKS || Chunks +HTML - UNIT_ENTITIES || Entities +HTML - UNIT_NO_DATA || Ei tietoa +HTML - UNIT_THE_PLAYERS || Pelaajia HTML - USER_AND_PASS_NOT_SPECIFIED || Käyttäjää ja salasana vaaditaan. HTML - USER_DOES_NOT_EXIST || Käyttäjää ei ole olemassa -HTML - USER_INFORMATION || KÄYTTÄJIEN TIEDOT HTML - USER_PASS_MISMATCH || Käyttäjä ja salasana ei täsmää HTML - WITH ||
    Millä -HTML - WORLD || Maailma -HTML - WORLD_LOAD || MAAILMAN VARAUS -HTML - WORLD_PLAYTIME || Maailman Peliaika -HTML - WORST_PING || Huonoin Ping HTML ERRORS - ACCESS_DENIED_403 || Pääsy Kielletty -HTML ERRORS - ANALYSIS_REFRESH || Analyysiä suoritetaan.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analyysiä suoritetaan, sivu päivittyy hetken kuluttua.. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Varmista että olet rekisteröinyt käyttäjän komennolla /plan register
    - Tarkista että käyttäjänimi ja salaasana ovat oikein
    - Nimi ja salasana ovat CASE SENSITIVE

    Jos unohdit salasanasi, pyydä valvojia poistamaan käyttäjäsi ja uudelleenrekisteröidy. -HTML ERRORS - AUTHENTICATION_FAILED_401 || Autentikaatio ei onnistunut. +HTML ERRORS - AUTHENTICATION_FAILED_401 || Todennus ei onnistunut. HTML ERRORS - FORBIDDEN_403 || Kielletty HTML ERRORS - NO_SERVERS_404 || Ei palvelimia jolla toiminto voitaisiin suorittaa. HTML ERRORS - NOT_FOUND_404 || Ei löytynyt HTML ERRORS - NOT_PLAYED_404 || Pelaaja ei ole pelannut palvelimella. HTML ERRORS - PAGE_NOT_FOUND_404 || Sivua ei ole olemassa. -HTML ERRORS - UNAUTHORIZED_401 || Ei Autentikoitu +HTML ERRORS - UNAUTHORIZED_401 || Todennusta ei suoritettu loppuun. HTML ERRORS - UNKNOWN_PAGE_404 || Varmista menneeesi komennon antamaan osoitteeseen, Esim:

    /player/PelaajanNimi
    /server/PalvelimenNimi

    HTML ERRORS - UUID_404 || Pelaajan UUID:ta ei löytynyt tietokannasta. In Depth Help - /plan ? || > §2Pääkomento\ Käytä alikomentoja ja apua\ §2/plan §fListaa alikomennot\ §2/plan ? §fYksityiskohtainen apu @@ -259,44 +306,42 @@ In Depth Help - /plan inspect ? || > §2Tutkimus Komento\ Päiv In Depth Help - /plan manage ? || > §2Hallinto Komento\ Hallitse Plan MySQL and SQLite tietokantoja.\ §2/plan m §fListaa alikomennot\ §2/plan m ? §fYksityiskohtainen apu In Depth Help - /plan manage backup ? || > §2Varmuuskopio Alikomento\ Luo uuden SQLite tietokannan (.db file) nykyisen tietokannan tiedoilla Plan plugins kansioon.. In Depth Help - /plan manage clear ? || > §2Poisto Alikomento\ Poistaa kaiken aktiivisesta tietokannasta. Käytä varoen -In Depth Help - /plan manage con ? || > §2Yhteyksien tutkimus Alikomento\ Käytetään yhteys ongelmien tutkimiseen verkostoissa.\ Lähettää viestin jokaiselle palvelimelle tietokannassa. -In Depth Help - /plan manage disable ? || > §2Sammutus Alikomento\ Voi poistaa osia käytöstä tilapäisesti.\ Hyväksytyt muuttujat:\ §2kickcount §fSulkee potkimisten laskun, mikäli /kickall komentoa käytetään sammutus makrossa. +In Depth Help - /plan manage disable ? || > §2Sammutus Alikomento\ Sammuttaa osia pluginista.\ Hyväksytyt muuttujat:\ §2kickcount §fLopettaa potkujen laskun jos /kickall käytetään sammutus makrossa. +In Depth Help - /plan manage export ? || > §2Viemis Alikomento\ Vie tuloksia tiedostoihin.\ Hyväksytyt muuttujat:\ §2list §fListaa muuttujat.\ §2players §fVie /players, /player pages + /player/raw json asetuksista riippuen.\ §2server_json §fVie /server sivun json asetuksista riippuen. In Depth Help - /plan manage import ? || > §2Tuonti Alikomento\ Tuo tietoa muualta.\ Hyväksytyt muuttujat:\ §2offline §fBukkitin pelaajatiedot, rekisteröitymispäivä ja nimi. In Depth Help - /plan manage move ? || > §2Siirto Alikomento\ Siirrä tietoa SQLite ja MySQL tietokantojen välillä.\ Kohde tietokanta tyhjennetään ennen siirtoa. +In Depth Help - /plan manage raw ? || > §2Raakadata Komento\ Näyttää linkin JSON sivulle.\ Ei saatavilla jos web palvelin on poissa käytöstä. In Depth Help - /plan manage remove ? || > §2Poisto Alikomento\ Poistaa pelaajan tiedot aktiivisesta tietokannasta. In Depth Help - /plan manage restore ? || > §2Palautus Alikomento\ Palauta edellinen varmuuskopio SQLite tietokanta (.db tiedosto)\ Voit myös palauttaa database.db tiedoston toiselta palvelimelta MySQL-tietokantan.\ Kohde tietokanta tyhjennetään ennen siirtoa. -In Depth Help - /plan manage setup ? || > §2Asennus Alikomento\ Muodosta yhteys BungeeCordin ja tämän palvelimen välille.\ BungeeAddress löytyy BungeeCordin käynnistyslokista +In Depth Help - /plan manage uninstalled ? || > §2Uninstalled Server Subcommand\ Marks a server as uninstalled in the database.\ Can not mark the server the command is being used on as uninstalled.\ Will affect ConnectionSystem. In Depth Help - /plan network ? || > §2Verkosto Komento\ Näyttää linkin verkosto sivulle.\ Jos palvelin ei ole verkostossa, näyttää tämä sivu palvelimen sivun. In Depth Help - /plan players ? || > §2Pelaajat Komento\ Näyttää linkin pelaajan sivulle. In Depth Help - /plan qinspect ? || > §2Pika-tutkimus Komento\ Näyttää tietoja pelaajasta pelin sisällä. In Depth Help - /plan reload ? || > §2Uudelleenlataus Komento\ Käynnistää Plan:in uudelleen.\ §bEi tue jar:in vaihtoa lennosta In Depth Help - /plan search ? || > §2Haku Komento\ Hae pelaajannimiä jollain hakusanalla.\§7 Esim: /plan search 123 - Etsii kaikki pelaajat joilla on 123 nimessään. In Depth Help - /plan servers ? || > §2Palvelimet Komento\ Näyttää tietokannassa olevat Plan palvelimet.\ Voidaan käyttää yhteys ongelmien tutkimiseen. -In Depth Help - /plan update ? || > §2Päivitys Komento\ Päivittää Plan:in seuraavan käynnistyksen yhteydessä\ /plan update - Muutosloki linkki\ /plan update -u - Ajoita päivitys kaikissa päällä olevissa palvelimissa seuraavalle käynnistyskerralle.\ /plan update cancel - Peruuta ajoitettu päivitys palvelimilla jotka eivät ole vielä käynnistyneet uudelleen. In Depth Help - /plan web ? || < §2Web Käyttäjähallinto Komento.\ §2/plan web §fList alikomentos\ §2/plan web ? §fYksityiskohtainen apu In Depth Help - /plan web register ? || > §2Rekisteröinti Alikomento\ Rekisteröi uuden Web Käyttäjän.\ Toiselle pelaajalle käyttäjän rekisteröinti vaatii plan.webmanage permission.\ Salasanat salataan PBKDF2 (64,000 iterations of SHA1) algoritmilla. In Depth Help - /planbungee disable ? || > §2Sammutus Komento\ Sammuttaa PlanBungee:n.\ /planbungee reload käynnistää Plan:in uudelleen.\ §bEi tue jar:in vaihtoa lennossa -In Depth Help - /planbungee setup ? || > §2Asennustilan vaihto Komento\ Vaihtaa asennustilaa Bungee:lla.\ Turvatoimi MySQL haistelulle toisella Plan palvelimella. Manage - Confirm Overwrite || Tiedot ${0}:ssa ylikirjoitetaan! Manage - Confirm Removal || Tiedot ${0}:ssa poistetaan! Manage - Fail || > §cJokin meni vikaan: ${0} Manage - Fail File not found || > §cTiedostoa ei löytynyt ${0} Manage - Fail Incorrect Database || > §c'${0}' ei ole tuettu tietokanta. +Manage - Fail No Exporter || §eViejää '${0}' ei ole olemassa Manage - Fail No Importer || §eTuojaa '${0}' ei ole olemassa +Manage - Fail No Server || Palvelinta ei löytynyt annetuilla parametreilla. Manage - Fail Same Database || > §cEi voi käyttää samaa tietokantaa molempiin asioihin! +Manage - Fail Same server || Ei voi merkitä tätä palvelinta poistetuksi (Olet siellä) Manage - Fail, Confirmation || > §cLisää '-a' vahvistaaksesi: ${0} -Manage - Fail, Connection Exception || §eEpäonnistumisen syy: -Manage - Fail, No Servers || §cEi Palvelimia tietokannassa. -Manage - Fail, Old version || §eSyy: Vanhempi versio vastaanottavalla palvelimella -Manage - Fail, Unauthorized || §eSyy: Ei authentikoitu, palvelin voi käyttää toista tietokantaa -Manage - Fail, Unexpected Exception || §eKummallinen Virhe: ${0} -Manage - List Importers || Tuojat: -Manage - Notify External Url || §eEi-lokaali osoite, tarkista että portti on auki +Manage - List Importers || Tuojat: Manage - Remind HotSwap || §eMuista vaihtaa tietokantaa (/plan m hotswap ${0}) & käynnistä Plan uudelleen. Manage - Start || > §2Prosessoidaan tietoa.. Manage - Success || > §aOnnistui! Negative || Ei Positive || Kyllä +Today || 'Tänään' +Unavailable || Unavailable Unknown || Tuntematon Version - DEV || Tämä on KEHITYS julkaisu. Version - Latest || Käytät uusinta versiota. @@ -312,24 +357,3 @@ WebServer FAIL - Port Bind || Web Palvelin ei käynnistynyt WebServer FAIL - SSL Context || Web Palvelin: SSL Kontekstin käynnistys ei onnistunut. WebServer FAIL - Store Load || Web Palvelin: SSL Sertifikaatin lataus ei onnistunut. Yesterday || 'Eilen' -Today || 'Tänään' -Health - Active Playtime Comparison Decrease || Aktiivisilta pelaajilta voi olla loppumassa tekeminen (Pelasivat ${0} vs ${1}, viimeisen kahden viikon vs viikot 2-4) -Health - Active Playtime Comparison Increase || Aktiivisilta pelaajilta vaikuttaa olevan tekemistä (Pelasivat ${0} vs ${1}, viimeisen kahden viikon vs viikot 2-4) -Health - Downtime || Palvelimen downtime (Ei Dataa) oli ${0} -Health - New Player Join Players, No || Uusilla pelaajilla voi olla yksinäistä (${0} paikalla keskimäärin) -Health - New Player Join Players, Yes || Uusilla pelaajilla on kavereita liittyessä (${0} paikalla keskimäärin) -Health - New Player Stickiness || ${0} uusista pelaajista jäi pelaamaan (${1}/${2}) -Health - No Servers Inaccuracy || Ei Bukkit/Sponge palvelimia sessio tietojen keräykseen - Nämä arviot ovat epätarkkoja. -Health - Player Play on Network || pelaajaa pelasi verkossa: -Health - Player Register Server || pelaajaa rekisteröityi palvelimille per päivä/palvelin keskimäärin. -Health - Player Visit Server || pelaajaa käy palvelimilla per päivä/palvelin keskimäärin. -Health - Regular Activity Change || Kestopelaajien määrä on -Health - Regular Activity Change Decrease || pienentynyt (${0}) -Health - Regular Activity Change Increase || kasvanut (+${0}) -Health - Regular Activity Change Zero || pysynyt samana (+${0}) -Health - Regular Activity Remain || ${0} kestopelaajista on pysynyt aktiivisena (${1}/${2}) -Health - Single Servers Inaccuracy || Yksi Bukkit/Sponge palvelin sessio tietojen keräykseen. -Health - TPS Above Low Threshold || Keskimääräinen TPS oli alarajan yläpuolella ${0} ajasta -Health - TPS Low Dips || Keskimääräinen TPS putosi alarajan alapuolelle (${0}) ${1} kertaa -HTML - FREE_DISK_SPACE || Vapaa Levytila -HTML - DISK_SPACE || LEVYTILA \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_FR.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_FR.txt index 999c093fb..effb0574a 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_FR.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_FR.txt @@ -1,6 +1,7 @@ Cmd - Click Me || Cliquez ici Cmd - Link || §2Lien : §f Cmd Disable - Disabled || §aLes systèmes de Plan sont maintenant désactivés. Vous pouvez toujours exécuter la commande '/planbungee reload' pour les redémarrer. +Cmd FAIL - Database not open || §cLa base de données est : ${0} - Merci de réessayer plus tard. Cmd FAIL - Invalid Username || §cCet utilisateur ne possède pas d'UUID. Cmd FAIL - No Feature || §eDéfinir une fonctionnalité à désactiver ! (supporte actuellement ${0}) Cmd FAIL - No Permission || §cVous ne possédez pas la permission requise. @@ -35,62 +36,40 @@ Cmd Qinspect - Player Kills || §2Kills de Joueurs : §f${ Cmd Qinspect - Playtime || §2Temps de jeu : §f${0} Cmd Qinspect - Registered || §2Incrit depuis : §f${0} Cmd Qinspect - Times Kicked || §2Nombre d'éjections : §f${0} -Cmd Setup - Allowed || §aLa configuration est maintenant autorisée. -Cmd Setup - Bad Request || §eLa connexion a réussi, mais le serveur de réception n'était un serveur Bukkit ou Sponge. Utilisez une autre adresse à la place. -Cmd Setup - Disallowed || §cLa configuration est maintenant interdite. -Cmd Setup - Forbidden || §eConnexion réussie, mais le mode de configuration de Bungee est désactivé. Exécutez '/plan bungee setup' pour l'activer. -Cmd Setup - Gateway Error || §eConnexion réussie, mais Bungee n'a pas réussi à se connecter à ce serveur (Le serveur Web actuel a-t-il redémarré ?). Exécutez '/plan m con' et '/planbungee con' pour déboguer. -Cmd Setup - Generic Fail || §eConnexion échouée : ${0} -Cmd Setup - Internal Error || §eConnexion échouée : ${0}, Vérifiez des possibles erreurs sur la page de débogage du serveur de réception. -Cmd Setup - Success || §aConnexion réussie, Plan peut redémarrer dans quelques secondes. -Cmd Setup - Unauthorized || §eConnexion réussie, mais le serveur de réception n'a pas autorisé ce serveur. Contactez-nous sur Discord pour obtenir de l'aide. -Cmd Setup - Url mistake || §cAssurez-vous d'utiliser l'adresse complète (commençant par http:// ou https://). Consultez les logs du Bungee pour la connaître. -Cmd Setup - WebServer not Enabled || §cLe serveur Web n'est pas activé sur ce serveur ! Assurez-vous qu'il se soit démarré lors du lancement du serveur ! Cmd SUCCESS - Feature disabled || §aFontionnalité '${0}' temporairement désactivée jusqu'au prochain rechargement du plugin. Cmd SUCCESS - WebUser register || §aAjout d'un nouvel utilisateur (${0}) avec succès ! Vous pouvez afficher le panneau Web dans le lien suivant. -Cmd Update - Cancel Success || §aAnnulation de l'opération... -Cmd Update - Cancelled || §cMise à jour annulée. -Cmd Update - Change log || Changements v${0}: -Cmd Update - Fail Cacnel || §cLa mise à jour a échoué sur un serveur, annulant la mise à jour sur tous les serveurs. -Cmd Update - Fail Force Notify || §e${0} échec de la mise à jour : force spécifiée, continuation. -Cmd Update - Fail Not Online || §cTous les serveurs n'étant pas en ligne ou accessibles, vous pouvez toujours mettre à jour les serveurs disponibles en exécutant '/plan update -u -force'. -Cmd Update - Notify Cancel || §aVous pouvez annuler la mise à jour sur des serveurs n'ayant pas encore redémarré en exécutant '/plan update cancel'. -Cmd Update - Online Check || Vérification que tous les serveurs soient en ligne... -Cmd Update - Scheduled || §a${0} prévu pour la mise à jour. -Cmd Update - Url mismatch || §cL'URL de téléchargement de la version ne commence pas par ${0} et n'est peut-être pas fiable. Vous pouvez télécharger cette version manuellement ici (téléchargement direct) : +Cmd WARN - Database not open || §eLa base de données est : ${0} - Cela pourrait prendre plus de temps que prévu... Cmd Web - Permission Levels || >\§70: Accéder à toutes les pages.\§71: Accéder au '/players' et à toutes les pages des joueurs.\§72: Accéder à la page du joueur avec le même nom d'utilisateur que l'utilisateur Web.\§73+: Pas de permission. Command Help - /plan analyze || Voir la page du serveur. Command Help - /plan dev || Commande du mode de développement. -Command Help - /plan help || Afficher la liste des commandes. -Command Help - /plan info || Vérifier la version de Plan. -Command Help - /plan inspect || Visionner la page d'un joueur. +Command Help - /plan help || Visualiser la liste des commandes. +Command Help - /plan info || Visualiser la version de Plan. +Command Help - /plan inspect || Visualiser la page d'un joueur. Command Help - /plan manage || Gérer la base de données de Plan. Command Help - /plan manage backup || Sauvegarder une base de données. Command Help - /plan manage clear || Effacer une base de données. -Command Help - /plan manage con || Déboguer la connexion Serveur -> Bungee. Command Help - /plan manage disable || Désactiver temporairement une fonctionnalité. +Command Help - /plan manage export || Déclencher l'exportation manuelle. Command Help - /plan manage hotswap || Changer rapidement de base de données. Command Help - /plan manage import || Importer des données externes. Command Help - /plan manage move || Déplacer des données entre des bases de données. +Command Help - /plan manage raw || Visualiser le JSON brut des données du joueur. Command Help - /plan manage remove || Supprimer les données d'un joueur. Command Help - /plan manage restore || Restaurer une sauvegarde précédente. -Command Help - /plan manage setup || Configuration de la connexion Serveur -> Bungee. -Command Help - /plan network || Visionner la page du réseau. -Command Help - /plan players || Visionner la page des joueurs. -Command Help - /plan qinspect || Visionner les informations d'un joueur (en jeu). +Command Help - /plan manage uninstalled || Mark a server as uninstalled in the database. +Command Help - /plan network || Visualiser la page du réseau. +Command Help - /plan players || Visualiser la page des joueurs. +Command Help - /plan qinspect || Visualiser les informations d'un joueur (en jeu). Command Help - /plan register || Enregistrer un utilisateur Web. Command Help - /plan reload || Recharger Plan. Command Help - /plan search || Rechercher un joueur. Command Help - /plan servers || Obtenir la liste des serveurs dans la base de données. -Command Help - /plan update || Obtenir le lien des modifications ou du plugin mis à jour. Command Help - /plan web check || Inspecter un utilisateur Web. Command Help - /plan web delete || Supprimer un utilisateur Web. -Command Help - /plan web level || Informations sur les niveaux de permission. +Command Help - /plan web level || Visualiser les informations sur les niveaux de permission. Command Help - /plan web list || Liste des utilisateurs Web. Command Help - /plan webuser || Gérer les utilisateurs Web. -Command Help - /planbungee con || Déboguer la connexion Bungee -> Serveur. Command Help - /planbungee disable || Désactiver temporairement Plan. -Command Help - /planbungee setup || Basculer le mode de configuration (on/off). Database - Apply Patch || Application de correctifs : ${0}... Database - Patches Applied || Tous les correctifs pour la base de données ont été appliqués avec succès. Database - Patches Applied Already || Tous les correctifs pour la base de données ont déjà été appliqués. @@ -98,152 +77,219 @@ Database MySQL - Launch Options Error || Configurations défectueuses, Database Notify - Clean || Suppression des données de ${0} joueurs. Database Notify - SQLite No WAL || Le mode WAL de SQLite n'est pas pris en charge sur cette version du serveur, en utilisant le mode par défaut. Cela peut possiblement affecter les performances. Disable || Plan a été désactivé. -Disable - Processing || Traitement des tâches critiques non traitées. (${0}) +Disable - Processing || Traitement des tâches critiques inachevées... (${0}) Disable - Processing Complete || Traitement complété. +Disable - Unsaved Session Save || Sauvegarde des sessions inachevées... Disable - WebServer || Le serveur Web a été désactivé. Enable || Plan a été activé. Enable - Database || Connexion à la base de données établie. (${0}) -Enable - Notify Address Confirmation || Assurez-vous que cette adresse pointe vers ce serveur : ${0}. Enable - Notify Empty IP || L'adresse IP située dans le fichier 'server.properties' est vide et l'option 'AlternativeIP' n'est pas utilisée. Attention, des liens incorrects seront donnés ! -Enable - Notify Geolocations disabled || La géolocalisation n'est pas active. (Data.Geolocations: false) +Enable - Notify Geolocations disabled || La Géolocalisation n'est pas active. (Data.Geolocations: false) Enable - Notify Geolocations Internet Required || Plan nécessite un accès à Internet lors de sa première utilisation pour télécharger la base de données 'GeoLite2 Geolocation'. Enable - Notify Webserver disabled || Le serveur Web n'a pas été initialisé. (WebServer.DisableWebServer: true) Enable - WebServer || Le serveur Web communique à travers le port ${0} (${1}). Enable FAIL - Database || La connexion à la base de données a échoué. (${0}) : ${1} Enable FAIL - Database Patch || L'application de correctifs pour la base de données a échoué, le plugin doit être désactivé. Merci de signaler ce problème. Enable FAIL - GeoDB Write || Une erreur s'est produite lors de l'enregistrement de la base de données 'GeoLite2 Geolocation' téléchargée précédemment. -Enable FAIL - WebServer (Bungee) || Le serveur Web n'a pas été initialisé ! +Enable FAIL - WebServer (Proxy) || Le serveur Web n'a pas été initialisé ! Enable FAIL - Wrong Database Type || ${0} n'est pas une base de données prise en charge. -HTML - ACTIVITY_INDEX || Indice d'activité -HTML - ALL || TOUT -HTML - ALL_TIME_PEAK || Pic maximal de joueurs connectés -HTML - AVERAGE_PING || Latence moyenne -HTML - AVG || MOYEN.NE -HTML - BANNED || Banni -HTML - BEST_PING || Meilleure latence -HTML - CALENDAR || CALENDRIER -HTML - CALENDAR_TEXT || Calendrier -HTML - CHUNKS || Tronçons -HTML - COMMAND || Commande -HTML - COMMNAND_USAGE || Utilisation des commandes -HTML - CONNECTION_INFORMATION || Informations de connexion -HTML - COUNTRY || Pays -HTML - CURRENT_PLAYERBASE || Base de joueurs actuelle -HTML - DEATHS || Morts -HTML - ENTITIES || Entités +HTML - COMPARING_15_DAYS || Comparaison des 15 derniers jours +HTML - COMPARING_60_DAYS || Comparaison des 60 derniers jours +HTML - COMPARING_7_DAYS || Comparaison des 7 derniers jours +HTML - DATABASE_NOT_OPEN || La base de données n'est pas ouverte, vérifiez son état avec la commande /plan info HTML - ERROR || Authentification échouée à cause d'une erreur -HTML - FAVORITE_SERVER || Serveur favori -HTML - GEOLOCATION || Géolocalisation -HTML - GEOLOCATION_TEXT || Géolocalisation -HTML - HEALTH_ESTIMATE || Estimation de la santé du serveur HTML - INDEX_ACTIVE || Actif HTML - INDEX_INACTIVE || Inactif HTML - INDEX_IRREGULAR || Irrégulier HTML - INDEX_REGULAR || Régulier HTML - INDEX_VERY_ACTIVE || Très actif -HTML - IP_ADDRESS || Adresse IP HTML - KILLED || Tué -HTML - KILLED_BY || Tué par -HTML - LAST_24_HOURS || 24 DERNIÈRES HEURES -HTML - LAST_30_DAYS || 30 DERNIERS JOURS -HTML - LAST_30_DAYS_TEXT || 30 derniers jours -HTML - LAST_7_DAYS || 7 DERNIERS JOURS -HTML - LAST_CONNECTED || Dernier connecté -HTML - LAST_PEAK || Dernier pic de joueurs connectés -HTML - LAST_SEEN || DERNIÈRE CONNEXION -HTML - LAST_SEEN_TEXT || Dernière connexion -HTML - LOADED_CHUNKS || Tronçons chargés -HTML - LOADED_ENTITIES || Entités chargés +HTML - LABEL_1ST_WEAPON || 1ère Arme de combat (la plus mortelle) +HTML - LABEL_2ND_WEAPON || 2ème Arme de combat +HTML - LABEL_3RD_WEAPON || 3ème Arbe de combat +HTML - LABEL_ACTIVITY_INDEX || Indice d'activité +HTML - LABEL_AFK || AFK +HTML - LABEL_AFK_TIME || Temps AFK +HTML - LABEL_AVG || Moyen(ne) +HTML - LABEL_AVG_KDR || Ratio Kills / Morts moyen +HTML - LABEL_AVG_MOB_KDR || Ratio Kills / Morts de Mobs moyen +HTML - LABEL_AVG_PLAYTIME || Temps de jeu moyen +HTML - LABEL_AVG_SESSION_LENGTH || Durée moyenne d'une session +HTML - LABEL_AVG_TPS || TPS moyen +HTML - LABEL_BANNED || Banni(e) +HTML - LABEL_BEST_PEAK || Pic maximal de joueurs connectés +HTML - LABEL_DAY_OF_WEEK || Day of the Week +HTML - LABEL_DEATHS || Morts +HTML - LABEL_DOWNTIME || Temps hors-ligne +HTML - LABEL_DURING_LOW_TPS || Pendant les pics de TPS bas : +HTML - LABEL_ENTITIES || Entités +HTML - LABEL_FAVORITE_SERVER || Serveur favori +HTML - LABEL_FIRST_SESSION_LENGTH || Durée de la première session +HTML - LABEL_FREE_DISK_SPACE || Espace disque disponible +HTML - LABEL_INACTIVE || Inactif(ve) +HTML - LABEL_LAST_PEAK || Dernier pic de joueurs connectés +HTML - LABEL_LAST_SEEN || Dernière connexion +HTML - LABEL_LOADED_CHUNKS || Tronçons chargés +HTML - LABEL_LOADED_ENTITIES || Entités chargés +HTML - LABEL_LONE_JOINS || Connexions de joueurs seuls +HTML - LABEL_LONE_NEW_JOINS || Connexions de débutants seuls +HTML - LABEL_LONGEST_SESSION || Session la plus longue +HTML - LABEL_LOW_TPS || Pics de TPS bas +HTML - LABEL_MAX_FREE_DISK || Max Free Disk +HTML - LABEL_MIN_FREE_DISK || Min Free Disk +HTML - LABEL_MOB_DEATHS || Morts causées par un Mob +HTML - LABEL_MOB_KDR || Ratio Kills / Morts de Mobs +HTML - LABEL_MOB_KILLS || Kills de Mobs +HTML - LABEL_MOST_ACTIVE_GAMEMODE || Mode de jeu le plus utilisé +HTML - LABEL_NAME || Name +HTML - LABEL_NEW || Nouveau(elle) +HTML - LABEL_NEW_PLAYERS || Nouveaux joueurs +HTML - LABEL_NICKNAME || Surnom +HTML - LABEL_NO_SESSION_KILLS || None +HTML - LABEL_ONLINE_FIRST_JOIN || Joueurs en ligne lors de la première connexion +HTML - LABEL_OPERATOR || Opérateur +HTML - LABEL_PER_PLAYER || / Joueur +HTML - LABEL_PER_REGULAR_PLAYER || / Joueur régulier +HTML - LABEL_PLAYER_DEATHS || Décès causés par le joueur +HTML - LABEL_PLAYER_KILLS || Kills de joueurs +HTML - LABEL_PLAYERS_ONLINE || Joueurs en ligne +HTML - LABEL_PLAYTIME || Temps de jeu +HTML - LABEL_REGISTERED || Inscription +HTML - LABEL_REGISTERED_PLAYERS || Joueurs enregistrés +HTML - LABEL_REGULAR || Régulier(ère) +HTML - LABEL_REGULAR_PLAYERS || Joueurs réguliers +HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity +HTML - LABEL_RETENTION || Rétention des nouveaux joueurs +HTML - LABEL_SERVER_DOWNTIME || Temps hors-ligne du serveur +HTML - LABEL_SERVER_OCCUPIED || Serveur inoccupé +HTML - LABEL_SESSION_ENDED || Session terminée +HTML - LABEL_SESSION_MEDIAN || Session médiane +HTML - LABEL_TIMES_KICKED || Nombre d'éjections +HTML - LABEL_TOTAL_PLAYERS || Joueurs totaux +HTML - LABEL_TOTAL_PLAYTIME || Temps de jeu total +HTML - LABEL_UNIQUE_PLAYERS || Joueurs uniques +HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' +HTML - LINK_BACK_NETWORK || Page du réseau +HTML - LINK_BACK_SERVER || Page du serveur +HTML - LINK_CHANGELOG || Visualiser les changements +HTML - LINK_DISCORD || Support général sur Discord +HTML - LINK_DOWNLOAD || Téléchargement +HTML - LINK_ISSUES || Rapport de bogues +HTML - LINK_NIGHT_MODE || Mode Nuit +HTML - LINK_PLAYER_PAGE || Player Page +HTML - LINK_QUICK_VIEW || Aperçu rapide +HTML - LINK_SERVER_ANALYSIS || Analyse du serveur +HTML - LINK_WIKI || Wiki, Documentation & Tutoriaux de Plan HTML - LOCAL_MACHINE || Machine locale -HTML - LONGEST || Plus long.ue -HTML - LOW_TPS_SPIKES || Pointes basses des TPS -HTML - MOB_CAUSED_DEATHS || Morts causées par un Mob -HTML - MOB_KDR || Mob KDR -HTML - MOB_KILLS || Kills de Mobs -HTML - MOST_RECENT_SESSIONS || Sessions récentes -HTML - NAME || Nom -HTML - NAV_COMMAND_USAGE || Utilisation des commandes -HTML - NAV_GEOLOCATIONS || Géolocalisation -HTML - NAV_INFORMATION || Informations -HTML - NAV_NETWORK_PLAYERS || Joueurs du réseau -HTML - NAV_ONLINE_ACTIVITY || Activité en ligne -HTML - NAV_OVERVIEW || Vue d'ensemble -HTML - NAV_PERFORMANCE || Performance -HTML - NAV_PLAYERS || Joueurs HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessions -HTML - NAV_SEVER_HEALTH || Santé du serveur -HTML - NETWORK || Réseau -HTML - NETWORK_INFORMATION || INFORMATIONS SUR LE RÉSEAU -HTML - NEW || NOUVEAU(X) HTML - NEW_CALENDAR || Nouveau : -HTML - NEW_PLAYERS_TEXT || Nouveaux joueurs -HTML - NEW_RETENTION || Rétention des nouveaux joueurs -HTML - NEW_TEXT || Nouveau -HTML - NICKNAME || Surnom HTML - NO_KILLS || Pas de Kills -HTML - NO_PLAYER_CAUSED_DEATHS || Aucun joueur n'a causé de mort HTML - OFFLINE || Hors-ligne HTML - ONLINE || En ligne -HTML - ONLINE_ACTIVITY || ACTIVITÉ EN LIGNE -HTML - OPERATOR || Opérateur -HTML - OVERVIEW || Vue d'ensemble HTML - PER_DAY || / Jour -HTML - PLAYER_CAUSED_DEATHS || Décès causés par le joueur -HTML - PLAYER_KILLS || Kills de joueurs -HTML - PLAYER_LIST || Liste des joueurs -HTML - PLAYERBASE_DEVELOPMENT || Évolution de la base de joueurs -HTML - PLAYERS || JOUEURS -HTML - PLAYERS_ONLINE || JOUEURS EN LIGNE -HTML - PLAYERS_ONLINE_TEXT || Joueurs en ligne HTML - PLAYERS_TEXT || Joueurs -HTML - PLAYTIME || Temps de jeu -HTML - PLEASE_WAIT || Merci de patienter... -HTML - PREDICETED_RETENTION || Rétention prévue -HTML - PUNCH_CARD || Carte perforée -HTML - PUNCHCARD || CARTE PERFORÉE -HTML - RECENT_LOGINS || CONNEXIONS RÉCENTES -HTML - REGISTERED || INSCRIPTION -HTML - REGISTERED_TEXT || Inscription -HTML - REGULAR || RÉGULIER(S) -HTML - SEEN_NICKNAMES || Surnoms Vus -HTML - SERVER || Serveur -HTML - SERVER_ANALYSIS || Analyse du serveur -HTML - SERVER_HEALTH_ESTIMATE || Estimation de la santé du serveur -HTML - SERVER_INFORMATION || INFORMATIONS SUR LE SERVEUR -HTML - SERVER_PREFERENCE || Préférence de serveur -HTML - SERVERS || Serveurs HTML - SESSION || Session -HTML - SESSION_ENDED || Session terminée -HTML - SESSION_LENGTH || Durée de la session -HTML - SESSION_MEDIAN || Session médiane -HTML - SESSIONS || Sessions -HTML - TIME || Temps -HTML - TIMES_KICKED || Nombre d'éjections -HTML - TIMES_USED || Nombre d'utilisations +HTML - SIDE_GEOLOCATIONS || Géolocalisation +HTML - SIDE_INFORMATION || INFORMATION +HTML - SIDE_NETWORK_OVERVIEW || Aperçu du réseau +HTML - SIDE_OVERVIEW || Vue d'ensemble +HTML - SIDE_PERFORMANCE || Performance +HTML - SIDE_PLAYER_LIST || Liste des joueurs +HTML - SIDE_PLAYERBASE || Base de joueurs +HTML - SIDE_PLAYERBASE_OVERVIEW || Aperçu de la base de joueurs +HTML - SIDE_PLUGINS || PLUGINS +HTML - SIDE_PVP_PVE || PvP & PvE +HTML - SIDE_SERVERS || Serveurs +HTML - SIDE_SERVERS_TITLE || SERVEURS +HTML - SIDE_SESSIONS || Sessions +HTML - SIDE_TO_MAIN_PAGE || Retour à la page principale +HTML - TEXT_CLICK_TO_EXPAND || Cliquez pour agrandir +HTML - TEXT_CONTRIBUTORS_CODE || Contributeurs au code +HTML - TEXT_CONTRIBUTORS_LOCALE || Traducteurs +HTML - TEXT_CONTRIBUTORS_MONEY || Un merci spécial à ceux qui ont financièrement soutenu le développement. +HTML - TEXT_CONTRIBUTORS_THANKS || En outre, ces gens formidables ont contribué : +HTML - TEXT_DEV_VERSION || Cette version est une version de développement. +HTML - TEXT_DEVELOPED_BY || est développé par +HTML - TEXT_FIRST_SESSION || First session +HTML - TEXT_LICENSED_UNDER || est développé et fait l'objet d'une licence en vertu de +HTML - TEXT_METRICS || Métriques bStats +HTML - TEXT_NO_EXTENSION_DATA || Pas de données d'extension +HTML - TEXT_NO_LOW_TPS || Pas de pics de TPS bas +HTML - TEXT_NO_SERVER || No server to display online activity for +HTML - TEXT_NO_SERVERS || No servers found in the database +HTML - TEXT_PLUGIN_INFORMATION || Informations concernant le plugin +HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players +HTML - TEXT_VERSION || Une nouvelle version a été publiée et est maintenant disponible en téléchargement. +HTML - TITLE_30_DAYS || 30 jours +HTML - TITLE_30_DAYS_AGO || Il y a 30 jours +HTML - TITLE_ALL || Tout +HTML - TITLE_ALL_TIME || Tout le temps +HTML - TITLE_AS_NUMBERS || en Nombres +HTML - TITLE_AVG_PING || Latence moyenne +HTML - TITLE_BEST_PING || Meilleure latence +HTML - TITLE_CALENDAR || Calendrier +HTML - TITLE_CONNECTION_INFO || Renseignements sur la connexion +HTML - TITLE_COUNTRY || Pays +HTML - TITLE_CPU_RAM || CPU & RAM +HTML - TITLE_CURRENT_PLAYERBASE || Base de joueurs actuelle +HTML - TITLE_DISK || Espace disque +HTML - TITLE_GRAPH_DAY_BY_DAY || Jour par Jour +HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Activité en ligne du réseau +HTML - TITLE_GRAPH_PUNCHCARD || Carte perforée sur 30 jours +HTML - TITLE_INSIGHTS || Perspectives sur 30 jours +HTML - TITLE_IS_AVAILABLE || est Disponible +HTML - TITLE_LAST_24_HOURS || 24 Dernières heures +HTML - TITLE_LAST_30_DAYS || 30 Derniers jours +HTML - TITLE_LAST_7_DAYS || 7 Derniers jours +HTML - TITLE_LAST_CONNECTED || Dernier connecté +HTML - TITLE_LENGTH || Longueur +HTML - TITLE_MOST_PLAYED_WORLD || Monde le plus joué +HTML - TITLE_NETWORK || Réseau +HTML - TITLE_NETWORK_AS_NUMBERS || Réseau en Nombres +HTML - TITLE_NOW || Maintenant +HTML - TITLE_ONLINE_ACTIVITY || Activité en ligne +HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || Activité en ligne en Nombres +HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || Aperçu de l'activité en ligne +HTML - TITLE_PERFORMANCE_AS_NUMBERS || Performances en Nombres +HTML - TITLE_PING || Latence +HTML - TITLE_PLAYER || Joueur +HTML - TITLE_PLAYER_OVERVIEW || Aperçu des joueurs +HTML - TITLE_PLAYERBASE_DEVELOPMENT || Évolution de la base de joueurs +HTML - TITLE_PVP_DEATHS || Décets récents en PvP +HTML - TITLE_PVP_KILLS || Kills récents en PvP +HTML - TITLE_PVP_PVE_NUMBERS || PvP & PvE en Nombres +HTML - TITLE_RECENT_KILLS || Kills récents +HTML - TITLE_RECENT_SESSIONS || Sessions récentes +HTML - TITLE_SEEN_NICKNAMES || Surnoms Vus +HTML - TITLE_SERVER || Serveur +HTML - TITLE_SERVER_AS_NUMBERS || Serveur en Nombres +HTML - TITLE_SERVER_OVERVIEW || Aperçu du serveur +HTML - TITLE_SERVER_PLAYTIME || Temps de jeu du serveur +HTML - TITLE_SERVER_PLAYTIME_30 || Temps de jeu du serveur sur 30 jours +HTML - TITLE_SESSION_START || Début de la session +HTML - TITLE_THEME_SELECT || Sélection du thème +HTML - TITLE_TITLE_PLAYER_PUNCHCARD || Carte perforée +HTML - TITLE_TPS || TPS +HTML - TITLE_TREND || Tendances +HTML - TITLE_TRENDS || Tendances sur 30 jours +HTML - TITLE_VERSION || Version +HTML - TITLE_WEEK_COMPARISON || Comparaison hebdomadaire +HTML - TITLE_WORLD || Charge du monde +HTML - TITLE_WORLD_PLAYTIME || Temps de jeu par monde +HTML - TITLE_WORST_PING || Pire latence HTML - TOTAL_ACTIVE_TEXT || Temps actif total HTML - TOTAL_AFK || Temps inactif total HTML - TOTAL_PLAYERS || Joueurs totaux -HTML - TOTAL_PLAYTIME || Temps de jeu total -HTML - UNIQUE || UNIQUE HTML - UNIQUE_CALENDAR || Unique : -HTML - UNIQUE_PLAYERS || JOUEURS UNIQUES -HTML - UNIQUE_PLAYERS_TEXT || Joueurs uniques -HTML - UNIQUE_TEXT || Unique(s) -HTML - USAGE || Utilisation -HTML - USED_COMMANDS || Commandes utilisées +HTML - UNIT_CHUNKS || Chunks +HTML - UNIT_ENTITIES || Entities +HTML - UNIT_NO_DATA || Aucune donnée +HTML - UNIT_THE_PLAYERS || Joueurs HTML - USER_AND_PASS_NOT_SPECIFIED || Utilisateur et mot de passe non spécifiés HTML - USER_DOES_NOT_EXIST || Cet utilisateur n'existe pas -HTML - USER_INFORMATION || INFORMATIONS SUR L'UTILISATEUR HTML - USER_PASS_MISMATCH || L'utilisateur et le mot de passe ne correspondent pas HTML - WITH ||
    Avec -HTML - WORLD || Monde -HTML - WORLD_LOAD || CHARGE DU MONDE -HTML - WORLD_PLAYTIME || Temps de jeu par monde -HTML - WORST_PING || Pire latence HTML ERRORS - ACCESS_DENIED_403 || Accès refusé. -HTML ERRORS - ANALYSIS_REFRESH || L'analyseur est en cours de rafraîchissement... -HTML ERRORS - ANALYSIS_REFRESH_LONG || L'analyse est en cours d'exécution, actualisez la page après quelques secondes.... HTML ERRORS - AUTH_FAIL_TIPS_401 || - Assurez-vous d'avoir enregistré un utilisateur avec :'/plan register'.
    - Vérifiez que le nom d'utilisateur et le mot de passe soient corrects.
    - Le nom d'utilisateur et le mot de passe sont sensibles au format majuscule/minuscule.

    Si vous avez oublié votre mot de passe, demandez à un membre du staff de supprimer votre ancien utilisateur puis de vous réinscrire. HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentification échouée. HTML ERRORS - FORBIDDEN_403 || Accès interdit. @@ -260,44 +306,42 @@ In Depth Help - /plan inspect ? || > §2Commande d'inspection\ In Depth Help - /plan manage ? || > §2Commande de gestion\ Gestion des bases de données MySQL et SQLite de Plan.\ §2/plan m §fListe des sous-commandes\ §2/plan m ? §fAide en profondeur In Depth Help - /plan manage backup ? || > §2Sous-commande de sauvegarde\ Crée une nouvelle base de données SQLite (fichier .db) avec le contenu de la base de données actuellement active dans la configuration de Plan. In Depth Help - /plan manage clear ? || > §2Sous-commande d'effacement\ Supprime l'intégralité de la base de données active. À utiliser avec précaution. -In Depth Help - /plan manage con ? || > §2Sous-commande de débogage de connexion\ Utilisée pour déboguer les connexions du réseau.\ Envoie une demande à chaque serveur présent dans la base de données. -In Depth Help - /plan manage disable ? || > §2Sous-commande de désactivation\ Peut désactiver des parties du plugin jusqu'au prochain rechargement.\ Arguments acceptés :\§2kickcount §fDésactive le comptage d'éjections dans le cas où tous les joueurs ont été expulsés par un arrêt. +In Depth Help - /plan manage disable ? || > §2Sous-commande de désactivation\ Peut désactivert des parties du plugin jusqu'au prochain redémarrage.\ Arguments acceptés :\ §2kickcounts §fDésactive le comptage d'éjections au cas où /kickall est utilisé sur la commande d'arrêt. +In Depth Help - /plan manage export ? || > §2Sous-commande d'exportation\ Déclenche l'exportation vers les dossiers de résultats.\ Arguements acceptés :\ §2list §fListe des arguments possibles.\ §2players §fExporte /players, /player pages + /player/raw JSON en fonction des valeurs de la configuration.\ §2server_json §fExporte /server/raw JSON si activé dans la configuration. In Depth Help - /plan manage import ? || > §2Sous-commande d'importation\ Importe des données depuis d'autres sources.\ Arguments acceptés :\ §2offline §fDonnées du joueur Bukkit, uniquement la date d'enregistrement et le nom. In Depth Help - /plan manage move ? || > §2Sous-commande de déplacement\ Déplace les données de SQLite vers MySQL ou autre.\ La base de données cible est effacée avant le transfert. +In Depth Help - /plan manage raw ? || > §2Sous-commande des données brutes\ Affiche le lien vers la page de données JSON brutes.\ Indisponible si le serveur Web de Plan n'est pas activé. In Depth Help - /plan manage remove ? || > §2Sous-commande de suppression\ Supprime les données d'un joueur de la base de données active. In Depth Help - /plan manage restore ? || > §2Sous-commande de restauration\ Restaure une sauvegarde de base de données SQLite précédente (fichier .db)\ Vous pouvez également restaurer le fichier database.db d'un autre serveur vers MySQL.\ La base de données cible est effacée avant le transfert. -In Depth Help - /plan manage setup ? || > §2Sous-commande de configuration\ Configure une connexion entre Bungee et ce serveur pour la fonctionnalité réseau.\ Vous pouvez trouver l'adresse du Bungee dans les logs de la la console lorsque Plan est activé sur Bungee. +In Depth Help - /plan manage uninstalled ? || > §2Sous-commande de serveur désinstallé\ Marque un serveur comme désinstallé dans la base de données.\ Impossible de marquer le serveur sur lequel la commande est utilisée comme désinstallé.\ Influencera le ConnectionSystem. In Depth Help - /plan network ? || > §2Sous-commande du réseau\ Affiche le lien vers la page du réseau.\ S'il ne s'agit pas d'un réseau, elle affiche la page du serveur. In Depth Help - /plan players ? || > §2Commande des joueurs\ Affiche un lien vers la page des joueurs. -In Depth Help - /plan qinspect ? || > §2Quick Inspect CommandCommande d'inspection rapide\ Affiche des informations sur un joueur en jeu. +In Depth Help - /plan qinspect ? || > §2Commande d'inspection rapide\ Affiche des informations sur un joueur en jeu. In Depth Help - /plan reload ? || > §2Commande de rechargement\ Redémarre le plugin en utilisant onDisable et onEnable.\ §bNe supporte pas l'échange de .jar à la volée (sans redémarrage complet) In Depth Help - /plan search ? || > §2Commande de recherches\ Obtient une liste des joueurs correspondant à l'argument donné.\§7 Exemple: /plan search 123 - Recherche tous les utilisateurs dont le nom comporte '123'. In Depth Help - /plan servers ? || > §2Commande des serveurs\ Affiche la liste des serveurs de Plan présents dans la base de données.\ Peut être utilisé pour résoudre les problèmes d'enregistrement de base de données sur un réseau. -In Depth Help - /plan update ? || > §2Commande de mise à jour\ Utilisé pour mettre à jour le plugin lors du prochain arrêt\ /plan update - Lien des modifications\ /plan update -u - Planifie la mise à jour sur tous les serveurs de réseau en ligne, au prochain redémarrage.\ /plan update cancel - Annule la mise à jour programmée sur les serveurs qui n'ont pas encore redémarré. In Depth Help - /plan web ? || < §2Commande de la gestion d'utilisateurs Web\ §2/plan web §fListe des sous-commandes\ §2/plan web ? §fAide en profondeure In Depth Help - /plan web register ? || > §2Commande d'enregistrement\ Enregistre un nouvel utilisateur Web.\ L'enregistrement d'un utilisateur pour un autre joueur nécessite l'autorisation 'plan.webmanage'.\ Les mots de passe sont hachés avec PBKDF2 (64000 itérations de SHA1) en utilisant 'un sel cryptographiquement aléatoire'. In Depth Help - /planbungee disable ? || > §2Commande de désactivation\ Lance onDisable pour PlanBungee.\ Le plugin peut être activé par la suite en exécutant la commande /planbungee reload.\ §bNe supporte pas l'échange de .jar à la volée (sans redémarrage complet) -In Depth Help - /planbungee setup ? || > §2Commande de bascule de configuration\ Bascule le mode de configuration sur Bungee.\ Protégez-vous contre le 'fouinage' non autorisée de MySQL avec un autre serveur. Manage - Confirm Overwrite || Les données ${0} seront écrasées ! Manage - Confirm Removal || Les données ${0} seront supprimées ! Manage - Fail || > §cUne erreur est survenue : ${0}. Manage - Fail File not found || > §cAucun fichier trouvé depuis ${0}. Manage - Fail Incorrect Database || > §c'${0}' n'est pas une base de données prise en charge. +Manage - Fail No Exporter || §eL'exportateur '${0}' n'existe pas. Manage - Fail No Importer || §eL'importateur '${0}' n'existe pas. +Manage - Fail No Server || Aucun serveur trouvé avec les paramètres suivants. Manage - Fail Same Database || > §cNe peut pas opérer sur et depuis la même base de données ! +Manage - Fail Same server || Impossible de marquer ce serveur comme désinstallé (vous y êtes). Manage - Fail, Confirmation || > §cAjoutez l'argmenter '-a' afin de confirmer l'éxécution : ${0} -Manage - Fail, Connection Exception || §eRaison de l'échec : -Manage - Fail, No Servers || §cAucun serveur trouvé dans la base de données. -Manage - Fail, Old version || §eRaison de l'échec : Le serveur de réception utilise une ancienne version de Plan. -Manage - Fail, Unauthorized || §eRaison de l'échec : Non autorisé. Le serveur utilise peut-être une base de données différente. -Manage - Fail, Unexpected Exception || §eErreur : ${0}. -Manage - List Importers || Importateurs : -Manage - Notify External Url || §eAdresse non locale, vérifiez que le port soit ouvert. +Manage - List Importers || Importateurs : Manage - Remind HotSwap || §eN'oubliez pas de passer à la nouvelle base de données (/plan m hotswap ${0}) & de recharger Plan. Manage - Start || > §2Traitement des données... Manage - Success || > §aSuccès ! Negative || Non Positive || Oui +Today || 'Aujourd''hui' +Unavailable || Unavailable Unknown || Inconnu Version - DEV || Ceci est une version de développement. Version - Latest || Vous utilisez la dernière version. @@ -313,24 +357,3 @@ WebServer FAIL - Port Bind || Le Serveur Web n'a pas été WebServer FAIL - SSL Context || Serveur Web : Échec d'initialisation du contexte SSL. WebServer FAIL - Store Load || Serveur Web : Échec du chargement du certificat SSL. Yesterday || 'Hier' -Today || 'Aujourd''hui' -Health - Active Playtime Comparison Decrease || Les joueurs actifs risquent de manquer de choses à faire (Joué ${0} contre ${1}, les deux dernières semaines par rapport aux semaines 2 à 4). -Health - Active Playtime Comparison Increase || Les joueurs actifs semblent avoir des choses à faire (Joué ${0} contre ${1}, les deux dernières semaines par rapport aux semaines 2 à 4). -Health - Downtime || Le temps total d'arrêt du serveur (absence de données) était de ${0}. -Health - New Player Join Players, No || Les nouveaux joueurs peuvent ne pas avoir de joueurs avec lesquels jouer (${0} en moyenne). -Health - New Player Join Players, Yes || Les nouveaux joueurs ont des joueurs avec lesquels jouer (${0} en moyenne). -Health - New Player Stickiness || ${0} des nouveaux joueurs sont restés (${1}/${2}). -Health - No Servers Inaccuracy || Aucun serveur Bukkit/Sponge pour collecter des données de session - Ces mesures sont inexactes. -Health - Player Play on Network || joueurs jouent sur le réseau : -Health - Player Register Server || joueurs s'enregistrent surle serveur par jour/serveur en moyenne. -Health - Player Visit Server || joueurs visitent le serveur par jour/serveur en moyenne. -Health - Regular Activity Change || Le nombre de joueurs réguliers a -Health - Regular Activity Change Decrease || diminué (${0}). -Health - Regular Activity Change Increase || augmenté (+${0}). -Health - Regular Activity Change Zero || stagné (+/-${0}). -Health - Regular Activity Remain || ${0} des joueurs réguliers sont restés actifs (${1}/${2}). -Health - Single Servers Inaccuracy || Un seul serveur Bukkit/Sponge pour collecter les données de session. -Health - TPS Above Low Threshold || Les TPS moyens étaient supérieurs au seuil bas ${0} du temps. -Health - TPS Low Dips || Les TPS moyens sont descendus sous le seuil bas (${0}) ${1} fois. -HTML - FREE_DISK_SPACE || Espace disque disponible -HTML - DISK_SPACE || ESPACE DISQUE diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_FR_old.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_FR_old.txt deleted file mode 100644 index 6cdfddc45..000000000 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_FR_old.txt +++ /dev/null @@ -1,118 +0,0 @@ -Analysis - Third Party || Analyse | Analyse des sources de données supplémentaires (tierce partie). -Analysis FAIL - Fetch Exception || Analyse | Échec de l’extraction des données pour l’analyse, une exception s'est produite. -Analysis FAIL - No Data || Analyse | L'analyse a échoué, aucune donnée dans la base de données. -Analysis FAIL - No Players || Analyse | L'analyse a échoué, aucun joueur connu. -Analysis - Fetch Phase || Analyse | Récupération des données.. -Analysis - Fetch Phase Start || Analyse | Vérification des joueurs disponibles.. -Analysis - Complete || Analyse | Analyse terminée. (En ${0}ms) ${1} -Analysis - Begin Analysis || Analyse | Données récupérées (${0} utilisateurs, en ${1}ms), début Analyse des données.. -Analysis - Start || Analyse | Début de l'analyse des données utilisateur.. ->Constant - CMD Footer || §f» ->Constant - List Ball || §7 •§2 -Cmd FAIL - No Data View || §e[Plan] Aucun moyen de voir les données disponibles. -Cmd FAIL - No Permission || §c[Plan] Vous n'avez pas la permission requise. -Cmd FAIL - Requires Arguments || §c[Plan] La commande nécessite des arguments. ${0} -Cmd FAIL - Require only one Argument || §c[Plan] La commande nécessite un argument. -Cmd FAIL - Timeout || §c[Plan] ${0} La commande a expiré ! Vérifiez avec '/plan status' et la console. -Cmd FAIL - Unknown Username || §c[Plan] Joueur introuvable dans la base de données. -Cmd FAIL - Unseen Username || §c[Plan] Ce joueur n'a pas joué sur ce serveur. -Cmd FAIL - Invalid Username || §c[Plan] Ce joueur n'existe pas. -Cmd Header - Analysis || §f»§2 Player Analytics - Résultats de l’analyse -Cmd Header - Info || §f»§2 Player Analytics - Info -Cmd Header - Inspect || §f»§2 Player Analytics - Résultats de l’inspection -Cmd Header - Search || §f»§2 Player Analytics - Résultats de la recherche: -In Depth Help - /plan analyze ? || §2Commande d’analyse\§f Utilisé pour actualiser le cache d'analyse et accéder à la page de résultats\§7 /plan status peut être utilisé pour vérifier le statut de l'analyse pendant son exécution.\§7 Alias: analyze, analyse, analysis, a -In Depth Help - /plan inspect ? || §2Commande d’inspection\§f Utilisé pour obtenir un lien vers la page d'inspection d’un utilisateur.\§7 Votre propre page d'inspection peut être consultée avec /plan inspect\§7 Alias: /plan -In Depth Help - /plan list ? || §2Commande de listing\§f Utilisé pour obtenir un lien vers la page des joueurs.\§7 La page des joueurs contient des liens vers toutes les pages d'inspection mises en cache.\§7 Alias: /plan pl -In Depth Help - /plan manage ? || §2Commande de gestion\§f Utilisé pour gérer la base de données du plugin.\§7 Alias: /plan m\§7 /plan m - Liste des sous-commandes\§7 /plan m ? - aide approfondie -In Depth Help - /plan manage clear ? || §2Gestion: Commande d’effacement.\§f Utilisé pour supprimer TOUTES les données dans la base de données active.\§7 Le plugin doit être rechargé après l’effacement terminé.\§7 Alias: /plan pl -In Depth Help - /plan manage dump ? || §2Gestion: Commande de listage\§f Utilisé pour lister des données importantes pour le rapport de bug à hastebin. -In Depth Help - /plan manage hotswap ? || §2Gestion: Commande de Hotswap\§f Utilisé pour modifier la base de données en cours d'utilisation.\§7 Ne change pas la base de données si la connexion échoue. -In Depth Help - /plan manage import ? || §2Gestion: Commande d'importation\§f Utilisé pour importer des données d'autres sources\§7 L'analyse sera désactivée pendant l'importation. -In Depth Help - /plan manage remove ? || §2Gestion: Commande de suppression\§f Utilisé pour supprimer les données de l'utilisateur de la base de données active. -In Depth Help - /plan ? || §2/plan - Commande principale\§f Utilisé pour accéder à toutes les sous-commandes et aide\§7 /plan - Liste des sous-commandes\§7 /plan ? - aide approfondie -In Depth Help - /plan qanalyze ? || §2Commande d'analyse rapide\§f Utilisé pour obtenir des informations en jeu sur l'analyse.\§7 A moins d'informations que la page Web d'analyse complète.\§7 Alias: qanalyze, ganalyse, qanalysis, qa -In Depth Help - /plan qinspect ? || §2Commande d'inspection rapide\§f Utilisé pour obtenir des informations d'inspection en jeu.\§7 A moins d'informations que la page Web d'inspection complète.\§7 Alias: /plan qi -In Depth Help - /plan search ? || §2Commande de recherche\§f Utilisé pour obtenir une liste des noms de joueurs qui correspondent à l'argument donné.\§7 Exemple: /plan search 123 - Trouve tous les utilisateurs avec 123 dans leur nom. -In Depth Help - /plan webuser ? || §2Commande de gestion des utilisateurs Web\§f Utilisé pour gérer les utilisateurs Web du plugin\§7 Les utilisateurs ont un niveau de permission:\§f 0 - Accès à toutes les pages\§f 1 - Accès à /players et toutes les pages d'inspection\§f 2 - Accès à sa propre page d'inspection\§7 Alias: /plan web -In Depth Help - /plan webuser register ? || §2Commande Web Register\§f Utilisé pour enregistrer un nouvel utilisateur pour le serveur Web.\§7 L'inscription d'un utilisateur pour un autre joueur nécessite la permission plan.webmanage.\§7 Les mots de passe sont hachés avec PBKDF2 (64,000 itérations de SHA1) en utilisant un sel cryptographiquement aléatoire. -Analysis NOTIFY - Temporary Disable || §eL'analyse a été temporairement désactivée en raison d’un charge trop importante, faites /plan status pour plus d'informations. -Cmd - Click Me || Cliquez ici -Cmd - Fetch Data || §f»§2 Récupération des données dans le cache.. -Cmd - Link || §7 • §2Lien: §f -Cmd - No Results || §7 • §2Aucun résultat pour §7${0}§2. -Cmd - Reload Success || §a[Plan] Recharge terminée. -Cmd - Results || §7 Joueurs correspondants: §f -Cmd - Searching || §f»§2Recherche.. -Cmd - Usage /plan analyze || Voir l'analyse du serveur -Cmd - Usage /plan help || Afficher la liste des commandes. -Cmd - Usage /plan info || Vérifier la version de of Plan -Cmd - Usage /plan inspect || Inspecter les données du joueur -Cmd - Usage /plan list || Liste à tous les joueurs mis en cache -Cmd - Usage /plan manage || Commande de gestion de base de données -Cmd - Usage /plan manage backup || Sauvegarder une base de données dans un fichier .db -Cmd - Usage /plan manage clean || Effacer les anciennes données de la base de données -Cmd - Usage /plan manage clear || Effacer TOUTES les données de la base de données -Cmd - Usage /plan manage dump || Créez un journal Hastebin pour les développeurs pour faciliter le rapport de bug -Cmd - Usage /plan manage hotswap || Hot Swap à une autre base de données et redémarrer le plugin -Cmd - Usage /plan manage import || Importer des données depuis les plugins pris en charge vers la base de données active. -Cmd - Usage /plan manage move || Copier les données d'une base de données à une autre et remplacer les valeurs -Cmd - Usage /plan manage remove || Supprimer les données des joueurs de la base de données active. -Cmd - Usage /plan manage restore || Restaurer une base de données à partir d'un fichier de sauvegarde -Cmd - Usage /plan qanalyze || Voir l'analyse du serveur sous forme de texte -Cmd - Usage /plan qinspect || Inspecter les données du joueur sous forme de texte -Cmd - Usage /plan reload || Redémarrer le plugin (Recharge la configuration) -Cmd - Usage /plan search || Rechercher un joueur -Cmd - Usage /plan webuser || Gérer les utilisateurs Web -Cmd - Usage /plan webuser check || Vérifiez un utilisateur Web et son niveau de permission. -Cmd - Usage /plan webuser delete || Supprimer un utilisateur Web -Cmd - Usage /plan webuser level || Informations sur les niveaux de permission. -Cmd - Usage /plan webuser register || Enregistrer un utilisateur pour le serveur Web -Disable || Player Analytics Désactivé. -Disable - Save || Sauvegarde des données mises en cache.. -Disable - WebServer || Arrêt du serveur Web.. -Enable || Player Analytics Activé. -Enable - Boot Analysis 30s Notify || Analyse | Analyse de démarrage dans 30 secondes.. -Enable - Boot Analysis Notify || Analyse | Démarrage l'analyse de démarrage.. -Enable Db FAIL - Disable Info || L'initialisation de la base de données a échoué, désactivation de Plan. -Enable - Db Info || ${0}-connexion à la base de données établie. -Enable - Db || Initialisation de la base de données.. -Enable FAIL-Db || ${0}-Connexion à la base de données échouée: ${1} -Enable FAIL - Wrong Db Type || Ce type de base de données n'existe pas. -Enable Notify - ChatListener || §eÉcouteur de chat désactivé, information de pseudo inexacte. -Enable Notify - Disabled CommandListener || §eÉcouteur d'utilisation des commandes désactivé. -Enable Notify - Disabled DeathListener || §eÉcouteur de morts désactivé, morts de joueur et de mob non enregistrées.. -Enable Notify-Empty IP || §eL’IP dans server.properties est vide et AlternativeServerIP n'est pas utilisé, des liens incorrects seront donnés ! -Enable Notify - No data view || §eServeur Web désactivé mais AlternativeIP non utilisé, aucun moyen d'afficher les données ! -Enable - WebServer || Initialisation du serveur Web.. -Enable - WebServer Info || Serveur Web en cours d'exécution sur le PORT ${0} -Html - Active || Le joueur est actif -Html - Banned || | Banni -Html - Inactive || Le joueur est inactif -Html - No Extra Plugins ||

    Aucun plugin supplémentaire enregistré.

    -Html - Offline || | Hors ligne -Html - Online || | En ligne -Html - OP || , Opérateur (Op) -Html - Table No Kills || Pas de kills -Manage FAIL - Confirm Action || §c[Plan] Ajoutez -a pour confirmer l'exécution ! ${0} -Manage FAIL - Faulty DB Connection || §c[Plan] Une des bases de données n'a pas été initialisée correctement. -Manage FAIL - Backup File Not Found || §c[Plan] Le fichier de sauvegarde n'existe pas ! -Manage FAIL - Incorrect DB || §c[Plan] Base de données incorrecte ! (sqlite/mysql accepté): -Manage FAIL - Incorrect Plugin || §c[Plan] Plugin non supporté: -Manage FAIL - Empty DB || §c[Plan] La base de données n'a pas de données de joueur ! -Manage FAIL - Unenabled Plugin || §c[Plan] Le plugin n'est pas activé: -Manage FAIL - Same DB || §c[Plan] Impossible de passer à la même base de données ! -Manage - Clear Success || §a[Plan] Toutes les données ont été effacées avec succès! -Manage - Remind Config Change || §e[Plan] N'oubliez pas de basculer vers la nouvelle base de données et de recharger le plugin -Manage - Copy Success || §a[Plan] Toutes les données ont été copiées avec succès ! -Manage - Process Fail || §c[Plan] Quelque chose s'est mal passé pendant le traitement des données ! -Manage - Import || §f» §2 Importation de données.. -Manage - Move Success || §a[Plan] Toutes les données ont été déplacées avec succès ! -Manage - Remove Success || §f» §2Les données de §f${0}§2 ont été supprimées de la base de données §f${1}§2. -Manage - Start || »§7 Traitement des données.. -Manage - Success || §f» §2 Succès ! -Manage NOTIFY - Overwrite || Les données dans la base de données ${0} seront écrasées ! -Manage NOTIFY - Partial Overwrite || Quelques données dans la base de données ${0} seront écrasées ! -Manage NOTIFY - Remove || Les données dans la base de données ${0} seront supprimées ! -Manage NOTIFY - Rewrite || Les données dans la base de données ${0} seront réécrites ! -WARN - Too Small Queue Size || La taille de la file d'attente est trop petite ! (${0}), changez le réglage à un nombre plus élevé ! (Actuellement ${1}) diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_JA.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_JA.txt index 4c48d83f6..11df6b5b4 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_JA.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_JA.txt @@ -1,4 +1,4 @@ -Cmd - Click Me || ここをクリック +Cmd - Click Me || ここをクリック Cmd - Link || §2リンク: §f Cmd Disable - Disabled || §a「Plan」は無効になりました。「/planbungee reload」コマンドを使ってプラグインを再起動できます Cmd FAIL - Database not open || §cデータベースは${0}です - しばらくしてからもう一度お試し下さい @@ -24,29 +24,18 @@ Cmd Info - Reload Complete || §aリロードが完了し Cmd Info - Reload Failed || §cプラグインのリロード中に何らかの問題が発生しました、Bukkit/SpigotサーバーかBungeeCordの再起動をお勧めします Cmd Info - Update || §2利用可能なアップデート: §f${0} Cmd Info - Version || §2バージョン: §f${0} -Cmd Notify - No WebUser || ウェブユーザーの情報がない可能性があります、「/plan register 」を使用して登録して下さい +Cmd Notify - No WebUser || ウェブユーザーの情報がない可能性があります、「/plan register 」を使用してユーザーを登録して下さい Cmd Notify - WebUser register || 登録が完了しました: '${0}' 権限レベル: ${1} Cmd Qinspect - Activity Index || §2活動指数: §f${0} | ${1} Cmd Qinspect - Deaths || §2死亡回数: §f${0} -Cmd Qinspect - Geolocation || §2出身: §f${0} +Cmd Qinspect - Geolocation || §2接続地域: §f${0} Cmd Qinspect - Last Seen || §2最終ログイン日: §f${0} Cmd Qinspect - Longest Session || §2最長ログイン時間: §f${0} Cmd Qinspect - Mob Kills || §2キルカウント(モブ): §f${0} Cmd Qinspect - Player Kills || §2キルカウント(プレイヤー): §f${0} Cmd Qinspect - Playtime || §2プレイ時間: §f${0} Cmd Qinspect - Registered || §2登録日: §f${0} -Cmd Qinspect - Times Kicked || §2キックされた時間: §f${0} -Cmd Setup - Allowed || §aセットアップモードが有効になりました -Cmd Setup - Bad Request || §e接続は成功しましたが、受信サーバーはBungeeCordのサーバーではありませんでした。代わりにBungeeCordのアドレスを使用して下さい -Cmd Setup - Disallowed || §cセットアップモードが無効になりました -Cmd Setup - Forbidden || §e接続は成功しましたが、BungeeCordの「Plan」のセットアップモードが無効になっています。「/planbungee setup」コマンドを使用して有効にして下さい -Cmd Setup - Gateway Error || §e接続は成功しましたが、BungeeCordの「Plan」のウェブサーバーに接続できませんでした。(ウェブサーバーが再起動中かもしれません)。「/plan m con 」と「/planbungee con」でデバック情報を表示できます -Cmd Setup - Generic Fail || §e以下の理由で接続に失敗しました: ${0} -Cmd Setup - Internal Error || §e接続は成功しました。 ${0}、受信サーバーのデバッグページでエラーログを確認できるようになりました -Cmd Setup - Success || §a接続は成功しました。「Plan」は数秒以内に再起動します。 -Cmd Setup - Unauthorized || §e接続は成功しましたが、このBukkit/Spigotサーバーを受信サーバーが拒否しました。Discordのサポートに連絡して下さい -Cmd Setup - Url mistake || §cウェブアドレスの入力を確かめて下さい(「http://」もしくは「https://」で始まります )。アドレスについてはBungeeCordの「Plan」を有効化した時のログを確認して下さい。 -Cmd Setup - WebServer not Enabled || §cこのBukkit/Spigotサーバーでウェブサーバーは有効になっていません。Bukkit/Spigotサーバー起動時にウェブサーバーが有効になっていることを確認して下さい! +Cmd Qinspect - Times Kicked || §2キックされた回数: §f${0} Cmd SUCCESS - Feature disabled || §a次にプラグインがリロードされるまで一時的に「${0}」を無効にしました。 Cmd SUCCESS - WebUser register || §a新規ユーザー「(${0})」の登録に成功しました!次のリンクでWebパネルを見ることができます。 Cmd WARN - Database not open || §eデータベースは${0}です - 予想以上に時間がかかるかもしれません @@ -59,7 +48,6 @@ Command Help - /plan inspect || 「プレイヤー」のURL Command Help - /plan manage || 「Plan」のデータベースを管理します Command Help - /plan manage backup || データベースをバックアップします Command Help - /plan manage clear || データベースを消去します -Command Help - /plan manage con || BungeeCordとBukkit/Spigotサーバーとの接続をデバックします Command Help - /plan manage disable || 機能を一時的に無効にします Command Help - /plan manage export || 手動でデータのエクスポートを行います Command Help - /plan manage hotswap || データベースを高速で変更します @@ -68,7 +56,6 @@ Command Help - /plan manage move || データベース間でデ Command Help - /plan manage raw || プレイヤーデータのJSONファイルを直接表示ます Command Help - /plan manage remove || プレイヤーのデータを削除します Command Help - /plan manage restore || 以前のバックアップから復元します -Command Help - /plan manage setup || BungeeCordとBukkit/Spigotサーバーとの接続をセットアップします Command Help - /plan manage uninstalled || 指定されたサーバーをデータベースからアンインストールします Command Help - /plan network || 「ネットワーク」のページのURLを表示します Command Help - /plan players || 「プレイヤー」のページのURLを表示します @@ -82,15 +69,13 @@ Command Help - /plan web delete || ウェブユーザーの削 Command Help - /plan web level || 権限レベルに関する情報を表示します Command Help - /plan web list || ウェブユーザーの一覧を表示します Command Help - /plan webuser || ウェブユーザーを管理します -Command Help - /planbungee con || BungeeとBukkit/Spigotサーバーとの接続をデバックします Command Help - /planbungee disable || プラグインを一時的に無効にします -Command Help - /planbungee setup || セットアップモードを切り替えます Database - Apply Patch || 次のパッチを適用しています: ${0}.. Database - Patches Applied || 全てのパッチは正常にデータベースに適用されました Database - Patches Applied Already || 全てのパッチは既にデータベースへ適用されています Database MySQL - Launch Options Error || 起動オプションに問題があります,デフォルトのオプションを使用して下さい (${0}) Database Notify - Clean || ${0} のプレイヤーデータを削除しています -Database Notify - SQLite No WAL || SQLiteのWALモードはこのサーバのバージョンではサポートされていないため、デフォルトを使用します。これはサーバーのパフォーマンスに影響を与える可能性があります +Database Notify - SQLite No WAL || SQLiteのWALモードはこのサーバのバージョンではサポートされていないため、初期設定に変更します。これはサーバーのパフォーマンスに影響を与える可能性があります Disable || プレイヤー分析が無効になりました Disable - Processing || 未実行の重要な処理があります (${0}) Disable - Processing Complete || 処理が完了しました @@ -98,7 +83,6 @@ Disable - Unsaved Session Save || 未保存のセッション Disable - WebServer || ウェブサーバーが無効になりました Enable || プレイヤー分析が有効になりました Enable - Database || ${0}のデータベースの接続が確立しました -Enable - Notify Address Confirmation || 次に表示されるアドレスがこのサーバーをである事を確認して下さい: ${0} Enable - Notify Empty IP || server.propertiesの設定で、IPの項目が設定されておらずAlternative IPが使用されていません。そのため誤ったリンクが表示されます! Enable - Notify Geolocations disabled || 位置情報サービスが有効ではありません。 (Data.Geolocations: false) Enable - Notify Geolocations Internet Required || 「Plan」は初回起動時、「GeoLite2」の位置情報データベースをダウンロードするためインターネットアクセスが必要です @@ -107,169 +91,212 @@ Enable - WebServer || ウェブサーバーは次 Enable FAIL - Database || ${0}のデータベースの接続に失敗しました: ${1} Enable FAIL - Database Patch || データベースのパッチ適用に失敗しました、プラグインを無効にする必要があります。バグ報告をお願いします Enable FAIL - GeoDB Write || ダウンロードした「GeoLite2」の位置情報データベースを保存中に何らかのエラーが発生しました -Enable FAIL - WebServer (Bungee) || ウェブサーバーの初期化に失敗しました! +Enable FAIL - WebServer (Proxy) || ウェブサーバーの初期化に失敗しました! Enable FAIL - Wrong Database Type || ${0}はサポートされていないデータベースです -Health - Active Playtime Comparison Decrease || よくログインしているプレイヤーのやることが少なくなっている可能性があります (直近2週間のアクティブ時間が${0}に対し、1ヶ月~2週間のアクティブ時間が${1}です) -Health - Active Playtime Comparison Increase || よくログインしているプレイヤーのやるべきことがある可能性があります (直近2週間のアクティブ時間が${0}に対し、1ヶ月~2週間のアクティブ時間が${1}です) -Health - Downtime || サーバーの合計停止時間(データが存在しない)が${0}です -Health - New Player Join Players, No || 新規プレイヤーが参加時、一緒にプレイするプレイヤーがいないかもしれません。(平均${0}人のプレイヤーがオンラインです) -Health - New Player Join Players, Yes || 新規プレイヤーが参加時、一緒にプレイするプレイヤーがいます。(平均${0}人のプレイヤーがオンラインです) -Health - New Player Stickiness || ${0}人の新規プレイヤーが行き詰詰まっています。(${1}/${2}) -Health - No Servers Inaccuracy || セッションデータを収集するBukkit/Spongeサーバーが接続されていません - 不正確なデータが表示されます -Health - Player Play on Network || 人のプレイヤーがネットワーク上でプレイしています: -Health - Player Register Server || 人のプレイヤーが1日あたり登録されています。以下サーバーごとの1日の登録数: -Health - Player Visit Server || 人のプレイヤーが1日あたりこのサーバーに接続しています。以下サーバーごとの1日の接続数: -Health - Regular Activity Change || しばしばログインしているプレイヤー数は -Health - Regular Activity Change Decrease || ${0}人減少しました (-${0}人) -Health - Regular Activity Change Increase || ${0}人増加しました (+${0}人) -Health - Regular Activity Change Zero || 変化していません (+${0}人) -Health - Regular Activity Remain || ${0}人のしばしばログインしているプレイヤーはよくログインするようになりました(${1}/${2}) -Health - Single Servers Inaccuracy || セッションデータを収集するためのBukkit/Spongeサーバーが1つしか存在しません -Health - TPS Above Low Threshold || 平均TPSの${0}が平均TPSの下限しきい値より高い値を示しています -Health - TPS Low Dips || 平均TPSの下限しきい値を下回った回数:${1}回(以前の値:${0}回) -HTML - ACTIVITY_INDEX || 活動指数 -HTML - ALL || 全て -HTML - ALL_TIME_PEAK || 全体のピークタイム -HTML - AVERAGE_PING || 平均応答時間 -HTML - AVG || 平均 -HTML - BANNED || BAN履歴 -HTML - BEST_PING || 最低応答時間 -HTML - CALENDAR || カレンダー -HTML - CALENDAR_TEXT || カレンダー -HTML - CHUNKS || チャンク -HTML - COMMAND || コマンド -HTML - COMMNAND_USAGE || コマンド使用履歴 -HTML - CONNECTION_INFORMATION || 接続情報 -HTML - COUNTRY || 国/地域 -HTML - CURRENT_PLAYERBASE || ログインプレイヤー +HTML - COMPARING_15_DAYS || 直近15日との比較 +HTML - COMPARING_60_DAYS || 30日前との比較 +HTML - COMPARING_7_DAYS || 直近1週間との比較 HTML - DATABASE_NOT_OPEN || データベースを開くことができませんでした。「/plan info」コマンドを実行して状況を確認して下さい -HTML - DEATHS || 死亡回数 -HTML - DISK_SPACE || ドライブの容量 -HTML - ENTITIES || エンティティ数 HTML - ERROR || エラーが発生したため認証に失敗しました -HTML - FAVORITE_SERVER || お気に入りのサーバー -HTML - FREE_DISK_SPACE || ドライブの空き容量 -HTML - GEOLOCATION || 地域 -HTML - GEOLOCATION_TEXT || 地域 -HTML - HEALTH_ESTIMATE || ネットワークの健康状態 HTML - INDEX_ACTIVE || よくログインしている HTML - INDEX_INACTIVE || 休止中 HTML - INDEX_IRREGULAR || たまにログインしている HTML - INDEX_REGULAR || しばしばログインしている HTML - INDEX_VERY_ACTIVE || とてもログインしている -HTML - IP_ADDRESS || IPアドレス HTML - KILLED || 殺した人 -HTML - KILLED_BY || 殺された人 -HTML - LAST_24_HOURS || 直近24時間以内 -HTML - LAST_30_DAYS || 直近30日以内 -HTML - LAST_30_DAYS_TEXT || 直近30日以内の出来事 -HTML - LAST_7_DAYS || 一週間以内 -HTML - LAST_CONNECTED || 直近の接続 -HTML - LAST_PEAK || 直近のピークタイム -HTML - LAST_SEEN || 直近のオンライン -HTML - LAST_SEEN_TEXT || 直近のオンライン -HTML - LOADED_CHUNKS || ロードされたチャンク数 -HTML - LOADED_ENTITIES || ロードされたエンティ数 +HTML - LABEL_1ST_WEAPON || 最もPvPで使用されている武器 +HTML - LABEL_2ND_WEAPON || 2番目にPvPで使用されている武器 +HTML - LABEL_3RD_WEAPON || 3番目にPvPで使用されている武器 +HTML - LABEL_ACTIVITY_INDEX || 活動指数 +HTML - LABEL_AFK || 離席 +HTML - LABEL_AFK_TIME || 離席時間 +HTML - LABEL_AVG || 平均 +HTML - LABEL_AVG_KDR || 平均キルデスレート +HTML - LABEL_AVG_MOB_KDR || モブに対してのキルデスレート +HTML - LABEL_AVG_PLAYTIME || 平均プレイ時間 +HTML - LABEL_AVG_SESSION_LENGTH || 平均接続時間 +HTML - LABEL_AVG_TPS || 平均TPS +HTML - LABEL_BANNED || BAN履歴 +HTML - LABEL_BEST_PEAK || 全体のピークタイム +HTML - LABEL_DAY_OF_WEEK || Day of the Week +HTML - LABEL_DEATHS || 死亡回数 +HTML - LABEL_DOWNTIME || ダウンタイム +HTML - LABEL_DURING_LOW_TPS || TPSの低下までの時間: +HTML - LABEL_ENTITIES || エンティティ数 +HTML - LABEL_FAVORITE_SERVER || お気に入りのサーバー +HTML - LABEL_FIRST_SESSION_LENGTH || 初参加時の接続時間 +HTML - LABEL_FREE_DISK_SPACE || ドライブの空き容量 +HTML - LABEL_INACTIVE || 休止中 +HTML - LABEL_LAST_PEAK || 直近のピークタイム +HTML - LABEL_LAST_SEEN || 直近のオンライン +HTML - LABEL_LOADED_CHUNKS || ロードされたチャンク数 +HTML - LABEL_LOADED_ENTITIES || ロードされたエンティ数 +HTML - LABEL_LONE_JOINS || 一人での接続 +HTML - LABEL_LONE_NEW_JOINS || 新しく一人での参加 +HTML - LABEL_LONGEST_SESSION || 最長接続時間 +HTML - LABEL_LOW_TPS || TPSの低下値 +HTML - LABEL_MAX_FREE_DISK || Max Free Disk +HTML - LABEL_MIN_FREE_DISK || Min Free Disk +HTML - LABEL_MOB_DEATHS || Mobによって殺された回数 +HTML - LABEL_MOB_KDR || Mobに対してのKDR +HTML - LABEL_MOB_KILLS || Mobを殺した回数 +HTML - LABEL_MOST_ACTIVE_GAMEMODE || 最も有効になっているゲームモード +HTML - LABEL_NAME || 名前 +HTML - LABEL_NEW || 新しい +HTML - LABEL_NEW_PLAYERS || 新規プレイヤー +HTML - LABEL_NICKNAME || ニックネーム +HTML - LABEL_NO_SESSION_KILLS || None +HTML - LABEL_ONLINE_FIRST_JOIN || Players online on first join +HTML - LABEL_OPERATOR || 管理者 +HTML - LABEL_PER_PLAYER || / プレイヤー(1プレイヤーあたりの) +HTML - LABEL_PER_REGULAR_PLAYER || / 登録済みプレイヤー(1登録済みプレイヤーあたりの) +HTML - LABEL_PLAYER_DEATHS || プレイヤーによるキル +HTML - LABEL_PLAYER_KILLS || プレイヤーキル +HTML - LABEL_PLAYERS_ONLINE || オンラインのプレイヤー +HTML - LABEL_PLAYTIME || プレイ時間 +HTML - LABEL_REGISTERED || 登録 +HTML - LABEL_REGISTERED_PLAYERS || 登録済みプレイヤー +HTML - LABEL_REGULAR || よく居るプレイヤー +HTML - LABEL_REGULAR_PLAYERS || よく居るプレイヤー +HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity +HTML - LABEL_RETENTION || 新規プレイヤーの継続率 +HTML - LABEL_SERVER_DOWNTIME || サーバーダウンタイム +HTML - LABEL_SERVER_OCCUPIED || サーバーの占有時間 +HTML - LABEL_SESSION_ENDED || ログアウト時間 +HTML - LABEL_SESSION_MEDIAN || 平均オンライン +HTML - LABEL_TIMES_KICKED || キック回数 +HTML - LABEL_TOTAL_PLAYERS || トータルプレイヤー数 +HTML - LABEL_TOTAL_PLAYTIME || トータルプレイ時間 +HTML - LABEL_UNIQUE_PLAYERS || ユニークプレイヤー +HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' +HTML - LINK_BACK_NETWORK || ネットワークページ +HTML - LINK_BACK_SERVER || サーバーページ +HTML - LINK_CHANGELOG || 変更履歴の確認 +HTML - LINK_DISCORD || Discordのサポートチャンネル +HTML - LINK_DOWNLOAD || ダウンロード +HTML - LINK_ISSUES || バグ報告 +HTML - LINK_NIGHT_MODE || ナイトモード +HTML - LINK_PLAYER_PAGE || Player Page +HTML - LINK_QUICK_VIEW || クイックビュー +HTML - LINK_SERVER_ANALYSIS || サーバーの分析結果 +HTML - LINK_WIKI || 「Plan」のwiki,チュートリアルとドキュメント HTML - LOCAL_MACHINE || ローカルマシン -HTML - LONGEST || 最長 -HTML - LOW_TPS_SPIKES || 最低TPS値 -HTML - MOB_CAUSED_DEATHS || Mobによって殺された回数 -HTML - MOB_KDR || Mobに対してのKDR -HTML - MOB_KILLS || Mobを殺した回数 -HTML - MOST_RECENT_SESSIONS || 最近のログイン -HTML - NAME || 名前 -HTML - NAV_COMMAND_USAGE || コマンド使用履歴 -HTML - NAV_GEOLOCATIONS || 地域 -HTML - NAV_INFORMATION || 概要 -HTML - NAV_NETWORK_PLAYERS || ネットワーク内のプレイヤー数 -HTML - NAV_ONLINE_ACTIVITY || 活動履歴 -HTML - NAV_OVERVIEW || 概要 -HTML - NAV_PERFORMANCE || パフォーマンス -HTML - NAV_PLAYERS || プレイヤー HTML - NAV_PLUGINS || プラグイン -HTML - NAV_SESSIONS || 接続履歴 -HTML - NAV_SEVER_HEALTH || サーバーの健康状態 -HTML - NETWORK || ネットワーク -HTML - NETWORK_INFORMATION || ネットワーク内の情報 -HTML - NEW || 新規 HTML - NEW_CALENDAR || New: -HTML - NEW_PLAYERS_TEXT || 新規プレイヤー -HTML - NEW_RETENTION || 新規プレイヤーの継続率 -HTML - NEW_TEXT || 新規 -HTML - NICKNAME || ニックネーム HTML - NO_KILLS || プレイヤーキルなし -HTML - NO_PLAYER_CAUSED_DEATHS || プレイヤーによるキルなし HTML - OFFLINE || オフライン HTML - ONLINE || オンライン -HTML - ONLINE_ACTIVITY || 活動指数 -HTML - OPERATOR || 管理者 -HTML - OVERVIEW || 概要 HTML - PER_DAY || /日 -HTML - PLAYER_CAUSED_DEATHS || プレイヤーによるキル -HTML - PLAYER_KILLS || プレイヤーキル -HTML - PLAYER_LIST || プレイヤー一覧 -HTML - PLAYERBASE_DEVELOPMENT || 登録されているプレイヤーの推移 -HTML - PLAYERS || プレイヤー -HTML - PLAYERS_ONLINE || オンラインのプレイヤー -HTML - PLAYERS_ONLINE_TEXT || オンラインのプレイヤー HTML - PLAYERS_TEXT || プレイヤー -HTML - PLAYTIME || プレイ時間 -HTML - PLEASE_WAIT || 少々お待ち下さい・・・ -HTML - PREDICETED_RETENTION || 推定される継続率 -HTML - PUNCH_CARD || パンチボード -HTML - PUNCHCARD || パンチボード -HTML - RECENT_LOGINS || 最近のログイン -HTML - REGISTERED || 登録日 -HTML - REGISTERED_TEXT || 登録 -HTML - REGULAR || よくいる -HTML - SEEN_NICKNAMES || ニックネーム一覧 -HTML - SERVER || サーバー -HTML - SERVER_ANALYSIS || サーバーの分析結果 -HTML - SERVER_HEALTH_ESTIMATE || サーバーの健康状態 -HTML - SERVER_INFORMATION || サーバーの情報 -HTML - SERVER_PREFERENCE || サーバー毎のプレイ時間 -HTML - SERVERS || 接続されているサーバー HTML - SESSION || オンライン -HTML - SESSION_ENDED || ログアウト時刻 -HTML - SESSION_LENGTH || 最長オンライン -HTML - SESSION_MEDIAN || 平均オンライン -HTML - SESSIONS || 接続履歴 -HTML - TIME || 時刻 -HTML - TIMES_KICKED || キック回数 -HTML - TIMES_USED || 使用回数 +HTML - SIDE_GEOLOCATIONS || 地域 +HTML - SIDE_INFORMATION || インフォメーション +HTML - SIDE_NETWORK_OVERVIEW || ネットワーク概要 +HTML - SIDE_OVERVIEW || 概要 +HTML - SIDE_PERFORMANCE || パフォーマンス +HTML - SIDE_PLAYER_LIST || プレイヤー一覧 +HTML - SIDE_PLAYERBASE || プレイヤーベース +HTML - SIDE_PLAYERBASE_OVERVIEW || プレイヤーベース概要 +HTML - SIDE_PLUGINS || プラグイン +HTML - SIDE_PVP_PVE || PvPとPvE +HTML - SIDE_SERVERS || 接続されているサーバー +HTML - SIDE_SERVERS_TITLE || 接続されているサーバー +HTML - SIDE_SESSIONS || 接続履歴 +HTML - SIDE_TO_MAIN_PAGE || メインページに戻る +HTML - TEXT_CLICK_TO_EXPAND || クリックして展開 +HTML - TEXT_CONTRIBUTORS_CODE || プログラミング貢献者: +HTML - TEXT_CONTRIBUTORS_LOCALE || 翻訳者: +HTML - TEXT_CONTRIBUTORS_MONEY || このプラグインの開発に募金して頂いた人々に特別な感謝を +HTML - TEXT_CONTRIBUTORS_THANKS || 加えて、次の素晴らしい人々が貢献しました: +HTML - TEXT_DEV_VERSION || このバージョンは開発版です +HTML - TEXT_DEVELOPED_BY || 開発者: +HTML - TEXT_FIRST_SESSION || First session +HTML - TEXT_LICENSED_UNDER || のライセンスの下で開発されています +HTML - TEXT_METRICS || 「bStats Metrics」が有効になっています +HTML - TEXT_NO_EXTENSION_DATA || 「Extension Data」が存在しません +HTML - TEXT_NO_LOW_TPS || TPSの低下が存在しません +HTML - TEXT_NO_SERVER || No server to display online activity for +HTML - TEXT_NO_SERVERS || No servers found in the database +HTML - TEXT_PLUGIN_INFORMATION || プラグインに関する情報 +HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players +HTML - TEXT_VERSION || 新しいバージョンがリリースされ、ダウンロード可能です +HTML - TITLE_30_DAYS || 30日間 +HTML - TITLE_30_DAYS_AGO || 30日前 +HTML - TITLE_ALL || 全て +HTML - TITLE_ALL_TIME || 全体 +HTML - TITLE_AS_NUMBERS || の情報 +HTML - TITLE_AVG_PING || 平均Ping値 +HTML - TITLE_BEST_PING || 最高Ping値 +HTML - TITLE_CALENDAR || カレンダー +HTML - TITLE_CONNECTION_INFO || 接続情報 +HTML - TITLE_COUNTRY || 国/地域 +HTML - TITLE_CPU_RAM || CPUとメモリー +HTML - TITLE_CURRENT_PLAYERBASE || ログインプレイヤー +HTML - TITLE_DISK || ドライブの容量 +HTML - TITLE_GRAPH_DAY_BY_DAY || 詳細情報 +HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || ネットワーク内の接続状況 +HTML - TITLE_GRAPH_PUNCHCARD || 直近30日間のパンチボード +HTML - TITLE_INSIGHTS || 直近30日間の +HTML - TITLE_IS_AVAILABLE || が利用可能です +HTML - TITLE_LAST_24_HOURS || 直近24時間以内 +HTML - TITLE_LAST_30_DAYS || 直近30日以内 +HTML - TITLE_LAST_7_DAYS || 一週間以内 +HTML - TITLE_LAST_CONNECTED || 直近の接続 +HTML - TITLE_LENGTH || 長さ +HTML - TITLE_MOST_PLAYED_WORLD || よくプレイしているワールド +HTML - TITLE_NETWORK || ネットワーク +HTML - TITLE_NETWORK_AS_NUMBERS || ネットワーク数 +HTML - TITLE_NOW || 今 +HTML - TITLE_ONLINE_ACTIVITY || 接続状況 +HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || 接続状況の情報 +HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || 接続状況の概要 +HTML - TITLE_PERFORMANCE_AS_NUMBERS || パフォーマンスの情報 +HTML - TITLE_PING || Ping +HTML - TITLE_PLAYER || プレイヤー +HTML - TITLE_PLAYER_OVERVIEW || プレイヤーの概要 +HTML - TITLE_PLAYERBASE_DEVELOPMENT || 登録されているプレイヤーの推移 +HTML - TITLE_PVP_DEATHS || 最近のPVPによる死亡 +HTML - TITLE_PVP_KILLS || 最近のPVPによるキル +HTML - TITLE_PVP_PVE_NUMBERS || PVPとPvEの情報 +HTML - TITLE_RECENT_KILLS || 最近のキル +HTML - TITLE_RECENT_SESSIONS || 最近のログイン +HTML - TITLE_SEEN_NICKNAMES || ニックネーム一覧 +HTML - TITLE_SERVER || サーバー +HTML - TITLE_SERVER_AS_NUMBERS || サーバー数 +HTML - TITLE_SERVER_OVERVIEW || サーバーの概要 +HTML - TITLE_SERVER_PLAYTIME || サーバーのプレイ時間 +HTML - TITLE_SERVER_PLAYTIME_30 || 直近30日間のサーバーのプレイ時間 +HTML - TITLE_SESSION_START || 接続開始時間 +HTML - TITLE_THEME_SELECT || テーマ選択 +HTML - TITLE_TITLE_PLAYER_PUNCHCARD || パンチカード +HTML - TITLE_TPS || TPS +HTML - TITLE_TREND || トレンド +HTML - TITLE_TRENDS || 直近30日間でのトレンド +HTML - TITLE_VERSION || バージョン +HTML - TITLE_WEEK_COMPARISON || 直近1周間での比較 +HTML - TITLE_WORLD || ワールドのロード数 +HTML - TITLE_WORLD_PLAYTIME || ワールドごとのプレイ時間 +HTML - TITLE_WORST_PING || 最低Ping値 HTML - TOTAL_ACTIVE_TEXT || 累計活動時間 HTML - TOTAL_AFK || 累計離席時間 HTML - TOTAL_PLAYERS || 全プレイヤー数 -HTML - TOTAL_PLAYTIME || 全プレイ時間 -HTML - UNIQUE || ユニーク HTML - UNIQUE_CALENDAR || ユニーク: -HTML - UNIQUE_PLAYERS || ユニークプレイヤー -HTML - UNIQUE_PLAYERS_TEXT || ユニークプレイヤー -HTML - UNIQUE_TEXT || ユニーク -HTML - USAGE || 使用履歴 -HTML - USED_COMMANDS || 使用したコマンド +HTML - UNIT_CHUNKS || Chunks +HTML - UNIT_ENTITIES || Entities +HTML - UNIT_NO_DATA || データなし +HTML - UNIT_THE_PLAYERS || プレイヤー HTML - USER_AND_PASS_NOT_SPECIFIED || ユーザーとパスワードが入力されてません HTML - USER_DOES_NOT_EXIST || 入力されたユーザーは存在しません -HTML - USER_INFORMATION || ユーザー情報 HTML - USER_PASS_MISMATCH || 入力されたユーザー名とパスワードが間違っています HTML - WITH ||
    死亡原因 -HTML - WORLD || ワールド -HTML - WORLD_LOAD || ワールドロード数 -HTML - WORLD_PLAYTIME || ワールドごとのプレイ時間 -HTML - WORST_PING || 最高応答時間 HTML ERRORS - ACCESS_DENIED_403 || アクセスが拒否されました -HTML ERRORS - ANALYSIS_REFRESH || 分析結果に基づいてデータを更新中です・・・ -HTML ERRORS - ANALYSIS_REFRESH_LONG || サーバーを分析中です・・・・
    数秒後にページが更新されない場合、ページを更新して下さい HTML ERRORS - AUTH_FAIL_TIPS_401 || - 登録したユーザーを「/plan register 」で確認できます。
    - 入力したユーザー名とパスワードが正しいことを確認して下さい
    - ユーザー名とパスワードは大文字と小文字が区別されています

    パスワードを忘れた場合は、管理者に古いユーザーを削除して新しくユーザーを再登録するよう依頼して下さい HTML ERRORS - AUTHENTICATION_FAILED_401 || 認証に失敗しました HTML ERRORS - FORBIDDEN_403 || 閲覧禁止 -HTML ERRORS - INSPECT_REFRESH || プレーヤページのリクエスト処理が実行中です・・ -HTML ERRORS - INSPECT_REFRESH_LONG || ページは自動的に更新されます・・ HTML ERRORS - NO_SERVERS_404 || リクエストを処理するサーバーがオンラインではありません HTML ERRORS - NOT_FOUND_404 || ページが見つかりませんでした HTML ERRORS - NOT_PLAYED_404 || プレイヤーはこのサーバーでプレイしていません HTML ERRORS - PAGE_NOT_FOUND_404 || ページは存在しません -HTML ERRORS - PLUGIN_TAB_REFRESH || 計算中です・・・ HTML ERRORS - UNAUTHORIZED_401 || 未認証状態です HTML ERRORS - UNKNOWN_PAGE_404 || リンクが間違っています、コマンドを使用してURLを確認して下さい。 URL例:

    /player/PlayerName
    /server/ServerName

    HTML ERRORS - UUID_404 || データベース内にプレヤーのUUIDが存在しません @@ -279,15 +306,13 @@ In Depth Help - /plan inspect ? || > §2検査コマンド\ プ In Depth Help - /plan manage ? || > §2管理コマンド\ MySQLまたはSQLiteの「Plan」のデータベースを管理します\ §2/plan m §f補助コマンドの一覧を表示します\ §2/plan m ? §f詳細なヘルプを表示します In Depth Help - /plan manage backup ? || > §2バックアップコマンド\ 現在アクティブになっているデータベースの内容を含むSQLiteの新しいデータベースを「Plan」のプラグインフォルダ内に作成します(拡張子は「.db」です) In Depth Help - /plan manage clear ? || > §2クリア補助コマンド\ アクティブになっているデータベースの全てを削除します。使用には注意が必要です -In Depth Help - /plan manage con ? || > §2接続デバッグ補助コマンド\ ネットワーク内の接続をデバッグするために使用します\ データベース内の各サーバーにリクエストを送信します。 -In Depth Help - /plan manage disable ? || > §2無効補助コマンド\ 次回のリロードまでプラグインの一部を無効にすることができます\ 使用できる引数:\ §2キックカウント §fシャットダウンマクロで「/kickball」が使用されている場合の無効化されたキックカウント。 +In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. In Depth Help - /plan manage export ? || > §2エクスポート補助コマンド\ 分析結果を特定のフォルダーへのエクスポートを有効化します\ 使用できる引数:\ §2list §fList possible arguments.\ §2players §fExport /players, /player pages + /player/raw json depending on config values.\ §2server_json §fExport /server/raw JSON if enabled in config. In Depth Help - /plan manage import ? || > §2インポート補助コマンド\ 別のソースからデータをインポートします\ 使用できる引数:\ §2オフライン §fBukkitのプレイヤーデータ、登録日と名前のみを登録できます In Depth Help - /plan manage move ? || > §2移動補助コマンド\ SQLiteからMySQLもしくは他の形式にデータを移行させます\ ターゲットとなるデータベースは移行前に空である必要があります In Depth Help - /plan manage raw ? || > §2生のデータ補助コマンド\ 生のJSONデータページへのリンクを表示します\ 「Plan」の内部ウェブサーバーが有効になっていない場合は利用できません In Depth Help - /plan manage remove ? || > §2削除補助コマンド\ プレイヤーのデータをアクティブになっているデータベースから削除します In Depth Help - /plan manage restore ? || > §2復元補助コマンド\ データベースを以前にバックアップしたSQLiteデータベース(拡張子が「.db」のファイル)に復元します\ 他のサーバーの拡張子が「.db」のデータベースをMySQLに復元することもできます\ ターゲットとなるデータベースは移行前に空である必要があります -In Depth Help - /plan manage setup ? || > §2セットアップ補助コマンド\ 「ネットワーク」のページを機能させるために、BungeeCordとこのBukkit/Spigotサーバーの間の接続を設定します.\ BungeeCordのアドレスはBungeeCordで「Plan」が有効化された時のログで見つけることができます。 In Depth Help - /plan manage uninstalled ? || > §2サーバーアンインストール補助コマンド\ 指定されたサーバーをデータベースからアンインストールします\ コマンドが実行されているサーバーをアンインストールするサーバーとしてマークすることはできません\ サーバーの接続システムに影響します In Depth Help - /plan network ? || > §2ネットワークコマンド\ 「ネットワーク」のページのURLを表示します\ BungeeCordのネットワーク上にない場合は、このページにサーバーページが表示されます。 In Depth Help - /plan players ? || > §2プレイヤーコマンド\ 「プレイヤー」のページのURLを表示します @@ -298,7 +323,6 @@ In Depth Help - /plan servers ? || > §2サーバーコマンド In Depth Help - /plan web ? || < §2ウェブユーザー管理コマンド\ §2/plan web §f補助コマンドの一覧を表示します\ §2/plan web ? §f詳細なヘルプを表示します In Depth Help - /plan web register ? || > §2登録コマンド\ 新しいウェブユーザーを登録します。\ プレイヤーが他のプレイヤーをユーザー登録するには「plan.webmanage」権限が必要です\ パスワードはPBKDF2(64,000回のSHA1の繰り返し)でソルト(暗号論)をかけてハッシュされます。 In Depth Help - /planbungee disable ? || > §2無効化コマンド\ BungeeCordの「Plan」で「onDisable」を実行します\ あとで「/planbungee reload」を使ってプラグインを有効にすることができます\ §bその状態で「Swapping jar」は対応していません -In Depth Help - /planbungee setup ? || > §2セットアップ切り替えコマンド\ BungeeCordでセットアップモードを切り替えます\ 許可されていない他のサーバーがMySQLを詮索するのを防ぎます。 Manage - Confirm Overwrite || ${0}のデータは上書きされます! Manage - Confirm Removal || ${0}のデータは削除されます! Manage - Fail || > §c何かがうまくいきませんでした: ${0} @@ -310,20 +334,14 @@ Manage - Fail No Server || 指定されたパラメー Manage - Fail Same Database || > §c同じデータベースを操作することはできません! Manage - Fail Same server || このサーバーをアンインストールするサーバーとして指定することはできません(あなたがこのサーバーにログインしているため) Manage - Fail, Confirmation || > §c実行を確認するために引数「-a」を追加します: ${0} -Manage - Fail, Connection Exception || §e失敗した理由: -Manage - Fail, No Servers || §cデータベース内にサーバーが存在しませんでした -Manage - Fail, Old version || §e失敗した理由:受信サーバーで実行されている「Plan」のバージョンが古いです -Manage - Fail, Unauthorized || §e失敗した理由:権限制限があります。サーバーが別のデータベースを使用している可能性があります -Manage - Fail, Unexpected Exception || §e不明な例外: ${0} -Manage - List Importers || インポーター: -Manage - Notify External Url || §e非ローカルアドレスです、接続先のポートが開放されていることを確認して下さい +Manage - List Importers || インポーター: Manage - Remind HotSwap || §e新しいデータベースに交換することを忘れないで下さい(/plan m hotswap ${0})。そして、プラグインをリロードして下さい Manage - Start || > §2データを処理中です.. Manage - Success || > §a成功しました! Negative || ない Positive || ある Today || '今日' -Yesterday || '昨日' +Unavailable || Unavailable Unknown || 不明 Version - DEV || このバージョンは開発版です Version - Latest || 最新版の「Plan」を使用しています @@ -338,3 +356,4 @@ WebServer - Notify no Cert file || Webサーバー: 以下のパ WebServer FAIL - Port Bind || Webサーバーの初期化が正常に終了しませんでした。ポート番号(${0})は使用されていませんか? WebServer FAIL - SSL Context || Webサーバー: SSLコンテキストの初期化に失敗しました。 WebServer FAIL - Store Load || Webサーバー: SSL証明書のロードに失敗しました +Yesterday || '昨日' diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_PT-BR.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_PT_BR.txt similarity index 54% rename from Plan/common/src/main/resources/assets/plan/locale/locale_PT-BR.txt rename to Plan/common/src/main/resources/assets/plan/locale/locale_PT_BR.txt index 449b072c5..5468c918e 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_PT-BR.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_PT_BR.txt @@ -1,6 +1,7 @@ Cmd - Click Me || Clique aqui Cmd - Link || §2Link: §f Cmd Disable - Disabled || §aO sistema do Plan agora está desativado. Você pode usar /planbungee reload para reiniciar o plugin. +Cmd FAIL - Database not open || §cDatabase is ${0} - Please try again a bit later. Cmd FAIL - Invalid Username || §cEsse usuário não tem uma UUID. Cmd FAIL - No Feature || §eDefina um recurso para desativar! (atualmente suporta ${0}) Cmd FAIL - No Permission || §cVocê não tem a permissão necessária. @@ -35,29 +36,9 @@ Cmd Qinspect - Player Kills || §2Assassinato de Jogadores Cmd Qinspect - Playtime || §2Tempo de Jogo: §f${0} Cmd Qinspect - Registered || §2Registered: §f${0} Cmd Qinspect - Times Kicked || §2Vezes Kickado: §f${0} -Cmd Setup - Allowed || §aA configuração agora é permitida -Cmd Setup - Bad Request || §eA conexão foi bem sucedida, mas o servidor de recebimento não era um servidor Bungee. Utilize o endereço de IP do Bungee. -Cmd Setup - Disallowed || §cA configuração de instalação agora foi bloqueada -Cmd Setup - Forbidden || §eA conexão foi bem sucedida, mas o Bungee desativou o modo de configuração - utilize '/planbungee setup' para ativá-lo. -Cmd Setup - Gateway Error || §eA conexão foi bem sucedida, mas o Bungee não conseguiu se conectar a esse servidor (O servidor web atual foi reiniciado?). Utilize /plan m con & /planbungee con para depurar. -Cmd Setup - Generic Fail || §eConexão falhou: ${0} -Cmd Setup - Internal Error || §eConexão bem sucedida. ${0}, verifique se possível o ErrorLog que está recebendo nos servidores na página de depuração. -Cmd Setup - Success || §aConexão bem sucedida, o Plan pode reiniciar em alguns segundos.. -Cmd Setup - Unauthorized || §eA conexão foi bem sucedida, mas o servidor de recebimento não autorizou esse servidor. Entre em contato por Discord para receber suporte. -Cmd Setup - Url mistake || §cVerifique se você está usando o endereço completo (que começa com http:// ou https://) - verifique no console do Bungee se está recebendo o endereço corretamente. -Cmd Setup - WebServer not Enabled || §cO servidor web não está habilitado nesse servidor! Certifique-se que essa opção esteja habilitada no arquivo de configuração do Plan! Cmd SUCCESS - Feature disabled || §aDesativado '${0}' temporariamente até o próximo reload do plugin. Cmd SUCCESS - WebUser register || §aFoi adicionado um novo usuário (${0})! Você pode ver o painel da web no link a seguir. -Cmd Update - Cancel Success || §aCancelar operação realizada. -Cmd Update - Cancelled || §cAtualização cancelada. -Cmd Update - Change log || Change Log v${0}: -Cmd Update - Fail Cacnel || §cA atualização falhou em um dos servidores, cancelando a atualização em todos os servidores.. -Cmd Update - Fail Force Notify || §e${0} falhou ao atualizar, utilize -force, para continuar com a atualização. -Cmd Update - Fail Not Online || §cNem todos os servidores estavam online ou acessíveis, você ainda pode atualizar os servidores disponíveis usando /plan update -u -force -Cmd Update - Notify Cancel || §aVocê pode cancelar a atualização dos servidores que não foram reiniciados ainda com /plan update cancel. -Cmd Update - Online Check || Verificando se todos os servidores estão online.. -Cmd Update - Scheduled || §a${0} agendado para atualização. -Cmd Update - Url mismatch || §cO url de download não começou com ${0} e pode ser que não seja confiável. Você pode baixar essa versão manualmente aqui (Download Direto): +Cmd WARN - Database not open || §eDatabase is ${0} - This might take longer than expected.. Cmd Web - Permission Levels || >\§70: Acesse todas as páginas\§71: Acesse '/players' e todas as páginas de jogadores\§72: Acesse a página de jogado com o mesmo usuário do login\§73+: Sem permissões Command Help - /plan analyze || Visualizar a página do servidor Command Help - /plan dev || Comando do modo de desenvolvimento @@ -67,14 +48,15 @@ Command Help - /plan inspect || Visualizar a página do jogad Command Help - /plan manage || Gerenciar o banco de dados do Plan Command Help - /plan manage backup || Fazer backup do banco de dados Command Help - /plan manage clear || Limpar o banco de dados -Command Help - /plan manage con || Depurar as conexões Servidor-Bungee Command Help - /plan manage disable || Desativar um recurso temporariamente +Command Help - /plan manage export || Trigger export manually Command Help - /plan manage hotswap || Alterar o banco de dados rapidamente Command Help - /plan manage import || Importar dados de outro lugar Command Help - /plan manage move || Mover dados entre banco de dados +Command Help - /plan manage raw || View raw JSON of player data Command Help - /plan manage remove || Remvoer dados de jogadores Command Help - /plan manage restore || Restaurar último backup realizado -Command Help - /plan manage setup || Configurar uma conexão Servidor-Bungee +Command Help - /plan manage uninstalled || Mark a server as uninstalled in the database. Command Help - /plan network || Visualizar a página da Network Command Help - /plan players || Visualizar a página de Jogadores Command Help - /plan qinspect || Visualziar informações do Jogador in-game @@ -82,15 +64,12 @@ Command Help - /plan register || Registrar um usuário web Command Help - /plan reload || Reiniciar Plan Command Help - /plan search || Buscar por um nome de jogador Command Help - /plan servers || Listar servidores do banco de dados -Command Help - /plan update || Pegar o link de Changelog ou atualização do plugin Command Help - /plan web check || Inspecionar um usuário web Command Help - /plan web delete || Excluir um usuário web Command Help - /plan web level || Informações sobre os níveis de permissão Command Help - /plan web list || Listar usuários web Command Help - /plan webuser || Gerenciar usuários web -Command Help - /planbungee con || Depurar conexões Bungee-Servidor Command Help - /planbungee disable || Desativar o plugin temporariamente -Command Help - /planbungee setup || Alternar o modo de configuração Database - Apply Patch || Aplicando o Patch: ${0}.. Database - Patches Applied || Todos os patchs de bancos de dados foram aplicados. Database - Patches Applied Already || Todos os patchs de bancos de dados já foram aplicados. @@ -100,10 +79,10 @@ Database Notify - SQLite No WAL || O modo WAL do SQLite não é Disable || Análise de Jogadores Desativado. Disable - Processing || Processando tarefas críticas não processadas anteriormente. (${0}) Disable - Processing Complete || Processamento completo. +Disable - Unsaved Session Save || Saving unfinished sessions.. Disable - WebServer || O servidor web foi desativado. Enable || Análise de Jogadores Ativado. Enable - Database || ${0}-conexão com o banco de dados estabilizada. -Enable - Notify Address Confirmation || Tenha certeza que esse endereço está apontando para ESSE servidor: ${0} Enable - Notify Empty IP || O IP no server.properties está vazio & o IP alternativo não está sendo usado. Os dados informados estão incorretos! Enable - Notify Geolocations disabled || A coleta de geolocalização está desativada. (Data.Geolocations: false) Enable - Notify Geolocations Internet Required || O Plan requer acesso à internet na primeira execução para baixar o banco de dados de geolocalização do GeoLite2. @@ -112,138 +91,205 @@ Enable - WebServer || O servidor web está rodando Enable FAIL - Database || ${0}-Falha na Conexão do Banco de Dados: ${1} Enable FAIL - Database Patch || O patch do banco de dados falhou, o plugin teve que ser desativado. Reporte esse problema no GitHub ou Discord para suporte. Enable FAIL - GeoDB Write || Algo deu errado ao salvar o banco de dados de geolocalização do GeoLite2 -Enable FAIL - WebServer (Bungee) || O servidor web não pode ser inicializado! +Enable FAIL - WebServer (Proxy) || O servidor web não pode ser inicializado! Enable FAIL - Wrong Database Type || ${0} não é um banco de dados suportado -HTML - ACTIVITY_INDEX || Índice de Atividade -HTML - ALL || TODOS -HTML - ALL_TIME_PEAK || Pico Máximo -HTML - AVERAGE_PING || Ping Médio -HTML - AVG || AVG -HTML - BANNED || Banido -HTML - BEST_PING || Melhor Ping -HTML - CALENDAR || CALENDÁRIO -HTML - CALENDAR_TEXT || Calendário -HTML - CHUNKS || Chunks -HTML - COMMAND || Comandos -HTML - COMMNAND_USAGE || Comandos Utilizados -HTML - CONNECTION_INFORMATION || Informações de Conexão -HTML - COUNTRY || País -HTML - CURRENT_PLAYERBASE || Base de Jogadores Atual -HTML - DEATHS || Mortes -HTML - ENTITIES || Entidades +HTML - COMPARING_15_DAYS || Comparing 15 days +HTML - COMPARING_60_DAYS || Comparing 30d ago to Now +HTML - COMPARING_7_DAYS || Comparing 7 days +HTML - DATABASE_NOT_OPEN || Database is not open, check db status with /plan info HTML - ERROR || Falha ao autenticar -HTML - FAVORITE_SERVER || Servidor Favorito -HTML - GEOLOCATION || Geolocalização -HTML - GEOLOCATION_TEXT || Geolocalização -HTML - HEALTH_ESTIMATE || Vida Estimada HTML - INDEX_ACTIVE || Ativo HTML - INDEX_INACTIVE || Inativo HTML - INDEX_IRREGULAR || Irregular HTML - INDEX_REGULAR || Regular HTML - INDEX_VERY_ACTIVE || Muito Ativo -HTML - IP_ADDRESS || Endereço IP HTML - KILLED || Assassinou -HTML - KILLED_BY || Morto por -HTML - LAST_24_HOURS || ÚLTIMAS 24 HORAS -HTML - LAST_30_DAYS || ÚLTIMOS 30 DIAS -HTML - LAST_30_DAYS_TEXT || Últimos 30 Dias -HTML - LAST_7_DAYS || ÚLTIMOS 7 DIAS -HTML - LAST_CONNECTED || Última Conexão -HTML - LAST_PEAK || Último Pico -HTML - LAST_SEEN || ÚLTIMA VEZ VISTO -HTML - LAST_SEEN_TEXT || Última Vez Visto -HTML - LOADED_CHUNKS || Chunks Carregados -HTML - LOADED_ENTITIES || Entidades Carregadas +HTML - LABEL_1ST_WEAPON || Deadliest PvP Weapon +HTML - LABEL_2ND_WEAPON || 2nd PvP Weapon +HTML - LABEL_3RD_WEAPON || 3rd PvP Weapon +HTML - LABEL_ACTIVITY_INDEX || Índice de Atividade +HTML - LABEL_AFK || AFK +HTML - LABEL_AFK_TIME || AFK Time +HTML - LABEL_AVG || Average +HTML - LABEL_AVG_KDR || Average KDR +HTML - LABEL_AVG_MOB_KDR || Average Mob KDR +HTML - LABEL_AVG_PLAYTIME || Average Playtime +HTML - LABEL_AVG_SESSION_LENGTH || Average Session Length +HTML - LABEL_AVG_TPS || Average TPS +HTML - LABEL_BANNED || Banido +HTML - LABEL_BEST_PEAK || Pico Máximo +HTML - LABEL_DAY_OF_WEEK || Day of the Week +HTML - LABEL_DEATHS || Mortes +HTML - LABEL_DOWNTIME || Downtime +HTML - LABEL_DURING_LOW_TPS || During Low TPS Spikes: +HTML - LABEL_ENTITIES || Entidades +HTML - LABEL_FAVORITE_SERVER || Servidor Favorito +HTML - LABEL_FIRST_SESSION_LENGTH || First session length +HTML - LABEL_FREE_DISK_SPACE || Espaço de Disco Livre +HTML - LABEL_INACTIVE || Inactive +HTML - LABEL_LAST_PEAK || Último Pico +HTML - LABEL_LAST_SEEN || Última Vez Visto +HTML - LABEL_LOADED_CHUNKS || Chunks Carregados +HTML - LABEL_LOADED_ENTITIES || Entidades Carregadas +HTML - LABEL_LONE_JOINS || Lone joins +HTML - LABEL_LONE_NEW_JOINS || Lone newbie joins +HTML - LABEL_LONGEST_SESSION || Longest Session +HTML - LABEL_LOW_TPS || Low TPS Spikes +HTML - LABEL_MAX_FREE_DISK || Max Free Disk +HTML - LABEL_MIN_FREE_DISK || Min Free Disk +HTML - LABEL_MOB_DEATHS || Mortes causadas por Mobs +HTML - LABEL_MOB_KDR || KDR por Mob +HTML - LABEL_MOB_KILLS || Assassinato de Mobs +HTML - LABEL_MOST_ACTIVE_GAMEMODE || Most Active Gamemode +HTML - LABEL_NAME || Nome +HTML - LABEL_NEW || New +HTML - LABEL_NEW_PLAYERS || Novos Jogadores +HTML - LABEL_NICKNAME || Nick +HTML - LABEL_NO_SESSION_KILLS || None +HTML - LABEL_ONLINE_FIRST_JOIN || Players online on first join +HTML - LABEL_OPERATOR || Operador +HTML - LABEL_PER_PLAYER || / Player +HTML - LABEL_PER_REGULAR_PLAYER || / Regular Player +HTML - LABEL_PLAYER_DEATHS || Mortes causadas por Jogadores +HTML - LABEL_PLAYER_KILLS || Assassinato +HTML - LABEL_PLAYERS_ONLINE || Jogadores Online +HTML - LABEL_PLAYTIME || Tempo de Jogo +HTML - LABEL_REGISTERED || Registrados +HTML - LABEL_REGISTERED_PLAYERS || Registered Players +HTML - LABEL_REGULAR || Regular +HTML - LABEL_REGULAR_PLAYERS || Regular Players +HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity +HTML - LABEL_RETENTION || Retenção de Novos Jogadores +HTML - LABEL_SERVER_DOWNTIME || Server Downtime +HTML - LABEL_SERVER_OCCUPIED || Server occupied +HTML - LABEL_SESSION_ENDED || Sessões Finalizadas +HTML - LABEL_SESSION_MEDIAN || Média de Sessões +HTML - LABEL_TIMES_KICKED || Vezes Kickado +HTML - LABEL_TOTAL_PLAYERS || Total Players +HTML - LABEL_TOTAL_PLAYTIME || Total Playtime +HTML - LABEL_UNIQUE_PLAYERS || Jogadores Únicos +HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' +HTML - LINK_BACK_NETWORK || Network page +HTML - LINK_BACK_SERVER || Server page +HTML - LINK_CHANGELOG || View Changelog +HTML - LINK_DISCORD || General Support on Discord +HTML - LINK_DOWNLOAD || Download +HTML - LINK_ISSUES || Report Issues +HTML - LINK_NIGHT_MODE || Night Mode +HTML - LINK_PLAYER_PAGE || Player Page +HTML - LINK_QUICK_VIEW || Quick view +HTML - LINK_SERVER_ANALYSIS || Análise do Servidor +HTML - LINK_WIKI || Plan Wiki, Tutorials & Documentation HTML - LOCAL_MACHINE || Máquina Local -HTML - LONGEST || Maior Tempo -HTML - LOW_TPS_SPIKES || Picos Baixos do TPS -HTML - MOB_CAUSED_DEATHS || Mortes causadas por Mobs -HTML - MOB_KDR || KDR por Mob -HTML - MOB_KILLS || Assassinato de Mobs -HTML - MOST_RECENT_SESSIONS || Sessões Mais Recentes -HTML - NAME || Nome -HTML - NAV_COMMAND_USAGE || Comandos Utilizados -HTML - NAV_GEOLOCATIONS || Geolocalizações -HTML - NAV_INFORMATION || Informações -HTML - NAV_NETWORK_PLAYERS || Jogadores da Network -HTML - NAV_ONLINE_ACTIVITY || Atividade Online -HTML - NAV_OVERVIEW || Visão Global -HTML - NAV_PERFORMANCE || Desempenho -HTML - NAV_PLAYERS || Jogadores HTML - NAV_PLUGINS || Plugins -HTML - NAV_SESSIONS || Sessões -HTML - NAV_SEVER_HEALTH || Vida do Servidor -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || INFORMAÇÕES DA NETWORK -HTML - NEW || NOVO HTML - NEW_CALENDAR || Novo: -HTML - NEW_PLAYERS_TEXT || Novos Jogadores -HTML - NEW_RETENTION || Retenção de Novos Jogadores -HTML - NEW_TEXT || Novo -HTML - NICKNAME || Nick HTML - NO_KILLS || Sem Kills -HTML - NO_PLAYER_CAUSED_DEATHS || Nenhum jogador lhe matou HTML - OFFLINE || Offline HTML - ONLINE || Online -HTML - ONLINE_ACTIVITY || ATIVIDADE ONLINE -HTML - OPERATOR || Operador -HTML - OVERVIEW || VISÃO GLOBAL HTML - PER_DAY || / Dia -HTML - PLAYER_CAUSED_DEATHS || Mortes causadas por Jogadores -HTML - PLAYER_KILLS || Assassinato -HTML - PLAYER_LIST || Lista de Jogadores -HTML - PLAYERBASE_DEVELOPMENT || Desenvolvimento da base de Jogadores -HTML - PLAYERS || JOGADORES -HTML - PLAYERS_ONLINE || JOGADORES ONLINE -HTML - PLAYERS_ONLINE_TEXT || Jogadores Online HTML - PLAYERS_TEXT || Jogadores -HTML - PLAYTIME || Tempo de Jogo -HTML - PLEASE_WAIT || Por favor, aguarde... -HTML - PREDICETED_RETENTION || Retenção Prevista -HTML - PUNCH_CARD || Cartão Perfurado -HTML - PUNCHCARD || CARTÃO PERFURADO -HTML - RECENT_LOGINS || LOGINS RECENTES -HTML - REGISTERED || REGISTRADOS -HTML - REGISTERED_TEXT || Registrados -HTML - REGULAR || REGULARES -HTML - SEEN_NICKNAMES || Nicks Vistos -HTML - SERVER || Servidor -HTML - SERVER_ANALYSIS || Análise do Servidor -HTML - SERVER_HEALTH_ESTIMATE || Estimativa de Integridade do Servidor -HTML - SERVER_INFORMATION || INFORMAÇÕES DO SERVIDOR -HTML - SERVER_PREFERENCE || Preferência de servidor -HTML - SERVERS || Servidores HTML - SESSION || Sessão -HTML - SESSION_ENDED || Sessões Finalizadas -HTML - SESSION_LENGTH || Tempo de Sessões -HTML - SESSION_MEDIAN || Média de Sessões -HTML - SESSIONS || Sessões -HTML - TIME || Tempo -HTML - TIMES_KICKED || Vezes Kickado -HTML - TIMES_USED || Vezes Usados +HTML - SIDE_GEOLOCATIONS || Geolocalizações +HTML - SIDE_INFORMATION || INFORMATION +HTML - SIDE_NETWORK_OVERVIEW || Network Overview +HTML - SIDE_OVERVIEW || Visão Global +HTML - SIDE_PERFORMANCE || Desempenho +HTML - SIDE_PLAYER_LIST || Lista de Jogadores +HTML - SIDE_PLAYERBASE || Playerbase +HTML - SIDE_PLAYERBASE_OVERVIEW || Playerbase Overview +HTML - SIDE_PLUGINS || PLUGINS +HTML - SIDE_PVP_PVE || PvP & PvE +HTML - SIDE_SERVERS || Servidores +HTML - SIDE_SERVERS_TITLE || SERVERS +HTML - SIDE_SESSIONS || Sessões +HTML - SIDE_TO_MAIN_PAGE || to main page +HTML - TEXT_CLICK_TO_EXPAND || Click to expand +HTML - TEXT_CONTRIBUTORS_CODE || code contributor +HTML - TEXT_CONTRIBUTORS_LOCALE || translator +HTML - TEXT_CONTRIBUTORS_MONEY || Extra special thanks to those who have monetarily supported the development. +HTML - TEXT_CONTRIBUTORS_THANKS || In addition following awesome people have contributed: +HTML - TEXT_DEV_VERSION || This version is a DEV release. +HTML - TEXT_DEVELOPED_BY || is developed by +HTML - TEXT_FIRST_SESSION || First session +HTML - TEXT_LICENSED_UNDER || is developed and licensed under +HTML - TEXT_METRICS || bStats Metrics +HTML - TEXT_NO_EXTENSION_DATA || No Extension Data +HTML - TEXT_NO_LOW_TPS || No low tps spikes +HTML - TEXT_NO_SERVER || No server to display online activity for +HTML - TEXT_NO_SERVERS || No servers found in the database +HTML - TEXT_PLUGIN_INFORMATION || Information about the plugin +HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players +HTML - TEXT_VERSION || A new version has been released and is now available for download. +HTML - TITLE_30_DAYS || 30 days +HTML - TITLE_30_DAYS_AGO || 30 days ago +HTML - TITLE_ALL || Todos +HTML - TITLE_ALL_TIME || All Time +HTML - TITLE_AS_NUMBERS || as Numbers +HTML - TITLE_AVG_PING || Average Ping +HTML - TITLE_BEST_PING || Best Ping +HTML - TITLE_CALENDAR || Calendário +HTML - TITLE_CONNECTION_INFO || Connection Information +HTML - TITLE_COUNTRY || País +HTML - TITLE_CPU_RAM || CPU & RAM +HTML - TITLE_CURRENT_PLAYERBASE || Base de Jogadores Atual +HTML - TITLE_DISK || Espaço de disco +HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day +HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Network Online Activity +HTML - TITLE_GRAPH_PUNCHCARD || Cartão Perfurado for 30 days +HTML - TITLE_INSIGHTS || Insights for 30 days +HTML - TITLE_IS_AVAILABLE || is Available +HTML - TITLE_LAST_24_HOURS || Últimas 24 horas +HTML - TITLE_LAST_30_DAYS || Últimos 30 dias +HTML - TITLE_LAST_7_DAYS || Últimos 7 dias +HTML - TITLE_LAST_CONNECTED || Última Conexão +HTML - TITLE_LENGTH || Length +HTML - TITLE_MOST_PLAYED_WORLD || Most played World +HTML - TITLE_NETWORK || Network +HTML - TITLE_NETWORK_AS_NUMBERS || Network as Numbers +HTML - TITLE_NOW || Now +HTML - TITLE_ONLINE_ACTIVITY || Online Activity +HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || Online Activity as Numbers +HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || Online Activity Overview +HTML - TITLE_PERFORMANCE_AS_NUMBERS || Performance as Numbers +HTML - TITLE_PING || Ping +HTML - TITLE_PLAYER || Player +HTML - TITLE_PLAYER_OVERVIEW || Player Overview +HTML - TITLE_PLAYERBASE_DEVELOPMENT || Desenvolvimento da base de Jogadores +HTML - TITLE_PVP_DEATHS || Recent PvP Deaths +HTML - TITLE_PVP_KILLS || Recent PvP Kills +HTML - TITLE_PVP_PVE_NUMBERS || PvP & PvE as Numbers +HTML - TITLE_RECENT_KILLS || Recent Kills +HTML - TITLE_RECENT_SESSIONS || Sessões Recentes +HTML - TITLE_SEEN_NICKNAMES || Nicks Vistos +HTML - TITLE_SERVER || Servidor +HTML - TITLE_SERVER_AS_NUMBERS || Server as Numbers +HTML - TITLE_SERVER_OVERVIEW || Server Overview +HTML - TITLE_SERVER_PLAYTIME || Server Playtime +HTML - TITLE_SERVER_PLAYTIME_30 || Server Playtime for 30 days +HTML - TITLE_SESSION_START || Session Started +HTML - TITLE_THEME_SELECT || Theme Select +HTML - TITLE_TITLE_PLAYER_PUNCHCARD || Punchcard +HTML - TITLE_TPS || TPS +HTML - TITLE_TREND || Trend +HTML - TITLE_TRENDS || Trends for 30 days +HTML - TITLE_VERSION || Version +HTML - TITLE_WEEK_COMPARISON || Week Comparison +HTML - TITLE_WORLD || World Load +HTML - TITLE_WORLD_PLAYTIME || Tempo de Jogo por Mundo +HTML - TITLE_WORST_PING || Worst Ping HTML - TOTAL_ACTIVE_TEXT || Tempo Total Ativo HTML - TOTAL_AFK || Tempo Total AFK HTML - TOTAL_PLAYERS || Total de Jogadores -HTML - TOTAL_PLAYTIME || Tempo Total Jogado -HTML - UNIQUE || ÚNICOS HTML - UNIQUE_CALENDAR || Únicos: -HTML - UNIQUE_PLAYERS || JOGADORES ÚNICOS -HTML - UNIQUE_PLAYERS_TEXT || Jogadores Únicos -HTML - UNIQUE_TEXT || Único -HTML - USAGE || Uso -HTML - USED_COMMANDS || Comandos Usados +HTML - UNIT_CHUNKS || Chunks +HTML - UNIT_ENTITIES || Entities +HTML - UNIT_NO_DATA || No Data +HTML - UNIT_THE_PLAYERS || Players HTML - USER_AND_PASS_NOT_SPECIFIED || Usuário e Senha não específicado HTML - USER_DOES_NOT_EXIST || Usuário não existe -HTML - USER_INFORMATION || INFORMAÇÕES DO USUÁRIO HTML - USER_PASS_MISMATCH || Usuário e Senha não coincidem HTML - WITH ||
    Com -HTML - WORLD || Mundo -HTML - WORLD_LOAD || MUNDOS CARREGADOS -HTML - WORLD_PLAYTIME || Tempo de Jogo por Mundo -HTML - WORST_PING || Pior Ping HTML ERRORS - ACCESS_DENIED_403 || Acesso Negado -HTML ERRORS - ANALYSIS_REFRESH || A análise está sendo atualizada.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || A análise está sendo executada, atualize a página após alguns segundos.. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Certifique-se de ter registrado um usuário com /plan register
    - Verifique se o nome de usuário e a senha estão corretos
    - O nome de usuário e senha fazem distinção entre maiúsculas e minúsculas, verifique se escreveu corretamente

    Se você esqueceu sua senha, peça para um staff que exclua seu antigo usuário e registre um novo. HTML ERRORS - AUTHENTICATION_FAILED_401 || Falha na Autenticação. HTML ERRORS - FORBIDDEN_403 || Proibido @@ -260,44 +306,42 @@ In Depth Help - /plan inspect ? || > §2Comando Inspect\ Atuali In Depth Help - /plan manage ? || > §2Comando Manage\ Gerencia o banco de dados MySQL e SQLite do Plan.\ §2/plan m §fListar subcomandos\ §2/plan m ? §fConsulta mais detalhada In Depth Help - /plan manage backup ? || > §2Subcomando Backup\ Cria um novo banco de dados SQLite (arquivo .db) com o conteúdo atual do banco de dados na pasta plugin do Plan. In Depth Help - /plan manage clear ? || > §2Subcomando Clear\ Remove qualquer coisa ativa no banco de dados. Utilize com cuidado. -In Depth Help - /plan manage con ? || > §2Subcomando Connection Debug\ Usado para depurar conexões na network.\ Envia uma requisição para cada servidor do banco de dados. -In Depth Help - /plan manage disable ? || > §2Subcomando Disable\ Pode desativar recursos do plugin até o próximo reload.\ Argumentos válidos:\ §2kickcount §fDesativa as contagens de kick em caso de /kickall. +In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. +In Depth Help - /plan manage export ? || > §2Export Subcommand\ Trigger export to result folders.\ Accepted Arguments:\ §2list §fList possible arguments.\ §2players §fExport /players, /player pages + /player/raw json depending on config values.\ §2server_json §fExport /server/raw JSON if enabled in config. In Depth Help - /plan manage import ? || > §2Subcomando Import\ Importa dados de outras fontes.\ Argumentos válidos:\ §2offline §fDados de jogadores do Bukkit, somente datas de registro e nomes. In Depth Help - /plan manage move ? || > §2Subcomando Move\ Move dados do SQLite para o MySQL ou outro meio.\ O banco de dados de destino é limpo antes da transferência de dados. +In Depth Help - /plan manage raw ? || > §2Raw Data Subcommand\ Displays link to raw JSON data page.\ Not available if Plan webserver is not enabled. In Depth Help - /plan manage remove ? || > §2Subcomando Remove\ Remover dados de jogadores do banco de dados ativo. In Depth Help - /plan manage restore ? || > §2Restore Subcommand\ Restore a previous backup SQLite database (.db file)\ You can also restore database.db from another server to MySQL.\ Target database is cleared before transfer. -In Depth Help - /plan manage setup ? || > §2Subcomando Setup\ Configura uma conexão entre Bungee e um servidor para a funcionalidade da rede.\ O endereço Bungee pode ser encontrado no log de ativação no console quando o Plan é habilitado no Bungee. +In Depth Help - /plan manage uninstalled ? || > §2Uninstalled Server Subcommand\ Marks a server as uninstalled in the database.\ Can not mark the server the command is being used on as uninstalled.\ Will affect ConnectionSystem. In Depth Help - /plan network ? || > §2Comando Network\ Mostra o link para a página da network.\ Se não for uma network, essa página mostra a página do servidor. In Depth Help - /plan players ? || > §2Comando Players\ Mostra o link para a página de jogadores. In Depth Help - /plan qinspect ? || > §2Comando Quick Inspect\ Mostra algumas informações sobre o jogador in-game. In Depth Help - /plan reload ? || > §2Comando Reload\ Reinicia o plugin usando onDisable e onEnable.\ §bIsso não suporta a troca de JAR (caso seja isso, precisa reiniciar o servidor) In Depth Help - /plan search ? || > §2Comando Search\ Pega uma lista de jogadores em que o nome coincida com o argumento dado.\§7 Exemplo: /plan search 123 - Encontra todos os jogadores com 123 no nome. In Depth Help - /plan servers ? || > §2Comando Servers\ Mostra uma lista de servidores do banco de dados.\ Podem ser usados para depurar problemas com registros no banco de dados da network. -In Depth Help - /plan update ? || > §2Comando Update\ Usado para atualizar o plugin na próxima parada\ /plan update - Changelog link\ /plan update -u - Agenda uma atualização para acontecer com todos os servidores da network que esteja online, na próxima vez em que eles sejam reiniciados.\ /plan update cancel - Cancela uma atualização agendad no servidor em que não foi reiniciado ainda. In Depth Help - /plan web ? || > §2Comando de Gerenciado do Usuário Web.\ §2/plan web §fLista subcomandos\ §2/plan web ? §fConsulta mais detalhada In Depth Help - /plan web register ? || > §2Subcomando Register\ Registra um novo usuário web.\ Registrando um usuário web para outro jogador precisa da permissão plan.webmanage.\ Senhas são criptografadas com PBKDF2 (64,000 iterações de SHA1) sendo totalmente aleatórias. In Depth Help - /planbungee disable ? || > §2Comando Disable\ Executa onDisable no PlanBungee.\ O plugin pode ser ativado com /planbungee reload.\ §bNão suporta a troca de JAR (caso seja isso, precisa reiniciar o servidor) -In Depth Help - /planbungee setup ? || > §2Comando Set-up\ Alterna o modo de configuração no Bungee.\ É um método de segurança para que não haja invasão MySQL a partir de servidores fake. Manage - Confirm Overwrite || Dados em ${0} serão sobrescritos! Manage - Confirm Removal || Dados em ${0} serão removidos! Manage - Fail || > §cAlguma coisa deu errado: ${0} Manage - Fail File not found || > §cNão foi encontrado um arquivo em ${0} Manage - Fail Incorrect Database || > §c'${0}' não é um banco de dados suportado. +Manage - Fail No Exporter || §eExporter '${0}' doesn't exist Manage - Fail No Importer || §eImportador '${0}' não existe +Manage - Fail No Server || No server found with given parameters. Manage - Fail Same Database || > §cNão é possível operar do mesmo banco de dados! +Manage - Fail Same server || Can not mark this server as uninstalled (You are on it) Manage - Fail, Confirmation || > §cAdicione o argumento '-a' para confirmar a execução: ${0} -Manage - Fail, Connection Exception || §eMotivo de falha: -Manage - Fail, No Servers || §cNenhum servidor encontrado no banco de dados. -Manage - Fail, Old version || §eMotivo de falha: Versão do Plan muito antiga no servidor de recebimento -Manage - Fail, Unauthorized || §eMotivo de falha: Não autorizado. O servidor pode estar usando um banco de dados diferente. -Manage - Fail, Unexpected Exception || §eExceção ímpar: ${0} -Manage - List Importers || Importadores: -Manage - Notify External Url || §eEndereço é remoto, verifique se a porta está aberta +Manage - List Importers || Importadores: Manage - Remind HotSwap || §eLembre-se de trocar para o novo banco de dados (/plan m hotswap ${0}) & reinicie o plugin. Manage - Start || > §2Processando dados.. Manage - Success || > §aSucesso! Negative || Não Positive || Sim +Today || 'Hoje' +Unavailable || Unavailable Unknown || Desconhecido Version - DEV || Essa é uma versão em desenvolvimento. Version - Latest || Você está usando a última versão. @@ -313,24 +357,3 @@ WebServer FAIL - Port Bind || O servidor web não foi inici WebServer FAIL - SSL Context || Servidor Web: Falha ao inicializar certificado SSL. WebServer FAIL - Store Load || Servidor Web: Falha ao carregar certificado SSL. Yesterday || 'Ontem' -Today || 'Hoje' -Health - Active Playtime Comparison Decrease || Jogadores ativos podem estar ficando sem coisas para fazer (Jogaram ${0} vs ${1}, últimas duas semanas vs últimas quatro semanas) -Health - Active Playtime Comparison Increase || Jogadores ativos parecem ter coisas para fazer (Jogaram ${0} vs ${1}, últimas duas semanas vs últimas quatro semanas) -Health - Downtime || O tempo de indisponibilidade total do servidor (sem dados) foi de ${0} -Health - New Player Join Players, No || Novos jogadores podem estar descontentes por verem o servidor com poucos jogadores ao entrar (${0} em média) -Health - New Player Join Players, Yes || Novos jogadores devem estar felizes ao entrar no servidores com muitos jogadores (${0} em média) -Health - New Player Stickiness || ${0} de novos jogadores ficaram no servidor pra jogar um pouco (${1}/${2}) -Health - No Servers Inaccuracy || Nenhum servidor Bukkit/Sponge para pegar dados de sessões - Essas medidas são imprecisas. -Health - Player Play on Network || jogadores que jogaram na network: -Health - Player Register Server || registros de jogadores no servidor por dia / servidor em média. -Health - Player Visit Server || visitas de jogadores no servidor por dia / servidor em médio. -Health - Regular Activity Change || Número de jogadores regulares é de -Health - Regular Activity Change Decrease || diminuiu (${0}) -Health - Regular Activity Change Increase || aumentou (+${0}) -Health - Regular Activity Change Zero || permaneceu o mesmo (+${0}) -Health - Regular Activity Remain || ${0} de jogadores regulares permaneceram ativos (${1}/${2}) -Health - Single Servers Inaccuracy || Único servidor Bukkit/Sponge para pegar dados de sessões. -Health - TPS Above Low Threshold || O TPS médio esteve acima do limite de segurança ${0} do tempo -Health - TPS Low Dips || O TPS médio esteve abaixo do limite de segurança (${0}) ${1} vezes -HTML - FREE_DISK_SPACE || Espaço de Disco Livre -HTML - DISK_SPACE || ESPAÇO DE DISCO \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/locale/locale_TR.txt b/Plan/common/src/main/resources/assets/plan/locale/locale_TR.txt index 1128fe006..d604cfa68 100644 --- a/Plan/common/src/main/resources/assets/plan/locale/locale_TR.txt +++ b/Plan/common/src/main/resources/assets/plan/locale/locale_TR.txt @@ -1,6 +1,7 @@ -Cmd - Click Me || Tıkla bana +Cmd - Click Me || Tıkla bana Cmd - Link || §2Link: §f Cmd Disable - Disabled || §aPlan sistemi şuanda kapandı. Plugini yeniden başlatmak için /planbungee komutunu kullanabilirsin. +Cmd FAIL - Database not open || §cDatabase is ${0} - Please try again a bit later. Cmd FAIL - Invalid Username || §cKullanıcının Bir UUID si yok. Cmd FAIL - No Feature || §eDevre dışı bırakılacak özelliği seçin! (Şuanda desteklenen ${0}) Cmd FAIL - No Permission || §cBunu gerekli izne sahip değilsin. @@ -35,29 +36,9 @@ Cmd Qinspect - Player Kills || §2Oyuncu öldürme: §f${0 Cmd Qinspect - Playtime || §2Oynama süresi: §f${0} Cmd Qinspect - Registered || §2Kayıtlı: §f${0} Cmd Qinspect - Times Kicked || §2Atılma sayısı: §f${0} -Cmd Setup - Allowed || §aŞuanda kuruluma izin verildi. -Cmd Setup - Bad Request || §eBağlantı başarılı, fakat yönlendirilmiş sunucu bir Bungee sunucusu değil. Bunun yerine Bungee adresini kullanın. -Cmd Setup - Disallowed || §cŞuanda kurulum yasaklandı -Cmd Setup - Forbidden || §eBağlantı başarılı, fakat Bungee kurulum modu kapalı - açmak için '/planbungee setup' komudunu kullanın. -Cmd Setup - Gateway Error || §eBağlantı başarılı, fakat Bungee bu sunucuya bağlanırken hata verdi (Geçerli web sunucusu yeniden başlatıldı mı?). Düzeltmek için /plan m con & /planbungee con komutlarını deneyin. -Cmd Setup - Generic Fail || §eBağlantı başarısız: ${0} -Cmd Setup - Internal Error || §eBağlantı başarılı. ${0}, sunucu ayıklama sayfasındayken hata loglarını incele. -Cmd Setup - Success || §aBağantı başarılı, Plan birkaç saniye içinde yeniden başlatılabilir.. -Cmd Setup - Unauthorized || §eBağlantı başarılı, ancak alıcı sunucu bu sunucuyu onaylamadı. Destek için Discord'a başvurun -Cmd Setup - Url mistake || §cAdrsesi tam olarak girdiğine emin ol (http:// ya da https:// ile başlamalı) - Tam adres için Bungee loglarını kontrol et. -Cmd Setup - WebServer not Enabled || §cWebServer bu sunucuda aktif değil! Açılışta etkinleştirildiğinden emin ol! Cmd SUCCESS - Feature disabled || §aKapatıldı '${0}' Plugin yeniden başlatılana kadar. Cmd SUCCESS - WebUser register || §aYeni kullanıcı (${0}) başarıyla eklendi! Web panelini aşağıdaki linkte görebilirsiniz. -Cmd Update - Cancel Success || §aİşlem iptal edildi. -Cmd Update - Cancelled || §cGüncelleme iptal edildi. -Cmd Update - Change log || Değişim kaydı v${0}: -Cmd Update - Fail Cacnel || §cGüncelleme sunucuda başarısız oldu, Tüm sunucularda güncellemeyi iptal edin.. -Cmd Update - Fail Force Notify || §e${0} failed to update, -force specified, continuing update. -Cmd Update - Fail Not Online || §cTüm sunucular çevrimiçi değil ya da erişilebilir değil, /plan update -u -force komudunu kullanarak plugini tüm sunucularda güncelleye bilrsin. -Cmd Update - Notify Cancel || §aTüm sunucularda plugin güncellenmeden önce /plan update cancel komuduyla iptal edebilirsin. -Cmd Update - Online Check || Tüm sunucular çevrimiçi mi diye kontrol ediliyor.. -Cmd Update - Scheduled || §a${0} Zamanlanış günceleme. -Cmd Update - Url mismatch || §cVersion download url did not start with ${0} and might not be trusted. You can download this version manually here (Direct download): +Cmd WARN - Database not open || §eDatabase is ${0} - This might take longer than expected.. Cmd Web - Permission Levels || >\§70: Access all pages\§71: Access '/players' and all player pages\§72: Access player page with the same username as the webuser\§73+: No permissions Command Help - /plan analyze || Sunucu Sayfasını gösterir Command Help - /plan dev || Geliştirme modu komutu @@ -67,14 +48,15 @@ Command Help - /plan inspect || Oyunucunun sayfasını göste Command Help - /plan manage || Plan Veritabanını Yönet Command Help - /plan manage backup || Veritabanını yedekler Command Help - /plan manage clear || Veritabanını temizler -Command Help - /plan manage con || Server-Bungee bağlantı sorunlarını çözer Command Help - /plan manage disable || Bir özelliği geçici olarak devre dışı bırakır +Command Help - /plan manage export || Trigger export manually Command Help - /plan manage hotswap || Veritabının hızlıca değiştirir Command Help - /plan manage import || Başka bir yerden veri alır Command Help - /plan manage move || Veriyi Veritabanları arasında taşır +Command Help - /plan manage raw || View raw JSON of player data Command Help - /plan manage remove || Oyuncu bilgilerini siler Command Help - /plan manage restore || Önceki bir Yedeği geri yükler -Command Help - /plan manage setup || Sunucu-Bungee kurulumunu yapar +Command Help - /plan manage uninstalled || Mark a server as uninstalled in the database. Command Help - /plan network || Network sayfasını görüntüler Command Help - /plan players || Oyuncu sayfasını görüntüler Command Help - /plan qinspect || Oyuncu bilgilerini oyun içi gösterir @@ -82,15 +64,12 @@ Command Help - /plan register || Web kullanıcısna kayıt yap Command Help - /plan reload || Plugini yeniden başlatır Command Help - /plan search || Bir oyuncu adı arar Command Help - /plan servers || Sunucun tüm veritabanını listeler -Command Help - /plan update || Sunucu kaydı veya plugini güncellemek için link verir Command Help - /plan web check || Web kullanıcısını inceler Command Help - /plan web delete || Web kullanıcısın siler Command Help - /plan web level || Yetki seviyeleri hakkında bilgi verir Command Help - /plan web list || Web kullanıcılarını listeler Command Help - /plan webuser || Web kullanıcılarını listeler -Command Help - /planbungee con || Bungee-Server bağlantı sorunlarını çözer Command Help - /planbungee disable || Eklentiyi geçici olarak devre dışı bırakır -Command Help - /planbungee setup || Kurulum modunu değiştirir Database - Apply Patch || Yama uygulanıyor: ${0}.. Database - Patches Applied || Yama tüm veritabanlarına başarıyla uygulandı. Database - Patches Applied Already || Veritabanlarına yama zaten uygulanmış. @@ -100,10 +79,10 @@ Database Notify - SQLite No WAL || SQLite WAL modu bu sunucu ver Disable || Oyuncu analizi kapatıldı. Disable - Processing || Önceden işlenmemiş kritik görevler işleniyor. (${0}) Disable - Processing Complete || İşlenme tamamlandı. +Disable - Unsaved Session Save || Saving unfinished sessions.. Disable - WebServer || Websunucusu kapatıldı. Enable || Oyuncu analizi aktif edildi. Enable - Database || ${0}- Veritabanı bağlantısı kurulmuş. -Enable - Notify Address Confirmation || Bu sunucu adresini doğru girdiğine emin ol: ${0} Enable - Notify Empty IP || server.properties IP adresi kısmı boş & AlternatifIP kullanılmıyor. Bu yüzden yanlış linkler verilecektir! Enable - Notify Geolocations disabled || Coğrafi konum toplama etkin değil. (Data.Geolocations: false) Enable - Notify Geolocations Internet Required || Plan GeoLite2 Geolocation veritabanını indirmek için ilk çalıştırmada internet erişimi gerektir. @@ -112,140 +91,207 @@ Enable - WebServer || Webserver Bu port üzerinden Enable FAIL - Database || ${0}-Veritabanı bağlantısı başarısız: ${1} Enable FAIL - Database Patch || VeriTabanı yaması başarısız, plugin devre dışı bırakılmış olmalı. Lütfen bu sorunu bildirin. Enable FAIL - GeoDB Write || İndirilen GeoLite2 Geolocation veritabanını kaydederken bir şeyler ters gitti. -Enable FAIL - WebServer (Bungee) || WebServer başlatılmadı! +Enable FAIL - WebServer (Proxy) || WebServer başlatılmadı! Enable FAIL - Wrong Database Type || ${0} Desteklenmeyen bir veritabanı -HTML - ACTIVITY_INDEX || Aktivite göstergesi -HTML - ALL || Tamamı -HTML - ALL_TIME_PEAK || Tüm Zamanların Zirvesi -HTML - AVERAGE_PING || Ortalama Ping -HTML - AVG || Ortalama -HTML - BANNED || Yasaklanmış -HTML - BEST_PING || En iyi Ping -HTML - CALENDAR || Takvim -HTML - CALENDAR_TEXT || Takvim -HTML - CHUNKS || Chunks -HTML - COMMAND || Komut -HTML - COMMNAND_USAGE || Komut Kullanımı -HTML - CONNECTION_INFORMATION || Bağlantı Bilgileri -HTML - COUNTRY || Ülke -HTML - CURRENT_PLAYERBASE || Current Playerbase -HTML - DEATHS || Ölümler -HTML - ENTITIES || Varlıklar +HTML - COMPARING_15_DAYS || Comparing 15 days +HTML - COMPARING_60_DAYS || Comparing 30d ago to Now +HTML - COMPARING_7_DAYS || Comparing 7 days +HTML - DATABASE_NOT_OPEN || Database is not open, check db status with /plan info HTML - ERROR || Kimlik doğrulama hata nedeniyle başarısız oldu -HTML - FAVORITE_SERVER || Favori Sunucu -HTML - GEOLOCATION || Coğrafi Konum -HTML - GEOLOCATION_TEXT || Coğrafi Konum -HTML - HEALTH_ESTIMATE || Sağlık Tahmini HTML - INDEX_ACTIVE || Aktivite HTML - INDEX_INACTIVE || Etkisiz HTML - INDEX_IRREGULAR || Düzensiz HTML - INDEX_REGULAR || Düzenli HTML - INDEX_VERY_ACTIVE || Çok Aktif -HTML - IP_ADDRESS || IP-address HTML - KILLED || Öldürülen -HTML - KILLED_BY || Tarafından Öldürülen -HTML - LAST_24_HOURS || SON 24 SAAT -HTML - LAST_30_DAYS || SON 30 GÜN -HTML - LAST_30_DAYS_TEXT || SON 30 GÜN -HTML - LAST_7_DAYS || SON 7 GÜN -HTML - LAST_CONNECTED || Son bağlantı -HTML - LAST_PEAK || Son Zirve -HTML - LAST_SEEN || Son Görülme -HTML - LAST_SEEN_TEXT || Son Görülme -HTML - LOADED_CHUNKS || Yüklenmiş Chunks lar -HTML - LOADED_ENTITIES || Yüklenmiş Varlıklar +HTML - LABEL_1ST_WEAPON || Deadliest PvP Weapon +HTML - LABEL_2ND_WEAPON || 2nd PvP Weapon +HTML - LABEL_3RD_WEAPON || 3rd PvP Weapon +HTML - LABEL_ACTIVITY_INDEX || Aktivite göstergesi +HTML - LABEL_AFK || AFK +HTML - LABEL_AFK_TIME || AFK Time +HTML - LABEL_AVG || Average +HTML - LABEL_AVG_KDR || Average KDR +HTML - LABEL_AVG_MOB_KDR || Average Mob KDR +HTML - LABEL_AVG_PLAYTIME || Average Playtime +HTML - LABEL_AVG_SESSION_LENGTH || Average Session Length +HTML - LABEL_AVG_TPS || Average TPS +HTML - LABEL_BANNED || Yasaklanmış +HTML - LABEL_BEST_PEAK || Tüm Zamanların Zirvesi +HTML - LABEL_DAY_OF_WEEK || Day of the Week +HTML - LABEL_DEATHS || Ölümler +HTML - LABEL_DOWNTIME || Downtime +HTML - LABEL_DURING_LOW_TPS || During Low TPS Spikes: +HTML - LABEL_ENTITIES || Varlıklar +HTML - LABEL_FAVORITE_SERVER || Favori Sunucu +HTML - LABEL_FIRST_SESSION_LENGTH || First session length +HTML - LABEL_FREE_DISK_SPACE || Free Disk Space +HTML - LABEL_INACTIVE || Inactive +HTML - LABEL_LAST_PEAK || Son Zirve +HTML - LABEL_LAST_SEEN || Son Görülme +HTML - LABEL_LOADED_CHUNKS || Yüklenmiş Chunks lar +HTML - LABEL_LOADED_ENTITIES || Yüklenmiş Varlıklar +HTML - LABEL_LONE_JOINS || Lone joins +HTML - LABEL_LONE_NEW_JOINS || Lone newbie joins +HTML - LABEL_LONGEST_SESSION || Longest Session +HTML - LABEL_LOW_TPS || Low TPS Spikes +HTML - LABEL_MAX_FREE_DISK || Max Free Disk +HTML - LABEL_MIN_FREE_DISK || Min Free Disk +HTML - LABEL_MOB_DEATHS || Yaratık Yüzünden ölümler +HTML - LABEL_MOB_KDR || Mob İstatistiği +HTML - LABEL_MOB_KILLS || Öldürülen Mob +HTML - LABEL_MOST_ACTIVE_GAMEMODE || Most Active Gamemode +HTML - LABEL_NAME || Name +HTML - LABEL_NEW || New +HTML - LABEL_NEW_PLAYERS || Yeni Oyuncular +HTML - LABEL_NICKNAME || Takma ad +HTML - LABEL_NO_SESSION_KILLS || None +HTML - LABEL_ONLINE_FIRST_JOIN || Players online on first join +HTML - LABEL_OPERATOR || Operator +HTML - LABEL_PER_PLAYER || / Player +HTML - LABEL_PER_REGULAR_PLAYER || / Regular Player +HTML - LABEL_PLAYER_DEATHS || Oyuncu ölüme sebep oldu +HTML - LABEL_PLAYER_KILLS || Oyuncu Öldürüldü +HTML - LABEL_PLAYERS_ONLINE || Oyuncu Çevrimiçi +HTML - LABEL_PLAYTIME || Oyun Süresi +HTML - LABEL_REGISTERED || Kayıtlı +HTML - LABEL_REGISTERED_PLAYERS || Registered Players +HTML - LABEL_REGULAR || Regular +HTML - LABEL_REGULAR_PLAYERS || Regular Players +HTML - LABEL_RELATIVE_JOIN_ACTIVITY || Relative Join Activity +HTML - LABEL_RETENTION || New Player Retention +HTML - LABEL_SERVER_DOWNTIME || Server Downtime +HTML - LABEL_SERVER_OCCUPIED || Server occupied +HTML - LABEL_SESSION_ENDED || Oturum Sona Erdi +HTML - LABEL_SESSION_MEDIAN || Session Median +HTML - LABEL_TIMES_KICKED || Kere Atılmış +HTML - LABEL_TOTAL_PLAYERS || Total Players +HTML - LABEL_TOTAL_PLAYTIME || Total Playtime +HTML - LABEL_UNIQUE_PLAYERS || Sunucuya İlk Defa Girenler +HTML - LABEL_WEEK_DAYS || 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' +HTML - LINK_BACK_NETWORK || Network page +HTML - LINK_BACK_SERVER || Server page +HTML - LINK_CHANGELOG || View Changelog +HTML - LINK_DISCORD || General Support on Discord +HTML - LINK_DOWNLOAD || Download +HTML - LINK_ISSUES || Report Issues +HTML - LINK_NIGHT_MODE || Night Mode +HTML - LINK_PLAYER_PAGE || Player Page +HTML - LINK_QUICK_VIEW || Quick view +HTML - LINK_SERVER_ANALYSIS || Sunucu analizi +HTML - LINK_WIKI || Plan Wiki, Tutorials & Documentation HTML - LOCAL_MACHINE || Yerel makine -HTML - LONGEST || En uzun -HTML - LOW_TPS_SPIKES || Düşük TPS Artışı -HTML - MOB_CAUSED_DEATHS || Yaratık Yüzünden ölümler -HTML - MOB_KDR || Mob İstatistiği -HTML - MOB_KILLS || Öldürülen Mob -HTML - MOST_RECENT_SESSIONS || En Son Oturumlar -HTML - NAME || İsim -HTML - NAV_COMMAND_USAGE || Komut kullanımı -HTML - NAV_GEOLOCATIONS || Coğrafi Konumlar -HTML - NAV_INFORMATION || Bilgi -HTML - NAV_NETWORK_PLAYERS || Network Oyuncuları -HTML - NAV_ONLINE_ACTIVITY || Çevrimiçi Etkinlik -HTML - NAV_OVERVIEW || Genel Bakış -HTML - NAV_PERFORMANCE || Performans -HTML - NAV_PLAYERS || Oyuncular HTML - NAV_PLUGINS || Pluginler -HTML - NAV_SESSIONS || Oturumlar -HTML - NAV_SEVER_HEALTH || Sunucu Sağlığı -HTML - NETWORK || Network -HTML - NETWORK_INFORMATION || NETWORK Bilgisi -HTML - NEW || YENİ HTML - NEW_CALENDAR || Yeni: -HTML - NEW_PLAYERS_TEXT || Yeni Oyuncular -HTML - NEW_RETENTION || New Player Retention -HTML - NEW_TEXT || Yeni -HTML - NICKNAME || Takma ad HTML - NO_KILLS || Öldürmesi yok -HTML - NO_PLAYER_CAUSED_DEATHS || Hiçbir Oyuncu Ölüme Sebep Olmadı HTML - OFFLINE || Çevrimdışı HTML - ONLINE || Çevrimiçi -HTML - ONLINE_ACTIVITY || ÇEVRİMİÇİ AKTİVİTE -HTML - OPERATOR || Operator -HTML - OVERVIEW || GENEL BAKIŞ HTML - PER_DAY || / Gün -HTML - PLAYER_CAUSED_DEATHS || Oyuncu ölüme sebep oldu -HTML - PLAYER_KILLS || Oyuncu Öldürüldü -HTML - PLAYER_LIST || Oyuncu Listesi -HTML - PLAYERBASE_DEVELOPMENT || Oyuncu Etkinlik Grafiği -HTML - PLAYERS || OYUNCULAR -HTML - PLAYERS_ONLINE || OYUNCU AKTİF -HTML - PLAYERS_ONLINE_TEXT || Oyuncu Çevrimiçi HTML - PLAYERS_TEXT || Oyuncular -HTML - PLAYTIME || Oyun Süresi -HTML - PLEASE_WAIT || Lütfen Bekleyin... -HTML - PREDICETED_RETENTION || Tahmin Edilen Tutma -HTML - PUNCH_CARD || Punchcard -HTML - PUNCHCARD || PUNCHCARD -HTML - RECENT_LOGINS || SON GİRİŞLER -HTML - REGISTERED || KAYITLI -HTML - REGISTERED_TEXT || Kayıtlı -HTML - REGULAR || DÜZENLİ -HTML - SEEN_NICKNAMES || Görülen takma adlar -HTML - SERVER || Sunucu -HTML - SERVER_ANALYSIS || Sunucu analizi -HTML - SERVER_HEALTH_ESTIMATE || Sunucu Sağlığı Tahmini -HTML - SERVER_INFORMATION || SUNUCU BİLGİSİ -HTML - SERVER_PREFERENCE || Sunucu Tercihi -HTML - SERVERS || Sunucular HTML - SESSION || Oturum -HTML - SESSION_ENDED || Oturum Sona Erdi -HTML - SESSION_LENGTH || Oturum Giriş Süresi -HTML - SESSION_MEDIAN || Session Median -HTML - SESSIONS || Oturumlar -HTML - TIME || Süre -HTML - TIMES_KICKED || Kere Atılmış -HTML - TIMES_USED || Kere Kullanışmış +HTML - SIDE_GEOLOCATIONS || Coğrafi Konumlar +HTML - SIDE_INFORMATION || INFORMATION +HTML - SIDE_NETWORK_OVERVIEW || Network Overview +HTML - SIDE_OVERVIEW || Genel Bakış +HTML - SIDE_PERFORMANCE || Performans +HTML - SIDE_PLAYER_LIST || Oyuncu Listesi +HTML - SIDE_PLAYERBASE || Playerbase +HTML - SIDE_PLAYERBASE_OVERVIEW || Playerbase Overview +HTML - SIDE_PLUGINS || PLUGINS +HTML - SIDE_PVP_PVE || PvP & PvE +HTML - SIDE_SERVERS || Sunucular +HTML - SIDE_SERVERS_TITLE || SERVERS +HTML - SIDE_SESSIONS || Oturumlar +HTML - SIDE_TO_MAIN_PAGE || to main page +HTML - TEXT_CLICK_TO_EXPAND || Click to expand +HTML - TEXT_CONTRIBUTORS_CODE || code contributor +HTML - TEXT_CONTRIBUTORS_LOCALE || translator +HTML - TEXT_CONTRIBUTORS_MONEY || Extra special thanks to those who have monetarily supported the development. +HTML - TEXT_CONTRIBUTORS_THANKS || In addition following awesome people have contributed: +HTML - TEXT_DEV_VERSION || This version is a DEV release. +HTML - TEXT_DEVELOPED_BY || is developed by +HTML - TEXT_FIRST_SESSION || First session +HTML - TEXT_LICENSED_UNDER || is developed and licensed under +HTML - TEXT_METRICS || bStats Metrics +HTML - TEXT_NO_EXTENSION_DATA || No Extension Data +HTML - TEXT_NO_LOW_TPS || No low tps spikes +HTML - TEXT_NO_SERVER || No server to display online activity for +HTML - TEXT_NO_SERVERS || No servers found in the database +HTML - TEXT_PLUGIN_INFORMATION || Information about the plugin +HTML - TEXT_PREDICTED_RETENTION || This value is a prediction based on previous players +HTML - TEXT_VERSION || A new version has been released and is now available for download. +HTML - TITLE_30_DAYS || 30 days +HTML - TITLE_30_DAYS_AGO || 30 days ago +HTML - TITLE_ALL || Tamamı +HTML - TITLE_ALL_TIME || All Time +HTML - TITLE_AS_NUMBERS || as Numbers +HTML - TITLE_AVG_PING || Average Ping +HTML - TITLE_BEST_PING || Best Ping +HTML - TITLE_CALENDAR || Takvim +HTML - TITLE_CONNECTION_INFO || Connection Information +HTML - TITLE_COUNTRY || Ülke +HTML - TITLE_CPU_RAM || CPU & RAM +HTML - TITLE_CURRENT_PLAYERBASE || Current Playerbase +HTML - TITLE_DISK || Disk Space +HTML - TITLE_GRAPH_DAY_BY_DAY || Day by Day +HTML - TITLE_GRAPH_NETWORK_ONLINE_ACTIVITY || Network Online Activity +HTML - TITLE_GRAPH_PUNCHCARD || Punchcard for 30 days +HTML - TITLE_INSIGHTS || Insights for 30 days +HTML - TITLE_IS_AVAILABLE || is Available +HTML - TITLE_LAST_24_HOURS || Son 24 saat +HTML - TITLE_LAST_30_DAYS || Son 30 gün +HTML - TITLE_LAST_7_DAYS || Son 7 gün +HTML - TITLE_LAST_CONNECTED || Son bağlantı +HTML - TITLE_LENGTH || Length +HTML - TITLE_MOST_PLAYED_WORLD || Most played World +HTML - TITLE_NETWORK || Network +HTML - TITLE_NETWORK_AS_NUMBERS || Network as Numbers +HTML - TITLE_NOW || Now +HTML - TITLE_ONLINE_ACTIVITY || Online Activity +HTML - TITLE_ONLINE_ACTIVITY_AS_NUMBERS || Online Activity as Numbers +HTML - TITLE_ONLINE_ACTIVITY_OVERVIEW || Online Activity Overview +HTML - TITLE_PERFORMANCE_AS_NUMBERS || Performance as Numbers +HTML - TITLE_PING || Ping +HTML - TITLE_PLAYER || Player +HTML - TITLE_PLAYER_OVERVIEW || Player Overview +HTML - TITLE_PLAYERBASE_DEVELOPMENT || Oyuncu Etkinlik Grafiği +HTML - TITLE_PVP_DEATHS || Recent PvP Deaths +HTML - TITLE_PVP_KILLS || Recent PvP Kills +HTML - TITLE_PVP_PVE_NUMBERS || PvP & PvE as Numbers +HTML - TITLE_RECENT_KILLS || Recent Kills +HTML - TITLE_RECENT_SESSIONS || En Son Oturumlar +HTML - TITLE_SEEN_NICKNAMES || Görülen takma adlar +HTML - TITLE_SERVER || Sunucu +HTML - TITLE_SERVER_AS_NUMBERS || Server as Numbers +HTML - TITLE_SERVER_OVERVIEW || Server Overview +HTML - TITLE_SERVER_PLAYTIME || Server Playtime +HTML - TITLE_SERVER_PLAYTIME_30 || Server Playtime for 30 days +HTML - TITLE_SESSION_START || Session Started +HTML - TITLE_THEME_SELECT || Theme Select +HTML - TITLE_TITLE_PLAYER_PUNCHCARD || Punchcard +HTML - TITLE_TPS || TPS +HTML - TITLE_TREND || Trend +HTML - TITLE_TRENDS || Trends for 30 days +HTML - TITLE_VERSION || Version +HTML - TITLE_WEEK_COMPARISON || Week Comparison +HTML - TITLE_WORLD || World Load +HTML - TITLE_WORLD_PLAYTIME || Dünya OyunSüresi +HTML - TITLE_WORST_PING || Worst Ping HTML - TOTAL_ACTIVE_TEXT || Toplam Aktiflik HTML - TOTAL_AFK || Toplam AFKlık HTML - TOTAL_PLAYERS || Toplam Oyuncular -HTML - TOTAL_PLAYTIME || Toplam Oyunda oynanan süre -HTML - UNIQUE || İLK DEFA GİRENLER HTML - UNIQUE_CALENDAR || Benzersiz: -HTML - UNIQUE_PLAYERS || SUNUCUYA İLK DEFA GİRENLER -HTML - UNIQUE_PLAYERS_TEXT || Sunucuya İlk Defa Girenler -HTML - UNIQUE_TEXT || İlk Defa Girenler -HTML - USAGE || Kullanım -HTML - USED_COMMANDS || Kullanılan Komutlar +HTML - UNIT_CHUNKS || Chunks +HTML - UNIT_ENTITIES || Entities +HTML - UNIT_NO_DATA || No Data +HTML - UNIT_THE_PLAYERS || Players HTML - USER_AND_PASS_NOT_SPECIFIED || Kullanıcı ve Şifre belirtilmedi HTML - USER_DOES_NOT_EXIST || Böyle Bir Kullanıcı Yok -HTML - USER_INFORMATION || KULLANICI BİLGİSİ HTML - USER_PASS_MISMATCH || Kullanıcı adı ve şifre uyuşmuyor HTML - WITH ||
    Birlikte -HTML - WORLD || Dünya -HTML - WORLD_LOAD || WORLD LOAD -HTML - WORLD_PLAYTIME || Dünya OyunSüresi -HTML - WORST_PING || Kötü Ping HTML ERRORS - ACCESS_DENIED_403 || Giriş reddedildi -HTML ERRORS - ANALYSIS_REFRESH || Analiz yenileniyor.. -HTML ERRORS - ANALYSIS_REFRESH_LONG || Analiz çalıştırılıyor, birkaç saniye sonra sayfayı yenileyin.. HTML ERRORS - AUTH_FAIL_TIPS_401 || - Bir kullanıcıyı /plan register
    - ile kayıt ettiğinize emin olun ismin ve şifrenin doğru olup olmadığını kontol edin
    - Kullanıcı adı ve şifre büyük / küçük harf duyarlıdır

    Eğer şifreni unuttuysan, Yetkiliden sizi tekrar kayıt etmesini isteyin. -HTML ERRORS - AUTHENTICATION_FAIlED_401 || Kimlik doğrulama başarısız oldu. +HTML ERRORS - AUTHENTICATION_FAILED_401 || Authentication Failed. HTML ERRORS - FORBIDDEN_403 || Yasaklanmış. HTML ERRORS - NO_SERVERS_404 || İsteği gerçekleştirecek çevrimiçi sunucu yok. HTML ERRORS - NOT_FOUND_404 || Bulunamadı @@ -260,44 +306,42 @@ In Depth Help - /plan inspect ? || > §2Denetleme Komutu\ Oyunc In Depth Help - /plan manage ? || > §2Yönetme Komutu\ MySQL ve SQLite Plan veritabanını yönetin.\ §2/plan m §fList subcommands\ §2/plan m ? §fDaha fazla yardım In Depth Help - /plan manage backup ? || > §2Yedekleme Alt Komutu\ Yeni bir SQLite Veritabanı (.db dosyası) Plan eklentisi klasöründeki etkin veritabanı içeriğiyle birlikte. In Depth Help - /plan manage clear ? || > §2Temizleme Alt Komutu\ Aktf Veritabanlarındaki tüm bilgileri siler. Silmeden önce yedek alın ve herkesi uyarın. -In Depth Help - /plan manage con ? || > §2Bağlantı sorunlarını çözme alt komutu\ Networka bağlanma sorunlarını çözmek için kullanılır.\ Veritabanındaki her sunucuya bir istek gönderir. -In Depth Help - /plan manage disable ? || > §2Devre Dışı Alt Komutu\ Eklentinin parçalarını bir sonraki yeniden yüklemeye kadar devre dışı bırakır.\ Kabul edilen argümanlar:\ §2Atılma sayısı §fKapatma makrosunda / kickall kullanılıyorsa kick sayımlarını devre dışı bırakır. +In Depth Help - /plan manage disable ? || > §2Disable Subcommand\ Can disable parts of the plugin until next reload.\ Accepted arguments:\ §2kickcount §fDisables kick counts in case /kickall is used on shutdown macro. +In Depth Help - /plan manage export ? || > §2Export Subcommand\ Trigger export to result folders.\ Accepted Arguments:\ §2list §fList possible arguments.\ §2players §fExport /players, /player pages + /player/raw json depending on config values.\ §2server_json §fExport /server/raw JSON if enabled in config. In Depth Help - /plan manage import ? || > §2Import Subcommand\ Import data from other sources.\ Accepted Arguments:\ §2Çevrimdışı §fBukkit oyuncu verileri, sadece kayıt tarihi ve ismi. In Depth Help - /plan manage move ? || > §2Veri konumu değiştirme alt Komutu\ Verileri SQLite'den MySQL'e veya başka bir yere taşıyın.\ Dosyaların gönderileceği Veritabanı aktarımdan önce temizlendi. +In Depth Help - /plan manage raw ? || > §2Raw Data Subcommand\ Displays link to raw JSON data page.\ Not available if Plan webserver is not enabled. In Depth Help - /plan manage remove ? || > §2Silme alt Komutu\ Aktif Veritabanından oyuncu bilgilerini siler. In Depth Help - /plan manage restore ? || > §2Onarım Alt Komutu\ Önceki bir yedek SQLite veritabanını geri yükler (.db dosyası)\ Ayrıca database.db dosyasını başka bir sunucudan MySQL'e geri yükleyebilirsiniz.\ Veriler gönderilmeden önce Veritabanı temizlenir. -In Depth Help - /plan manage setup ? || > §2Kurulum alt Komutu\ Bungeecord ile server Arasında bağlantı kurmalısın.\ Bungeeaddress planını aktif ettiğinde bungee konsolunda görünür. +In Depth Help - /plan manage uninstalled ? || > §2Uninstalled Server Subcommand\ Marks a server as uninstalled in the database.\ Can not mark the server the command is being used on as uninstalled.\ Will affect ConnectionSystem. In Depth Help - /plan network ? || > §2Network Komutu\ Ağ sayfasının bağlantısını görüntüler.\ Networkte değilse, bu sayfa sunucu sayfasını görüntüler. In Depth Help - /plan players ? || > §2Oyuncu Komutu\ Oyuncular sayfasına olan bağlantıyı verir. In Depth Help - /plan qinspect ? || > §2Hızlı Denetim Komutu\ Oyun içindeyken oyuncu hakkında bilgi verir. In Depth Help - /plan reload ? || > §2Yeniden Başlatma Komutu\ Restarts the plugin using onDisable and onEnable.\ §bDoes not support swapping jar on the fly In Depth Help - /plan search ? || > §2Arama Komutu\ Verilen argümanla eşleşen Oyuncu isimlerinin bir listesini alın.\§7 Örnek: /plan search 123 - 123 adındaki tüm kullanıcıları adında bulur. In Depth Help - /plan servers ? || > §2Sunucu Komutu\ Veritabanındaki Plan sunucularının listesini görüntüler.\ Networkteki veritabanı kaydıyla ilgili sorunları çözmek için kullanılabilir. -In Depth Help - /plan update ? || > §2Güncelleme Komutu\ Bir sonraki kapanışta eklentiyi güncellemek için kullanılır\ /plan update - Değişim linki\ /plan update -u - Güncelleştirmeyi, çevrimiçi olan tüm Netwok sunucularında, bir sonraki açılışında güncellenecek şekilde ayarlayın.\ /plan update cancel - Henüz yeniden başlatılmamış sunucularda zamanlanmış güncellemeyi iptal et. In Depth Help - /plan web ? || < §2Web Kullanıcısı Yönetim Paneli Komutu.\ §2/plan web §fList subcommands\ §2/plan web ? §fIn Depth help In Depth Help - /plan web register ? || > §2Kayıt Alt Komutu\ Yeni bir Web Kullanıcısı kaydeder.\ Bir kullanıcıyı başka bir oyuncuya kaydetmek plan.webmanage izni gerektirir.\ Şifreler, kriptografik olarak rasgele bir sıra kullanılarak PBKDF2 (64.000 iterasyon SHA1) ile karıştırıldı. In Depth Help - /planbungee disable ? || > §2Devre Dışı Komutu\ Runs onDisable on PlanBungee.\ Plugin can be enabled with /planbungee reload afterwards.\ §bDoes not support swapping jar on the fly -In Depth Help - /planbungee setup ? || > §2Set-up toggle Command\ Bungee'deki kurulum modunu değiştirir.\ Başka bir sunucudan yetkisiz MySQL girişine karşı korunma. Manage - Confirm Overwrite || ${0} içindeki verilen üzerinden yazılacak! Manage - Confirm Removal || ${0} İçindeki Veri Silinecek! Manage - Fail || > §cBirşey yanlış gidiyor: ${0} Manage - Fail File not found || > §cBurada bir dosya bulunamadı ${0} Manage - Fail Incorrect Database || > §c'${0}' Desteklenmeyen bir VeriTabanı. +Manage - Fail No Exporter || §eExporter '${0}' doesn't exist Manage - Fail No Importer || §eAlıcı '${0}' yok +Manage - Fail No Server || No server found with given parameters. Manage - Fail Same Database || > §cAynı veritabanında veya benzerinde çalışamaz! +Manage - Fail Same server || Can not mark this server as uninstalled (You are on it) Manage - Fail, Confirmation || > §cKomutu onaylamak için '-a' komuta ekle: ${0} -Manage - Fail, Connection Exception || §eHata Sebebi: -Manage - Fail, No Servers || §cVeritabanında sunucu verileri bulunamadı. -Manage - Fail, Old version || §eHata Sebebi: Şuanda sunucuzda eski Plan versiyonu çalışıyor. -Manage - Fail, Unauthorized || §eHata Sebebi: Yetkisiz. Sunucu farklı veritabanı kullanıyor olabilir. -Manage - Fail, Unexpected Exception || §eOdd Exception: ${0} -Manage - List Importers || Importers: -Manage - Notify External Url || §eBöyle bir adres yok, lütfen portların açık olduğunu kontrol edin. +Manage - List Importers || Importers: Manage - Remind HotSwap || §eRemember to swap to the new database (/plan m hotswap ${0}) & reload the plugin. Manage - Start || > §2Veri işleniyor.. Manage - Success || > §aBaşarılı! Negative || Hayır Positive || Evet +Today || 'Today' +Unavailable || Unavailable Unknown || Bilinmeyen Version - DEV || Bu bir GELİŞTİRİCİ sürümüdür. Version - Latest || En son sürümü kullanıyorsunuz. @@ -312,4 +356,4 @@ WebServer - Notify no Cert file || WebServer: Setrifikası Dosya WebServer FAIL - Port Bind || Web Sunucusu başarıyla başlatılmadı. Bu (${0}) port mu kullanılıyor ? WebServer FAIL - SSL Context || WebServer: SSL İçeriği Başlatma Başarısız Oldu. WebServer FAIL - Store Load || WebServer: SSL Sertifikası yüklenirken sorun oluştu. -Yesterday || 'Dün' \ No newline at end of file +Yesterday || 'Dün' diff --git a/Plan/common/src/main/resources/assets/plan/themes/greyscale.yml b/Plan/common/src/main/resources/assets/plan/themes/greyscale.yml index 462032da4..d7fc0d36c 100644 --- a/Plan/common/src/main/resources/assets/plan/themes/greyscale.yml +++ b/Plan/common/src/main/resources/assets/plan/themes/greyscale.yml @@ -1,11 +1,11 @@ # Default Theme bar color when user has not chosen a color -DefaultColor: "teal" +DefaultColor: "plan" Font: # Location of the FontStyleSheet, eg. https://fonts.googleapis.com/css?family=Quicksand:300,400 - FontStyleSheet: https://fonts.googleapis.com/css?family=Roboto:400,700&subset=latin,cyrillic-ext + FontStyleSheet: https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext # Name of the Font for CSS eg "'Quicksand', sans-serif" - FontFamily: '"Roboto", sans-serif' + FontFamily: '"Nunito"' # Color codes for each theme element color # These affect icons & backgrounds of all elements of this color. diff --git a/Plan/common/src/main/resources/assets/plan/themes/mute.yml b/Plan/common/src/main/resources/assets/plan/themes/mute.yml index d6992cf20..7545f6d5a 100644 --- a/Plan/common/src/main/resources/assets/plan/themes/mute.yml +++ b/Plan/common/src/main/resources/assets/plan/themes/mute.yml @@ -1,11 +1,11 @@ # Default Theme bar color when user has not chosen a color -DefaultColor: "green" +DefaultColor: "plan" Font: # Location of the FontStyleSheet, eg. https://fonts.googleapis.com/css?family=Quicksand:300,400 - FontStyleSheet: https://fonts.googleapis.com/css?family=Roboto:400,700&subset=latin,cyrillic-ext + FontStyleSheet: https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext # Name of the Font for CSS eg "'Quicksand', sans-serif" - FontFamily: '"Roboto", sans-serif' + FontFamily: '"Nunito"' # Color codes for each theme element color # These affect icons & backgrounds of all elements of this color. diff --git a/Plan/common/src/main/resources/assets/plan/themes/pastel.yml b/Plan/common/src/main/resources/assets/plan/themes/pastel.yml index 01c4c79aa..f2a0811d8 100644 --- a/Plan/common/src/main/resources/assets/plan/themes/pastel.yml +++ b/Plan/common/src/main/resources/assets/plan/themes/pastel.yml @@ -1,11 +1,11 @@ # Default Theme bar color when user has not chosen a color -DefaultColor: "indigo" +DefaultColor: "plan" Font: # Location of the FontStyleSheet, eg. https://fonts.googleapis.com/css?family=Quicksand:300,400 - FontStyleSheet: https://fonts.googleapis.com/css?family=Roboto:400,700&subset=latin,cyrillic-ext + FontStyleSheet: https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext # Name of the Font for CSS eg "'Quicksand', sans-serif" - FontFamily: '"Roboto", sans-serif' + FontFamily: '"Nunito"' # Color codes for each theme element color # These affect icons & backgrounds of all elements of this color. diff --git a/Plan/common/src/main/resources/assets/plan/themes/sepia.yml b/Plan/common/src/main/resources/assets/plan/themes/sepia.yml index e151334a8..8d6a8a9ce 100644 --- a/Plan/common/src/main/resources/assets/plan/themes/sepia.yml +++ b/Plan/common/src/main/resources/assets/plan/themes/sepia.yml @@ -1,11 +1,11 @@ # Default Theme bar color when user has not chosen a color -DefaultColor: "brown" +DefaultColor: "plan" Font: # Location of the FontStyleSheet, eg. https://fonts.googleapis.com/css?family=Quicksand:300,400 - FontStyleSheet: https://fonts.googleapis.com/css?family=Roboto:400,700&subset=latin,cyrillic-ext + FontStyleSheet: https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext # Name of the Font for CSS eg "'Quicksand', sans-serif" - FontFamily: '"Roboto", sans-serif' + FontFamily: '"Nunito"' # Color codes for each theme element color # These affect icons & backgrounds of all elements of this color. diff --git a/Plan/common/src/main/resources/assets/plan/themes/soft.yml b/Plan/common/src/main/resources/assets/plan/themes/soft.yml index ad3602520..1ddd4b688 100644 --- a/Plan/common/src/main/resources/assets/plan/themes/soft.yml +++ b/Plan/common/src/main/resources/assets/plan/themes/soft.yml @@ -1,11 +1,11 @@ # Default Theme bar color when user has not chosen a color -DefaultColor: "light-green" +DefaultColor: "plan" Font: # Location of the FontStyleSheet, eg. https://fonts.googleapis.com/css?family=Quicksand:300,400 - FontStyleSheet: https://fonts.googleapis.com/css?family=Roboto:400,700&subset=latin,cyrillic-ext + FontStyleSheet: https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext # Name of the Font for CSS eg "'Quicksand', sans-serif" - FontFamily: '"Roboto", sans-serif' + FontFamily: '"Nunito"' # Color codes for each theme element color # These affect icons & backgrounds of all elements of this color. diff --git a/Plan/common/src/main/resources/assets/plan/themes/theme.yml b/Plan/common/src/main/resources/assets/plan/themes/theme.yml index c7a5df825..71b1de3e9 100644 --- a/Plan/common/src/main/resources/assets/plan/themes/theme.yml +++ b/Plan/common/src/main/resources/assets/plan/themes/theme.yml @@ -1,11 +1,11 @@ # Default Theme bar color when user has not chosen a color -DefaultColor: "light-green" +DefaultColor: "plan" Font: # Location of the FontStyleSheet, eg. https://fonts.googleapis.com/css?family=Quicksand:300,400 - FontStyleSheet: https://fonts.googleapis.com/css?family=Roboto:400,700&subset=latin,cyrillic-ext + FontStyleSheet: https://fonts.googleapis.com/css?family=Nunito:400,700,800,900&display=swap&subset=latin-ext # Name of the Font for CSS eg "'Quicksand', sans-serif" - FontFamily: '"Roboto", sans-serif' + FontFamily: '"Nunito"' # Color codes for each theme element color # These affect icons & backgrounds of all elements of this color. diff --git a/Plan/common/src/main/resources/assets/plan/web/css/main.css b/Plan/common/src/main/resources/assets/plan/web/css/main.css deleted file mode 100644 index 19ddc2f88..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/main.css +++ /dev/null @@ -1,168 +0,0 @@ -.black { - color: #000000; -} -.darkblue { - color: #0000AA; -} -.darkgreen { - color: #00AA00; -} -.darkaqua { - color: #00AAAA; -} -.darkred { - color: #AA0000; -} -.darkpurple { - color: #AA00AA; -} -.gold { - color: #FFAA00; -} -.gray { - color: #AAAAAA; -} -.darkgray { - color: #555555; -} -.blue { - color: #5555FF; -} -.green { - color: #55FF55; -} -.aqua { - color: #55FFFF; -} -.red { - color: #FF5555; -} -.pink { - color: #FF55FF; -} -.yellow { - color: #FFFF55; - text-shadow: 0 0 6px #000; -} -.white { - color: #FFFFFF; - text-shadow: 0 0 8px #000; -} - -.g { - color: #008000; -} -.o { - color: #FFA500; -} -.r { - color: #FF4500; -} - -.nav-button { -} - -.sidenav-button:hover { - background: #89c471; -} - -.right { - float: right; -} -.left { - float: left; -} - -.plugin { - max-height: 300px; - overflow-y: auto; - height: 100%; -} - -.main-limiter { - width: 100%; - overflow-x: hidden; - margin: 0; - padding: 0; -} - -.tab { - width: 0; - float: left; -} - -.scrollbar { - max-height: 650px; - overflow-y: auto; -} - -.session-header h3 { - margin: 0; -} -.session-header p { - margin: 0; -} -.session { - -} - -.session-header { - background: #ddd; - color: #000; - margin: 1px 3px; - display: flex; - flex-direction: row; - padding: 0; -} - -.session-col { - width: 100%; - background: #fff; - color: #000; - margin: 0 2px; - display: flex; - flex-direction: column; - padding: 10px 20px; -} - -.session-content { - display: none; - margin: 2px 10px; - flex-direction: row; -} - -.plugins-content { - display: none; -} -.plugins-header h2 { - margin: 0; -} -.plugins-header p { - margin: 0; -} - -.plugin { - padding: 10px; -} - -.plugin p { - padding: 5px 0; - margin: 0; -} - -.help { - text-decoration: none; -!important; -} - -@media only screen and (max-width: 1680px) { - .nav-button { - font-size: 19px; - } -} -@media only screen and (max-width: 780px) { - .scrollbar { - overflow-x: auto; - width: 100%; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/css/materialize.css b/Plan/common/src/main/resources/assets/plan/web/css/materialize.css deleted file mode 100644 index f1ed98446..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/materialize.css +++ /dev/null @@ -1,415 +0,0 @@ -/*! - * Materialize v0.97.7 (http://materializecss.com) - * Copyright 2014-2015 Materialize - * MIT License (https://raw.githubusercontent.com/Dogfalo/materialize/master/LICENSE) - */ - -/* Radio Buttons - ========================================================================== */ -[type="radio"]:not(:checked), -[type="radio"]:checked { - position: absolute; - left: -9999px; - opacity: 0; -} - -[type="radio"]:not(:checked) + label, -[type="radio"]:checked + label { - position: relative; - padding-left: 35px; - cursor: pointer; - display: inline-block; - height: 25px; - line-height: 25px; - font-size: 1rem; - transition: .28s ease; - /* webkit (konqueror) browsers */ - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -[type="radio"] + label:before, -[type="radio"] + label:after { - content: ''; - position: absolute; - left: 0; - top: 0; - margin: 4px; - width: 16px; - height: 16px; - z-index: 0; - transition: .28s ease; -} - -/* Unchecked styles */ -[type="radio"]:not(:checked) + label:before, -[type="radio"]:not(:checked) + label:after, -[type="radio"]:checked + label:before, -[type="radio"]:checked + label:after, -[type="radio"].with-gap:checked + label:before, -[type="radio"].with-gap:checked + label:after { - border-radius: 50%; -} - -[type="radio"]:not(:checked) + label:before, -[type="radio"]:not(:checked) + label:after { - border: 2px solid #5a5a5a; -} - -[type="radio"]:not(:checked) + label:after { - z-index: -1; - -webkit-transform: scale(0); - transform: scale(0); -} - -/* Checked styles */ -[type="radio"]:checked + label:before { - border: 2px solid transparent; -} - -[type="radio"]:checked + label:after, -[type="radio"].with-gap:checked + label:before, -[type="radio"].with-gap:checked + label:after { - border: 2px solid #26a69a; -} - -[type="radio"]:checked + label:after, -[type="radio"].with-gap:checked + label:after { - background-color: #26a69a; - z-index: 0; -} - -[type="radio"]:checked + label:after { - -webkit-transform: scale(1.02); - transform: scale(1.02); -} - -/* Radio With gap */ -[type="radio"].with-gap:checked + label:after { - -webkit-transform: scale(0.5); - transform: scale(0.5); -} - -/* Focused styles */ -[type="radio"].tabbed:focus + label:before { - box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1); -} - -/* Disabled Radio With gap */ -[type="radio"].with-gap:disabled:checked + label:before { - border: 2px solid rgba(0, 0, 0, 0.26); -} - -[type="radio"].with-gap:disabled:checked + label:after { - border: none; - background-color: rgba(0, 0, 0, 0.26); -} - -/* Disabled style */ -[type="radio"]:disabled:not(:checked) + label:before, -[type="radio"]:disabled:checked + label:before { - background-color: transparent; - border-color: rgba(0, 0, 0, 0.26); -} - -[type="radio"]:disabled + label { - color: rgba(0, 0, 0, 0.26); -} - -[type="radio"]:disabled:not(:checked) + label:before { - border-color: rgba(0, 0, 0, 0.26); -} - -[type="radio"]:disabled:checked + label:after { - background-color: rgba(0, 0, 0, 0.26); - border-color: #BDBDBD; -} - -/* Checkboxes - ========================================================================== */ -/* CUSTOM CSS CHECKBOXES */ -form p { - margin-bottom: 10px; - text-align: left; -} - -form p:last-child { - margin-bottom: 0; -} - -/* Remove default checkbox */ -[type="checkbox"]:not(:checked), -[type="checkbox"]:checked { - position: absolute; - left: -9999px; - opacity: 0; -} - -[type="checkbox"] { - /* checkbox aspect */ -} - -[type="checkbox"] + label { - position: relative; - padding-left: 35px; - cursor: pointer; - display: inline-block; - height: 25px; - line-height: 25px; - font-size: 1rem; - -webkit-user-select: none; - /* webkit (safari, chrome) browsers */ - -moz-user-select: none; - /* mozilla browsers */ - -khtml-user-select: none; - /* webkit (konqueror) browsers */ - -ms-user-select: none; - /* IE10+ */ -} - -[type="checkbox"] + label:before, -[type="checkbox"]:not(.filled-in) + label:after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 18px; - height: 18px; - z-index: 0; - border: 2px solid #5a5a5a; - border-radius: 1px; - margin-top: 2px; - transition: .2s; -} - -[type="checkbox"]:not(.filled-in) + label:after { - border: 0; - -webkit-transform: scale(0); - transform: scale(0); -} - -[type="checkbox"]:not(:checked):disabled + label:before { - border: none; - background-color: rgba(0, 0, 0, 0.26); -} - -[type="checkbox"].tabbed:focus + label:after { - -webkit-transform: scale(1); - transform: scale(1); - border: 0; - border-radius: 50%; - box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1); - background-color: rgba(0, 0, 0, 0.1); -} - -[type="checkbox"]:checked + label:before { - top: -4px; - left: -5px; - width: 12px; - height: 22px; - border: 2px solid transparent; - border-right-color: #26a69a; - border-bottom-color: #26a69a; - -webkit-transform: rotate(40deg); - transform: rotate(40deg); - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-transform-origin: 100% 100%; - transform-origin: 100% 100%; -} - -[type="checkbox"]:checked:disabled + label:before { - border-right: 2px solid rgba(0, 0, 0, 0.26); - border-bottom: 2px solid rgba(0, 0, 0, 0.26); -} - -/* Indeterminate checkbox */ -[type="checkbox"]:indeterminate + label:before { - top: -11px; - left: -12px; - width: 10px; - height: 22px; - border-top: none; - border-left: none; - border-right: 2px solid #26a69a; - border-bottom: none; - -webkit-transform: rotate(90deg); - transform: rotate(90deg); - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-transform-origin: 100% 100%; - transform-origin: 100% 100%; -} - -[type="checkbox"]:indeterminate:disabled + label:before { - border-right: 2px solid rgba(0, 0, 0, 0.26); - background-color: transparent; -} - -[type="checkbox"].filled-in + label:after { - border-radius: 2px; -} - -[type="checkbox"].filled-in + label:before, -[type="checkbox"].filled-in + label:after { - content: ''; - left: 0; - position: absolute; - /* .1s delay is for check animation */ - transition: border .25s, background-color .25s, width .20s .1s, height .20s .1s, top .20s .1s, left .20s .1s; - z-index: 1; -} - -[type="checkbox"].filled-in:not(:checked) + label:before { - width: 0; - height: 0; - border: 3px solid transparent; - left: 6px; - top: 10px; - -webkit-transform: rotateZ(37deg); - transform: rotateZ(37deg); - -webkit-transform-origin: 20% 40%; - transform-origin: 100% 100%; -} - -[type="checkbox"].filled-in:not(:checked) + label:after { - height: 20px; - width: 20px; - background-color: transparent; - border: 2px solid #5a5a5a; - top: 0; - z-index: 0; -} - -[type="checkbox"].filled-in:checked + label:before { - top: 0; - left: 1px; - width: 8px; - height: 13px; - border-top: 2px solid transparent; - border-left: 2px solid transparent; - border-right: 2px solid #fff; - border-bottom: 2px solid #fff; - -webkit-transform: rotateZ(37deg); - transform: rotateZ(37deg); - -webkit-transform-origin: 100% 100%; - transform-origin: 100% 100%; -} - -[type="checkbox"].filled-in:checked + label:after { - top: 0; - width: 20px; - height: 20px; - border: 2px solid #26a69a; - background-color: #26a69a; - z-index: 0; -} - -[type="checkbox"].filled-in.tabbed:focus + label:after { - border-radius: 2px; - border-color: #5a5a5a; - background-color: rgba(0, 0, 0, 0.1); -} - -[type="checkbox"].filled-in.tabbed:checked:focus + label:after { - border-radius: 2px; - background-color: #26a69a; - border-color: #26a69a; -} - -[type="checkbox"].filled-in:disabled:not(:checked) + label:before { - background-color: transparent; - border: 2px solid transparent; -} - -[type="checkbox"].filled-in:disabled:not(:checked) + label:after { - border-color: transparent; - background-color: #BDBDBD; -} - -[type="checkbox"].filled-in:disabled:checked + label:before { - background-color: transparent; -} - -[type="checkbox"].filled-in:disabled:checked + label:after { - background-color: #BDBDBD; - border-color: #BDBDBD; -} - -/* Switch - ========================================================================== */ -.switch, -.switch * { - -webkit-user-select: none; - -moz-user-select: none; - -khtml-user-select: none; - -ms-user-select: none; -} - -.switch label { - cursor: pointer; -} - -.switch label input[type=checkbox] { - opacity: 0; - width: 0; - height: 0; -} - -.switch label input[type=checkbox]:checked + .lever { - background-color: #84c7c1; -} - -.switch label input[type=checkbox]:checked + .lever:after { - background-color: #26a69a; - left: 24px; -} - -.switch label .lever { - content: ""; - display: inline-block; - position: relative; - width: 40px; - height: 15px; - background-color: #818181; - border-radius: 15px; - margin-right: 10px; - transition: background 0.3s ease; - vertical-align: middle; - margin: 0 16px; -} - -.switch label .lever:after { - content: ""; - position: absolute; - display: inline-block; - width: 21px; - height: 21px; - background-color: #F1F1F1; - border-radius: 21px; - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4); - left: -5px; - top: -3px; - transition: left 0.3s ease, background .3s ease, box-shadow 0.1s ease; -} - -input[type=checkbox]:checked:not(:disabled) ~ .lever:active::after, -input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(38, 166, 154, 0.1); -} - -input[type=checkbox]:not(:disabled) ~ .lever:active:after, -input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(0, 0, 0, 0.08); -} - -.switch input[type=checkbox][disabled] + .lever { - cursor: default; -} - -.switch label input[type=checkbox][disabled] + .lever:after, -.switch label input[type=checkbox][disabled]:checked + .lever:after { - background-color: #BDBDBD; -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.css b/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.css new file mode 100644 index 000000000..a0d365b2a --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.css @@ -0,0 +1,12547 @@ +/*! + * Start Bootstrap - SB Admin 2 v4.0.0 (https://startbootstrap.com/template-overviews/sb-admin-2) + * Copyright 2013-2019 Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-sb-admin-2/blob/master/LICENSE) + */ + +/*! + * Bootstrap v4.2.1 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +:root { + --blue: #4e73df; + --indigo: #6610f2; + --purple: #6f42c1; + --pink: #e83e8c; + --red: #e74a3b; + --orange: #fd7e14; + --yellow: #f6c23e; + --green: #1cc88a; + --teal: #20c9a6; + --cyan: #36b9cc; + --white: #fff; + --gray: #858796; + --gray-dark: #5a5c69; + --primary: #4e73df; + --secondary: #858796; + --success: #1cc88a; + --info: #36b9cc; + --warning: #f6c23e; + --danger: #e74a3b; + --light: #f8f9fc; + --dark: #5a5c69; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 1100px; + --breakpoint-xl: 1400px; + --font-family-sans-serif: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +*, +*::before, +*::after { + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block; +} + +body { + margin: 0; + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #858796; + text-align: left; + background-color: #fff; +} + +[tabindex="-1"]:focus { + outline: 0 !important; +} + +hr { + -webkit-box-sizing: content-box; + box-sizing: content-box; + height: 0; + overflow: visible; +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0.5rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +abbr[title], +abbr[data-original-title] { + text-decoration: underline; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + text-decoration-skip-ink: none; +} + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; +} + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; +} + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; +} + +dt { + font-weight: 700; +} + +dd { + margin-bottom: .5rem; + margin-left: 0; +} + +blockquote { + margin: 0 0 1rem; +} + +b, +strong { + font-weight: bolder; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sub { + bottom: -.25em; +} + +sup { + top: -.5em; +} + +a { + color: #4e73df; + text-decoration: none; + background-color: transparent; +} + +a:hover { + color: #224abe; + text-decoration: underline; +} + +.fc-title { + color: #eee; +} + +.fc-time { + color: #eee; +} + +a:not([href]):not([tabindex]) { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):focus { + outline: 0; +} + +pre, +code, +kbd, +samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 1em; +} + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; +} + +figure { + margin: 0 0 1rem; +} + +img { + vertical-align: middle; + border-style: none; +} + +svg { + overflow: hidden; + vertical-align: middle; +} + +table { + border-collapse: collapse; +} + +caption { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #858796; + text-align: left; + caption-side: bottom; +} + +th { + text-align: inherit; +} + +label { + display: inline-block; + margin-bottom: 0.5rem; +} + +button { + border-radius: 0; +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + padding: 0; + border-style: none; +} + +input[type="radio"], +input[type="checkbox"] { + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} + +input[type="date"], +input[type="time"], +input[type="datetime-local"], +input[type="month"] { + -webkit-appearance: listbox; +} + +textarea { + overflow: auto; + resize: vertical; +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: .5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + outline-offset: -2px; + -webkit-appearance: none; +} + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; +} + +output { + display: inline-block; +} + +summary { + display: list-item; + cursor: pointer; +} + +template { + display: none; +} + +[hidden] { + display: none !important; +} + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + margin-bottom: 0.5rem; + font-family: inherit; + font-weight: 400; + line-height: 1.2; + color: inherit; +} + +h1, .h1 { + font-size: 2.5rem; +} + +h2, .h2 { + font-size: 2rem; +} + +h3, .h3 { + font-size: 1.75rem; +} + +h4, .h4 { + font-size: 1.5rem; +} + +h5, .h5 { + font-size: 1.25rem; +} + +h6, .h6 { + font-size: 1rem; +} + +.lead { + font-size: 1.25rem; + font-weight: 300; +} + +.display-1 { + font-size: 6rem; + font-weight: 300; + line-height: 1.2; +} + +.display-2 { + font-size: 5.5rem; + font-weight: 300; + line-height: 1.2; +} + +.display-3 { + font-size: 4.5rem; + font-weight: 300; + line-height: 1.2; +} + +.display-4 { + font-size: 3.5rem; + font-weight: 300; + line-height: 1.2; +} + +hr { + margin-top: 1rem; + margin-bottom: 1rem; + border: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); +} + +small, +.small { + font-size: 80%; + font-weight: 400; +} + +mark, +.mark { + padding: 0.2em; + background-color: #fcf8e3; +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + list-style: none; +} + +.list-inline-item { + display: inline-block; +} + +.list-inline-item:not(:last-child) { + margin-right: 0.5rem; +} + +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem; +} + +.blockquote-footer { + display: block; + font-size: 80%; + color: #858796; +} + +.blockquote-footer::before { + content: "\2014\00A0"; +} + +.img-fluid { + max-width: 100%; + height: auto; +} + +.img-thumbnail { + padding: 0.25rem; + background-color: #fff; + border: 1px solid #dddfeb; + border-radius: 0.35rem; + max-width: 100%; + height: auto; +} + +.figure { + display: inline-block; +} + +.figure-img { + margin-bottom: 0.5rem; + line-height: 1; +} + +.figure-caption { + font-size: 90%; + color: #858796; +} + +code { + font-size: 87.5%; + color: #e83e8c; + word-break: break-word; +} + +a > code { + color: inherit; +} + +kbd { + padding: 0.2rem 0.4rem; + font-size: 87.5%; + color: #fff; + background-color: #3a3b45; + border-radius: 0.2rem; +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; +} + +pre { + display: block; + font-size: 87.5%; + color: #3a3b45; +} + +pre code { + font-size: inherit; + color: inherit; + word-break: normal; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +.container { + width: 100%; + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media (min-width: 1100px) { + .container { + max-width: 960px; + } +} + +@media (min-width: 1400px) { + .container { + max-width: 1140px; + } +} + +.container-fluid { + width: 100%; + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-right: auto; + margin-left: auto; +} + +.row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -0.75rem; + margin-left: -0.75rem; +} + +.no-gutters { + margin-right: 0; + margin-left: 0; +} + +.no-gutters > .col, +.no-gutters > [class*="col-"] { + padding-right: 0; + padding-left: 0; +} + +.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, +.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, +.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, +.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, +.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, +.col-xl-auto { + position: relative; + width: 100%; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.col { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; +} + +.col-auto { + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; +} + +.col-1 { + -webkit-box-flex: 0; + -ms-flex: 0 0 8.33333%; + flex: 0 0 8.33333%; + max-width: 8.33333%; +} + +.col-2 { + -webkit-box-flex: 0; + -ms-flex: 0 0 16.66667%; + flex: 0 0 16.66667%; + max-width: 16.66667%; +} + +.col-3 { + -webkit-box-flex: 0; + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; +} + +.col-4 { + -webkit-box-flex: 0; + -ms-flex: 0 0 33.33333%; + flex: 0 0 33.33333%; + max-width: 33.33333%; +} + +.col-5 { + -webkit-box-flex: 0; + -ms-flex: 0 0 41.66667%; + flex: 0 0 41.66667%; + max-width: 41.66667%; +} + +.col-6 { + -webkit-box-flex: 0; + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; +} + +.col-7 { + -webkit-box-flex: 0; + -ms-flex: 0 0 58.33333%; + flex: 0 0 58.33333%; + max-width: 58.33333%; +} + +.col-8 { + -webkit-box-flex: 0; + -ms-flex: 0 0 66.66667%; + flex: 0 0 66.66667%; + max-width: 66.66667%; +} + +.col-9 { + -webkit-box-flex: 0; + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; +} + +.col-10 { + -webkit-box-flex: 0; + -ms-flex: 0 0 83.33333%; + flex: 0 0 83.33333%; + max-width: 83.33333%; +} + +.col-11 { + -webkit-box-flex: 0; + -ms-flex: 0 0 91.66667%; + flex: 0 0 91.66667%; + max-width: 91.66667%; +} + +.col-12 { + -webkit-box-flex: 0; + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; +} + +.order-first { + -webkit-box-ordinal-group: 0; + -ms-flex-order: -1; + order: -1; +} + +.order-last { + -webkit-box-ordinal-group: 14; + -ms-flex-order: 13; + order: 13; +} + +.order-0 { + -webkit-box-ordinal-group: 1; + -ms-flex-order: 0; + order: 0; +} + +.order-1 { + -webkit-box-ordinal-group: 2; + -ms-flex-order: 1; + order: 1; +} + +.order-2 { + -webkit-box-ordinal-group: 3; + -ms-flex-order: 2; + order: 2; +} + +.order-3 { + -webkit-box-ordinal-group: 4; + -ms-flex-order: 3; + order: 3; +} + +.order-4 { + -webkit-box-ordinal-group: 5; + -ms-flex-order: 4; + order: 4; +} + +.order-5 { + -webkit-box-ordinal-group: 6; + -ms-flex-order: 5; + order: 5; +} + +.order-6 { + -webkit-box-ordinal-group: 7; + -ms-flex-order: 6; + order: 6; +} + +.order-7 { + -webkit-box-ordinal-group: 8; + -ms-flex-order: 7; + order: 7; +} + +.order-8 { + -webkit-box-ordinal-group: 9; + -ms-flex-order: 8; + order: 8; +} + +.order-9 { + -webkit-box-ordinal-group: 10; + -ms-flex-order: 9; + order: 9; +} + +.order-10 { + -webkit-box-ordinal-group: 11; + -ms-flex-order: 10; + order: 10; +} + +.order-11 { + -webkit-box-ordinal-group: 12; + -ms-flex-order: 11; + order: 11; +} + +.order-12 { + -webkit-box-ordinal-group: 13; + -ms-flex-order: 12; + order: 12; +} + +.offset-1 { + margin-left: 8.33333%; +} + +.offset-2 { + margin-left: 16.66667%; +} + +.offset-3 { + margin-left: 25%; +} + +.offset-4 { + margin-left: 33.33333%; +} + +.offset-5 { + margin-left: 41.66667%; +} + +.offset-6 { + margin-left: 50%; +} + +.offset-7 { + margin-left: 58.33333%; +} + +.offset-8 { + margin-left: 66.66667%; +} + +.offset-9 { + margin-left: 75%; +} + +.offset-10 { + margin-left: 83.33333%; +} + +.offset-11 { + margin-left: 91.66667%; +} + +@media (min-width: 576px) { + .col-sm { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + + .col-sm-auto { + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + + .col-sm-1 { + -webkit-box-flex: 0; + -ms-flex: 0 0 8.33333%; + flex: 0 0 8.33333%; + max-width: 8.33333%; + } + + .col-sm-2 { + -webkit-box-flex: 0; + -ms-flex: 0 0 16.66667%; + flex: 0 0 16.66667%; + max-width: 16.66667%; + } + + .col-sm-3 { + -webkit-box-flex: 0; + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + + .col-sm-4 { + -webkit-box-flex: 0; + -ms-flex: 0 0 33.33333%; + flex: 0 0 33.33333%; + max-width: 33.33333%; + } + + .col-sm-5 { + -webkit-box-flex: 0; + -ms-flex: 0 0 41.66667%; + flex: 0 0 41.66667%; + max-width: 41.66667%; + } + + .col-sm-6 { + -webkit-box-flex: 0; + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + + .col-sm-7 { + -webkit-box-flex: 0; + -ms-flex: 0 0 58.33333%; + flex: 0 0 58.33333%; + max-width: 58.33333%; + } + + .col-sm-8 { + -webkit-box-flex: 0; + -ms-flex: 0 0 66.66667%; + flex: 0 0 66.66667%; + max-width: 66.66667%; + } + + .col-sm-9 { + -webkit-box-flex: 0; + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + + .col-sm-10 { + -webkit-box-flex: 0; + -ms-flex: 0 0 83.33333%; + flex: 0 0 83.33333%; + max-width: 83.33333%; + } + + .col-sm-11 { + -webkit-box-flex: 0; + -ms-flex: 0 0 91.66667%; + flex: 0 0 91.66667%; + max-width: 91.66667%; + } + + .col-sm-12 { + -webkit-box-flex: 0; + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + + .order-sm-first { + -webkit-box-ordinal-group: 0; + -ms-flex-order: -1; + order: -1; + } + + .order-sm-last { + -webkit-box-ordinal-group: 14; + -ms-flex-order: 13; + order: 13; + } + + .order-sm-0 { + -webkit-box-ordinal-group: 1; + -ms-flex-order: 0; + order: 0; + } + + .order-sm-1 { + -webkit-box-ordinal-group: 2; + -ms-flex-order: 1; + order: 1; + } + + .order-sm-2 { + -webkit-box-ordinal-group: 3; + -ms-flex-order: 2; + order: 2; + } + + .order-sm-3 { + -webkit-box-ordinal-group: 4; + -ms-flex-order: 3; + order: 3; + } + + .order-sm-4 { + -webkit-box-ordinal-group: 5; + -ms-flex-order: 4; + order: 4; + } + + .order-sm-5 { + -webkit-box-ordinal-group: 6; + -ms-flex-order: 5; + order: 5; + } + + .order-sm-6 { + -webkit-box-ordinal-group: 7; + -ms-flex-order: 6; + order: 6; + } + + .order-sm-7 { + -webkit-box-ordinal-group: 8; + -ms-flex-order: 7; + order: 7; + } + + .order-sm-8 { + -webkit-box-ordinal-group: 9; + -ms-flex-order: 8; + order: 8; + } + + .order-sm-9 { + -webkit-box-ordinal-group: 10; + -ms-flex-order: 9; + order: 9; + } + + .order-sm-10 { + -webkit-box-ordinal-group: 11; + -ms-flex-order: 10; + order: 10; + } + + .order-sm-11 { + -webkit-box-ordinal-group: 12; + -ms-flex-order: 11; + order: 11; + } + + .order-sm-12 { + -webkit-box-ordinal-group: 13; + -ms-flex-order: 12; + order: 12; + } + + .offset-sm-0 { + margin-left: 0; + } + + .offset-sm-1 { + margin-left: 8.33333%; + } + + .offset-sm-2 { + margin-left: 16.66667%; + } + + .offset-sm-3 { + margin-left: 25%; + } + + .offset-sm-4 { + margin-left: 33.33333%; + } + + .offset-sm-5 { + margin-left: 41.66667%; + } + + .offset-sm-6 { + margin-left: 50%; + } + + .offset-sm-7 { + margin-left: 58.33333%; + } + + .offset-sm-8 { + margin-left: 66.66667%; + } + + .offset-sm-9 { + margin-left: 75%; + } + + .offset-sm-10 { + margin-left: 83.33333%; + } + + .offset-sm-11 { + margin-left: 91.66667%; + } +} + +@media (min-width: 768px) { + .col-md { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + + .col-md-auto { + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + + .col-md-1 { + -webkit-box-flex: 0; + -ms-flex: 0 0 8.33333%; + flex: 0 0 8.33333%; + max-width: 8.33333%; + } + + .col-md-2 { + -webkit-box-flex: 0; + -ms-flex: 0 0 16.66667%; + flex: 0 0 16.66667%; + max-width: 16.66667%; + } + + .col-md-3 { + -webkit-box-flex: 0; + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + + .col-md-4 { + -webkit-box-flex: 0; + -ms-flex: 0 0 33.33333%; + flex: 0 0 33.33333%; + max-width: 33.33333%; + } + + .col-md-5 { + -webkit-box-flex: 0; + -ms-flex: 0 0 41.66667%; + flex: 0 0 41.66667%; + max-width: 41.66667%; + } + + .col-md-6 { + -webkit-box-flex: 0; + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + + .col-md-7 { + -webkit-box-flex: 0; + -ms-flex: 0 0 58.33333%; + flex: 0 0 58.33333%; + max-width: 58.33333%; + } + + .col-md-8 { + -webkit-box-flex: 0; + -ms-flex: 0 0 66.66667%; + flex: 0 0 66.66667%; + max-width: 66.66667%; + } + + .col-md-9 { + -webkit-box-flex: 0; + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + + .col-md-10 { + -webkit-box-flex: 0; + -ms-flex: 0 0 83.33333%; + flex: 0 0 83.33333%; + max-width: 83.33333%; + } + + .col-md-11 { + -webkit-box-flex: 0; + -ms-flex: 0 0 91.66667%; + flex: 0 0 91.66667%; + max-width: 91.66667%; + } + + .col-md-12 { + -webkit-box-flex: 0; + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + + .order-md-first { + -webkit-box-ordinal-group: 0; + -ms-flex-order: -1; + order: -1; + } + + .order-md-last { + -webkit-box-ordinal-group: 14; + -ms-flex-order: 13; + order: 13; + } + + .order-md-0 { + -webkit-box-ordinal-group: 1; + -ms-flex-order: 0; + order: 0; + } + + .order-md-1 { + -webkit-box-ordinal-group: 2; + -ms-flex-order: 1; + order: 1; + } + + .order-md-2 { + -webkit-box-ordinal-group: 3; + -ms-flex-order: 2; + order: 2; + } + + .order-md-3 { + -webkit-box-ordinal-group: 4; + -ms-flex-order: 3; + order: 3; + } + + .order-md-4 { + -webkit-box-ordinal-group: 5; + -ms-flex-order: 4; + order: 4; + } + + .order-md-5 { + -webkit-box-ordinal-group: 6; + -ms-flex-order: 5; + order: 5; + } + + .order-md-6 { + -webkit-box-ordinal-group: 7; + -ms-flex-order: 6; + order: 6; + } + + .order-md-7 { + -webkit-box-ordinal-group: 8; + -ms-flex-order: 7; + order: 7; + } + + .order-md-8 { + -webkit-box-ordinal-group: 9; + -ms-flex-order: 8; + order: 8; + } + + .order-md-9 { + -webkit-box-ordinal-group: 10; + -ms-flex-order: 9; + order: 9; + } + + .order-md-10 { + -webkit-box-ordinal-group: 11; + -ms-flex-order: 10; + order: 10; + } + + .order-md-11 { + -webkit-box-ordinal-group: 12; + -ms-flex-order: 11; + order: 11; + } + + .order-md-12 { + -webkit-box-ordinal-group: 13; + -ms-flex-order: 12; + order: 12; + } + + .offset-md-0 { + margin-left: 0; + } + + .offset-md-1 { + margin-left: 8.33333%; + } + + .offset-md-2 { + margin-left: 16.66667%; + } + + .offset-md-3 { + margin-left: 25%; + } + + .offset-md-4 { + margin-left: 33.33333%; + } + + .offset-md-5 { + margin-left: 41.66667%; + } + + .offset-md-6 { + margin-left: 50%; + } + + .offset-md-7 { + margin-left: 58.33333%; + } + + .offset-md-8 { + margin-left: 66.66667%; + } + + .offset-md-9 { + margin-left: 75%; + } + + .offset-md-10 { + margin-left: 83.33333%; + } + + .offset-md-11 { + margin-left: 91.66667%; + } +} + +@media (min-width: 1100px) { + .col-lg { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + + .col-lg-auto { + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + + .col-lg-1 { + -webkit-box-flex: 0; + -ms-flex: 0 0 8.33333%; + flex: 0 0 8.33333%; + max-width: 8.33333%; + } + + .col-lg-2 { + -webkit-box-flex: 0; + -ms-flex: 0 0 16.66667%; + flex: 0 0 16.66667%; + max-width: 16.66667%; + } + + .col-lg-3 { + -webkit-box-flex: 0; + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + + .col-lg-4 { + -webkit-box-flex: 0; + -ms-flex: 0 0 33.33333%; + flex: 0 0 33.33333%; + max-width: 33.33333%; + } + + .col-lg-5 { + -webkit-box-flex: 0; + -ms-flex: 0 0 41.66667%; + flex: 0 0 41.66667%; + max-width: 41.66667%; + } + + .col-lg-6 { + -webkit-box-flex: 0; + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + + .col-lg-7 { + -webkit-box-flex: 0; + -ms-flex: 0 0 58.33333%; + flex: 0 0 58.33333%; + max-width: 58.33333%; + } + + .col-lg-8 { + -webkit-box-flex: 0; + -ms-flex: 0 0 66.66667%; + flex: 0 0 66.66667%; + max-width: 66.66667%; + } + + .col-lg-9 { + -webkit-box-flex: 0; + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + + .col-lg-10 { + -webkit-box-flex: 0; + -ms-flex: 0 0 83.33333%; + flex: 0 0 83.33333%; + max-width: 83.33333%; + } + + .col-lg-11 { + -webkit-box-flex: 0; + -ms-flex: 0 0 91.66667%; + flex: 0 0 91.66667%; + max-width: 91.66667%; + } + + .col-lg-12 { + -webkit-box-flex: 0; + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + + .order-lg-first { + -webkit-box-ordinal-group: 0; + -ms-flex-order: -1; + order: -1; + } + + .order-lg-last { + -webkit-box-ordinal-group: 14; + -ms-flex-order: 13; + order: 13; + } + + .order-lg-0 { + -webkit-box-ordinal-group: 1; + -ms-flex-order: 0; + order: 0; + } + + .order-lg-1 { + -webkit-box-ordinal-group: 2; + -ms-flex-order: 1; + order: 1; + } + + .order-lg-2 { + -webkit-box-ordinal-group: 3; + -ms-flex-order: 2; + order: 2; + } + + .order-lg-3 { + -webkit-box-ordinal-group: 4; + -ms-flex-order: 3; + order: 3; + } + + .order-lg-4 { + -webkit-box-ordinal-group: 5; + -ms-flex-order: 4; + order: 4; + } + + .order-lg-5 { + -webkit-box-ordinal-group: 6; + -ms-flex-order: 5; + order: 5; + } + + .order-lg-6 { + -webkit-box-ordinal-group: 7; + -ms-flex-order: 6; + order: 6; + } + + .order-lg-7 { + -webkit-box-ordinal-group: 8; + -ms-flex-order: 7; + order: 7; + } + + .order-lg-8 { + -webkit-box-ordinal-group: 9; + -ms-flex-order: 8; + order: 8; + } + + .order-lg-9 { + -webkit-box-ordinal-group: 10; + -ms-flex-order: 9; + order: 9; + } + + .order-lg-10 { + -webkit-box-ordinal-group: 11; + -ms-flex-order: 10; + order: 10; + } + + .order-lg-11 { + -webkit-box-ordinal-group: 12; + -ms-flex-order: 11; + order: 11; + } + + .order-lg-12 { + -webkit-box-ordinal-group: 13; + -ms-flex-order: 12; + order: 12; + } + + .offset-lg-0 { + margin-left: 0; + } + + .offset-lg-1 { + margin-left: 8.33333%; + } + + .offset-lg-2 { + margin-left: 16.66667%; + } + + .offset-lg-3 { + margin-left: 25%; + } + + .offset-lg-4 { + margin-left: 33.33333%; + } + + .offset-lg-5 { + margin-left: 41.66667%; + } + + .offset-lg-6 { + margin-left: 50%; + } + + .offset-lg-7 { + margin-left: 58.33333%; + } + + .offset-lg-8 { + margin-left: 66.66667%; + } + + .offset-lg-9 { + margin-left: 75%; + } + + .offset-lg-10 { + margin-left: 83.33333%; + } + + .offset-lg-11 { + margin-left: 91.66667%; + } +} + +@media (min-width: 1400px) { + .col-xl { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + max-width: 100%; + } + + .col-xl-auto { + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + width: auto; + max-width: 100%; + } + + .col-xl-1 { + -webkit-box-flex: 0; + -ms-flex: 0 0 8.33333%; + flex: 0 0 8.33333%; + max-width: 8.33333%; + } + + .col-xl-2 { + -webkit-box-flex: 0; + -ms-flex: 0 0 16.66667%; + flex: 0 0 16.66667%; + max-width: 16.66667%; + } + + .col-xl-3 { + -webkit-box-flex: 0; + -ms-flex: 0 0 25%; + flex: 0 0 25%; + max-width: 25%; + } + + .col-xl-4 { + -webkit-box-flex: 0; + -ms-flex: 0 0 33.33333%; + flex: 0 0 33.33333%; + max-width: 33.33333%; + } + + .col-xl-5 { + -webkit-box-flex: 0; + -ms-flex: 0 0 41.66667%; + flex: 0 0 41.66667%; + max-width: 41.66667%; + } + + .col-xl-6 { + -webkit-box-flex: 0; + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; + } + + .col-xl-7 { + -webkit-box-flex: 0; + -ms-flex: 0 0 58.33333%; + flex: 0 0 58.33333%; + max-width: 58.33333%; + } + + .col-xl-8 { + -webkit-box-flex: 0; + -ms-flex: 0 0 66.66667%; + flex: 0 0 66.66667%; + max-width: 66.66667%; + } + + .col-xl-9 { + -webkit-box-flex: 0; + -ms-flex: 0 0 75%; + flex: 0 0 75%; + max-width: 75%; + } + + .col-xl-10 { + -webkit-box-flex: 0; + -ms-flex: 0 0 83.33333%; + flex: 0 0 83.33333%; + max-width: 83.33333%; + } + + .col-xl-11 { + -webkit-box-flex: 0; + -ms-flex: 0 0 91.66667%; + flex: 0 0 91.66667%; + max-width: 91.66667%; + } + + .col-xl-12 { + -webkit-box-flex: 0; + -ms-flex: 0 0 100%; + flex: 0 0 100%; + max-width: 100%; + } + + .order-xl-first { + -webkit-box-ordinal-group: 0; + -ms-flex-order: -1; + order: -1; + } + + .order-xl-last { + -webkit-box-ordinal-group: 14; + -ms-flex-order: 13; + order: 13; + } + + .order-xl-0 { + -webkit-box-ordinal-group: 1; + -ms-flex-order: 0; + order: 0; + } + + .order-xl-1 { + -webkit-box-ordinal-group: 2; + -ms-flex-order: 1; + order: 1; + } + + .order-xl-2 { + -webkit-box-ordinal-group: 3; + -ms-flex-order: 2; + order: 2; + } + + .order-xl-3 { + -webkit-box-ordinal-group: 4; + -ms-flex-order: 3; + order: 3; + } + + .order-xl-4 { + -webkit-box-ordinal-group: 5; + -ms-flex-order: 4; + order: 4; + } + + .order-xl-5 { + -webkit-box-ordinal-group: 6; + -ms-flex-order: 5; + order: 5; + } + + .order-xl-6 { + -webkit-box-ordinal-group: 7; + -ms-flex-order: 6; + order: 6; + } + + .order-xl-7 { + -webkit-box-ordinal-group: 8; + -ms-flex-order: 7; + order: 7; + } + + .order-xl-8 { + -webkit-box-ordinal-group: 9; + -ms-flex-order: 8; + order: 8; + } + + .order-xl-9 { + -webkit-box-ordinal-group: 10; + -ms-flex-order: 9; + order: 9; + } + + .order-xl-10 { + -webkit-box-ordinal-group: 11; + -ms-flex-order: 10; + order: 10; + } + + .order-xl-11 { + -webkit-box-ordinal-group: 12; + -ms-flex-order: 11; + order: 11; + } + + .order-xl-12 { + -webkit-box-ordinal-group: 13; + -ms-flex-order: 12; + order: 12; + } + + .offset-xl-0 { + margin-left: 0; + } + + .offset-xl-1 { + margin-left: 8.33333%; + } + + .offset-xl-2 { + margin-left: 16.66667%; + } + + .offset-xl-3 { + margin-left: 25%; + } + + .offset-xl-4 { + margin-left: 33.33333%; + } + + .offset-xl-5 { + margin-left: 41.66667%; + } + + .offset-xl-6 { + margin-left: 50%; + } + + .offset-xl-7 { + margin-left: 58.33333%; + } + + .offset-xl-8 { + margin-left: 66.66667%; + } + + .offset-xl-9 { + margin-left: 75%; + } + + .offset-xl-10 { + margin-left: 83.33333%; + } + + .offset-xl-11 { + margin-left: 91.66667%; + } +} + +.table { + width: 100%; + background-color: transparent; +} + +.table th, +.table td { + padding: 0.75rem; + vertical-align: top; + border-top: 1px solid #dddfeb; +} + +.table thead th { + vertical-align: bottom; + border-bottom: 2px solid #dddfeb; +} + +.table tbody + tbody { + border-top: 2px solid #dddfeb; +} + +.table-sm th, +.table-sm td { + padding: 0.3rem; +} + +.table-bordered { + border: 1px solid #dddfeb; +} + +.table-bordered th, +.table-bordered td { + border: 1px solid #dddfeb; +} + +.table-bordered thead th, +.table-bordered thead td { + border-bottom-width: 2px; +} + +.table-borderless th, +.table-borderless td, +.table-borderless thead th, +.table-borderless tbody + tbody { + border: 0; +} + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, 0.05); +} + +.table-hover tbody tr:hover { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-primary, +.table-primary > th, +.table-primary > td { + background-color: #cdd8f6; +} + +.table-primary th, +.table-primary td, +.table-primary thead th, +.table-primary tbody + tbody { + border-color: #a3b6ee; +} + +.table-hover .table-primary:hover { + background-color: #b7c7f2; +} + +.table-hover .table-primary:hover > td, +.table-hover .table-primary:hover > th { + background-color: #b7c7f2; +} + +.table-secondary, +.table-secondary > th, +.table-secondary > td { + background-color: #dddde2; +} + +.table-secondary th, +.table-secondary td, +.table-secondary thead th, +.table-secondary tbody + tbody { + border-color: #c0c1c8; +} + +.table-hover .table-secondary:hover { + background-color: #cfcfd6; +} + +.table-hover .table-secondary:hover > td, +.table-hover .table-secondary:hover > th { + background-color: #cfcfd6; +} + +.table-success, +.table-success > th, +.table-success > td { + background-color: #bff0de; +} + +.table-success th, +.table-success td, +.table-success thead th, +.table-success tbody + tbody { + border-color: #89e2c2; +} + +.table-hover .table-success:hover { + background-color: #aaebd3; +} + +.table-hover .table-success:hover > td, +.table-hover .table-success:hover > th { + background-color: #aaebd3; +} + +.table-info, +.table-info > th, +.table-info > td { + background-color: #c7ebf1; +} + +.table-info th, +.table-info td, +.table-info thead th, +.table-info tbody + tbody { + border-color: #96dbe4; +} + +.table-hover .table-info:hover { + background-color: #b3e4ec; +} + +.table-hover .table-info:hover > td, +.table-hover .table-info:hover > th { + background-color: #b3e4ec; +} + +.table-warning, +.table-warning > th, +.table-warning > td { + background-color: #fceec9; +} + +.table-warning th, +.table-warning td, +.table-warning thead th, +.table-warning tbody + tbody { + border-color: #fadf9b; +} + +.table-hover .table-warning:hover { + background-color: #fbe6b1; +} + +.table-hover .table-warning:hover > td, +.table-hover .table-warning:hover > th { + background-color: #fbe6b1; +} + +.table-danger, +.table-danger > th, +.table-danger > td { + background-color: #f8ccc8; +} + +.table-danger th, +.table-danger td, +.table-danger thead th, +.table-danger tbody + tbody { + border-color: #f3a199; +} + +.table-hover .table-danger:hover { + background-color: #f5b7b1; +} + +.table-hover .table-danger:hover > td, +.table-hover .table-danger:hover > th { + background-color: #f5b7b1; +} + +.table-light, +.table-light > th, +.table-light > td { + background-color: #fdfdfe; +} + +.table-light th, +.table-light td, +.table-light thead th, +.table-light tbody + tbody { + border-color: #fbfcfd; +} + +.table-hover .table-light:hover { + background-color: #ececf6; +} + +.table-hover .table-light:hover > td, +.table-hover .table-light:hover > th { + background-color: #ececf6; +} + +.table-dark, +.table-dark > th, +.table-dark > td { + background-color: #d1d1d5; +} + +.table-dark th, +.table-dark td, +.table-dark thead th, +.table-dark tbody + tbody { + border-color: #a9aab1; +} + +.table-hover .table-dark:hover { + background-color: #c4c4c9; +} + +.table-hover .table-dark:hover > td, +.table-hover .table-dark:hover > th { + background-color: #c4c4c9; +} + +.table-active, +.table-active > th, +.table-active > td { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-hover .table-active:hover { + background-color: rgba(0, 0, 0, 0.075); +} + +.table-hover .table-active:hover > td, +.table-hover .table-active:hover > th { + background-color: rgba(0, 0, 0, 0.075); +} + +.table .thead-dark th { + color: #fff; + background-color: #3a3b45; + border-color: #4b4d5a; +} + +.table .thead-light th { + color: #6e707e; + background-color: #eaecf4; + border-color: #dddfeb; +} + +.table-dark { + color: #fff; + background-color: #3a3b45; +} + +.table-dark th, +.table-dark td, +.table-dark thead th { + border-color: #4b4d5a; +} + +.table-dark.table-bordered { + border: 0; +} + +.table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.05); +} + +.table-dark.table-hover tbody tr:hover { + background-color: rgba(255, 255, 255, 0.075); +} + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + + .table-responsive-sm > .table-bordered { + border: 0; + } +} + +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + + .table-responsive-md > .table-bordered { + border: 0; + } +} + +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + + .table-responsive-lg > .table-bordered { + border: 0; + } +} + +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + + .table-responsive-xl > .table-bordered { + border: 0; + } +} + +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; +} + +.table-responsive > .table-bordered { + border: 0; +} + +.form-control { + display: block; + width: 100%; + height: calc(2.25rem + 2px); + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #6e707e; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #d1d3e2; + border-radius: 0.35rem; + -webkit-transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; +} + +@media screen and (prefers-reduced-motion: reduce) { + .form-control { + -webkit-transition: none; + transition: none; + } +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} + +.form-control:focus { + color: #6e707e; + background-color: #fff; + border-color: #bac8f3; + outline: 0; + -webkit-box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); + box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); +} + +.form-control::-webkit-input-placeholder { + color: #858796; + opacity: 1; +} + +.form-control:-ms-input-placeholder { + color: #858796; + opacity: 1; +} + +.form-control::-ms-input-placeholder { + color: #858796; + opacity: 1; +} + +.form-control::placeholder { + color: #858796; + opacity: 1; +} + +.form-control:disabled, .form-control[readonly] { + background-color: #eaecf4; + opacity: 1; +} + +select.form-control:focus::-ms-value { + color: #6e707e; + background-color: #fff; +} + +.form-control-file, +.form-control-range { + display: block; + width: 100%; +} + +.col-form-label { + padding-top: calc(0.375rem + 1px); + padding-bottom: calc(0.375rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; +} + +.col-form-label-lg { + padding-top: calc(0.5rem + 1px); + padding-bottom: calc(0.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5; +} + +.col-form-label-sm { + padding-top: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px); + font-size: 0.875rem; + line-height: 1.5; +} + +.form-control-plaintext { + display: block; + width: 100%; + padding-top: 0.375rem; + padding-bottom: 0.375rem; + margin-bottom: 0; + line-height: 1.5; + color: #858796; + background-color: transparent; + border: solid transparent; + border-width: 1px 0; +} + +.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { + padding-right: 0; + padding-left: 0; +} + +.form-control-sm { + height: calc(1.8125rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.form-control-lg { + height: calc(2.875rem + 2px); + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +select.form-control[size], select.form-control[multiple] { + height: auto; +} + +textarea.form-control { + height: auto; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-text { + display: block; + margin-top: 0.25rem; +} + +.form-row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; +} + +.form-row > .col, +.form-row > [class*="col-"] { + padding-right: 5px; + padding-left: 5px; +} + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem; +} + +.form-check-input { + position: absolute; + margin-top: 0.3rem; + margin-left: -1.25rem; +} + +.form-check-input:disabled ~ .form-check-label { + color: #858796; +} + +.form-check-label { + margin-bottom: 0; +} + +.form-check-inline { + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding-left: 0; + margin-right: 0.75rem; +} + +.form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: 0.3125rem; + margin-left: 0; +} + +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #1cc88a; +} + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(28, 200, 138, 0.9); + border-radius: 0.35rem; +} + +.was-validated .form-control:valid, .form-control.is-valid { + border-color: #1cc88a; + padding-right: 2.25rem; + background-repeat: no-repeat; + background-position: center right calc(2.25rem / 4); + background-size: calc(2.25rem / 2) calc(2.25rem / 2); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%231cc88a' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); +} + +.was-validated .form-control:valid:focus, .form-control.is-valid:focus { + border-color: #1cc88a; + -webkit-box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); + box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); +} + +.was-validated .form-control:valid ~ .valid-feedback, +.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback, +.form-control.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: 2.25rem; + background-position: top calc(2.25rem / 4) right calc(2.25rem / 4); +} + +.was-validated .custom-select:valid, .custom-select.is-valid { + border-color: #1cc88a; + padding-right: 3.4375rem; + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%231cc88a' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") no-repeat center right 1.75rem/1.125rem 1.125rem; +} + +.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { + border-color: #1cc88a; + -webkit-box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); + box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); +} + +.was-validated .custom-select:valid ~ .valid-feedback, +.was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback, +.custom-select.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .form-control-file:valid ~ .valid-feedback, +.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback, +.form-control-file.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { + color: #1cc88a; +} + +.was-validated .form-check-input:valid ~ .valid-feedback, +.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, +.form-check-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { + color: #1cc88a; +} + +.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { + border-color: #1cc88a; +} + +.was-validated .custom-control-input:valid ~ .valid-feedback, +.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback, +.custom-control-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { + border-color: #34e3a4; + background-color: #34e3a4; +} + +.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { + -webkit-box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); + box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); +} + +.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #1cc88a; +} + +.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { + border-color: #1cc88a; +} + +.was-validated .custom-file-input:valid ~ .valid-feedback, +.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback, +.custom-file-input.is-valid ~ .valid-tooltip { + display: block; +} + +.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { + border-color: #1cc88a; + -webkit-box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); + box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.25); +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #e74a3b; +} + +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(231, 74, 59, 0.9); + border-radius: 0.35rem; +} + +.was-validated .form-control:invalid, .form-control.is-invalid { + border-color: #e74a3b; + padding-right: 2.25rem; + background-repeat: no-repeat; + background-position: center right calc(2.25rem / 4); + background-size: calc(2.25rem / 2) calc(2.25rem / 2); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23e74a3b' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E"); +} + +.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { + border-color: #e74a3b; + -webkit-box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); + box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); +} + +.was-validated .form-control:invalid ~ .invalid-feedback, +.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback, +.form-control.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: 2.25rem; + background-position: top calc(2.25rem / 4) right calc(2.25rem / 4); +} + +.was-validated .custom-select:invalid, .custom-select.is-invalid { + border-color: #e74a3b; + padding-right: 3.4375rem; + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23e74a3b' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") no-repeat center right 1.75rem/1.125rem 1.125rem; +} + +.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { + border-color: #e74a3b; + -webkit-box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); + box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); +} + +.was-validated .custom-select:invalid ~ .invalid-feedback, +.was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback, +.custom-select.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .form-control-file:invalid ~ .invalid-feedback, +.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback, +.form-control-file.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { + color: #e74a3b; +} + +.was-validated .form-check-input:invalid ~ .invalid-feedback, +.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, +.form-check-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { + color: #e74a3b; +} + +.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { + border-color: #e74a3b; +} + +.was-validated .custom-control-input:invalid ~ .invalid-feedback, +.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback, +.custom-control-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { + border-color: #ed7468; + background-color: #ed7468; +} + +.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { + -webkit-box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); + box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); +} + +.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #e74a3b; +} + +.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { + border-color: #e74a3b; +} + +.was-validated .custom-file-input:invalid ~ .invalid-feedback, +.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback, +.custom-file-input.is-invalid ~ .invalid-tooltip { + display: block; +} + +.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { + border-color: #e74a3b; + -webkit-box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); + box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.25); +} + +.form-inline { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.form-inline .form-check { + width: 100%; +} + +@media (min-width: 576px) { + .form-inline label { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + margin-bottom: 0; + } + + .form-inline .form-group { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + margin-bottom: 0; + } + + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + + .form-inline .form-control-plaintext { + display: inline-block; + } + + .form-inline .input-group, + .form-inline .custom-select { + width: auto; + } + + .form-inline .form-check { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + width: auto; + padding-left: 0; + } + + .form-inline .form-check-input { + position: relative; + margin-top: 0; + margin-right: 0.25rem; + margin-left: 0; + } + + .form-inline .custom-control { + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + } + + .form-inline .custom-control-label { + margin-bottom: 0; + } +} + +.btn { + display: inline-block; + font-weight: 400; + color: #858796; + text-align: center; + vertical-align: middle; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.35rem; + -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; +} + +@media screen and (prefers-reduced-motion: reduce) { + .btn { + -webkit-transition: none; + transition: none; + } +} + +.btn:hover:not(.disabled):not(:disabled) { + color: #ccc; + text-decoration: none; +} + +.btn:focus, .btn.focus { + outline: 0; + -webkit-box-shadow: 0 0 0 0.2rem rgba(82, 82, 82, 0.25); + box-shadow: 0 0 0 0.2rem rgba(82, 82, 82, 0.25); +} + +.btn.disabled, .btn:disabled { + opacity: 0.65; +} + +.btn:not(:disabled):not(.disabled) { + cursor: pointer; +} + +a.btn.disabled, +fieldset:disabled a.btn { + pointer-events: none; +} + +.btn-primary { + color: #fff; + background-color: #4e73df; + border-color: #4e73df; +} + +.btn-primary:hover { + color: #fff; + background-color: #2e59d9; + border-color: #2653d4; +} + +.btn-primary:focus, .btn-primary.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(105, 136, 228, 0.5); + box-shadow: 0 0 0 0.2rem rgba(105, 136, 228, 0.5); +} + +.btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #4e73df; + border-color: #4e73df; +} + +.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, +.show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #2653d4; + border-color: #244ec9; +} + +.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, +.show > .btn-primary.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(105, 136, 228, 0.5); + box-shadow: 0 0 0 0.2rem rgba(105, 136, 228, 0.5); +} + +.btn-secondary { + color: #fff; + background-color: #858796; + border-color: #858796; +} + +.btn-secondary:hover { + color: #fff; + background-color: #717384; + border-color: #6b6d7d; +} + +.btn-secondary:focus, .btn-secondary.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(151, 153, 166, 0.5); + box-shadow: 0 0 0 0.2rem rgba(151, 153, 166, 0.5); +} + +.btn-secondary.disabled, .btn-secondary:disabled { + color: #fff; + background-color: #858796; + border-color: #858796; +} + +.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, +.show > .btn-secondary.dropdown-toggle { + color: #fff; + background-color: #6b6d7d; + border-color: #656776; +} + +.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, +.show > .btn-secondary.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(151, 153, 166, 0.5); + box-shadow: 0 0 0 0.2rem rgba(151, 153, 166, 0.5); +} + +.btn-success { + color: #fff; + background-color: #1cc88a; + border-color: #1cc88a; +} + +.btn-success:hover { + color: #fff; + background-color: #17a673; + border-color: #169b6b; +} + +.btn-success:focus, .btn-success.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(62, 208, 156, 0.5); + box-shadow: 0 0 0 0.2rem rgba(62, 208, 156, 0.5); +} + +.btn-success.disabled, .btn-success:disabled { + color: #fff; + background-color: #1cc88a; + border-color: #1cc88a; +} + +.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, +.show > .btn-success.dropdown-toggle { + color: #fff; + background-color: #169b6b; + border-color: #149063; +} + +.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, +.show > .btn-success.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(62, 208, 156, 0.5); + box-shadow: 0 0 0 0.2rem rgba(62, 208, 156, 0.5); +} + +.btn-info { + color: #fff; + background-color: #36b9cc; + border-color: #36b9cc; +} + +.btn-info:hover { + color: #fff; + background-color: #2c9faf; + border-color: #2a96a5; +} + +.btn-info:focus, .btn-info.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(84, 196, 212, 0.5); + box-shadow: 0 0 0 0.2rem rgba(84, 196, 212, 0.5); +} + +.btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #36b9cc; + border-color: #36b9cc; +} + +.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, +.show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #2a96a5; + border-color: #278c9b; +} + +.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, +.show > .btn-info.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(84, 196, 212, 0.5); + box-shadow: 0 0 0 0.2rem rgba(84, 196, 212, 0.5); +} + +.btn-warning { + color: #fff; + background-color: #f6c23e; + border-color: #f6c23e; +} + +.btn-warning:hover { + color: #fff; + background-color: #f4b619; + border-color: #f4b30d; +} + +.btn-warning:focus, .btn-warning.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(247, 203, 91, 0.5); + box-shadow: 0 0 0 0.2rem rgba(247, 203, 91, 0.5); +} + +.btn-warning.disabled, .btn-warning:disabled { + color: #fff; + background-color: #f6c23e; + border-color: #f6c23e; +} + +.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, +.show > .btn-warning.dropdown-toggle { + color: #fff; + background-color: #f4b30d; + border-color: #e9aa0b; +} + +.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, +.show > .btn-warning.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(247, 203, 91, 0.5); + box-shadow: 0 0 0 0.2rem rgba(247, 203, 91, 0.5); +} + +.btn-danger { + color: #fff; + background-color: #e74a3b; + border-color: #e74a3b; +} + +.btn-danger:hover { + color: #fff; + background-color: #e02d1b; + border-color: #d52a1a; +} + +.btn-danger:focus, .btn-danger.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(235, 101, 88, 0.5); + box-shadow: 0 0 0 0.2rem rgba(235, 101, 88, 0.5); +} + +.btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #e74a3b; + border-color: #e74a3b; +} + +.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, +.show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #d52a1a; + border-color: #ca2819; +} + +.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, +.show > .btn-danger.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(235, 101, 88, 0.5); + box-shadow: 0 0 0 0.2rem rgba(235, 101, 88, 0.5); +} + +.btn-light { + color: #3a3b45; + background-color: #f8f9fc; + border-color: #f8f9fc; +} + +.btn-light:hover { + color: #3a3b45; + background-color: #dde2f1; + border-color: #d4daed; +} + +.btn-light:focus, .btn-light.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 221, 225, 0.5); + box-shadow: 0 0 0 0.2rem rgba(220, 221, 225, 0.5); +} + +.btn-light.disabled, .btn-light:disabled { + color: #3a3b45; + background-color: #f8f9fc; + border-color: #f8f9fc; +} + +.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, +.show > .btn-light.dropdown-toggle { + color: #3a3b45; + background-color: #d4daed; + border-color: #cbd3e9; +} + +.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, +.show > .btn-light.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(220, 221, 225, 0.5); + box-shadow: 0 0 0 0.2rem rgba(220, 221, 225, 0.5); +} + +.btn-dark { + color: #fff; + background-color: #5a5c69; + border-color: #5a5c69; +} + +.btn-dark:hover { + color: #fff; + background-color: #484a54; + border-color: #42444e; +} + +.btn-dark:focus, .btn-dark.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(115, 116, 128, 0.5); + box-shadow: 0 0 0 0.2rem rgba(115, 116, 128, 0.5); +} + +.btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #5a5c69; + border-color: #5a5c69; +} + +.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, +.show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #42444e; + border-color: #3d3e47; +} + +.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, +.show > .btn-dark.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(115, 116, 128, 0.5); + box-shadow: 0 0 0 0.2rem rgba(115, 116, 128, 0.5); +} + +.btn-outline-primary { + color: #4e73df; + border-color: #4e73df; +} + +.btn-outline-primary:hover { + color: #fff; + background-color: #4e73df; + border-color: #4e73df; +} + +.btn-outline-primary:focus, .btn-outline-primary.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.5); + box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.5); +} + +.btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #4e73df; + background-color: transparent; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, +.show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: #4e73df; + border-color: #4e73df; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-primary.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.5); + box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.5); +} + +.btn-outline-secondary { + color: #858796; + border-color: #858796; +} + +.btn-outline-secondary:hover { + color: #fff; + background-color: #858796; + border-color: #858796; +} + +.btn-outline-secondary:focus, .btn-outline-secondary.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(133, 135, 150, 0.5); + box-shadow: 0 0 0 0.2rem rgba(133, 135, 150, 0.5); +} + +.btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #858796; + background-color: transparent; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, +.show > .btn-outline-secondary.dropdown-toggle { + color: #fff; + background-color: #858796; + border-color: #858796; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-secondary.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(133, 135, 150, 0.5); + box-shadow: 0 0 0 0.2rem rgba(133, 135, 150, 0.5); +} + +.btn-outline-success { + color: #1cc88a; + border-color: #1cc88a; +} + +.btn-outline-success:hover { + color: #fff; + background-color: #1cc88a; + border-color: #1cc88a; +} + +.btn-outline-success:focus, .btn-outline-success.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.5); + box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.5); +} + +.btn-outline-success.disabled, .btn-outline-success:disabled { + color: #1cc88a; + background-color: transparent; +} + +.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, +.show > .btn-outline-success.dropdown-toggle { + color: #fff; + background-color: #1cc88a; + border-color: #1cc88a; +} + +.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-success.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.5); + box-shadow: 0 0 0 0.2rem rgba(28, 200, 138, 0.5); +} + +.btn-outline-info { + color: #36b9cc; + border-color: #36b9cc; +} + +.btn-outline-info:hover { + color: #fff; + background-color: #36b9cc; + border-color: #36b9cc; +} + +.btn-outline-info:focus, .btn-outline-info.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(54, 185, 204, 0.5); + box-shadow: 0 0 0 0.2rem rgba(54, 185, 204, 0.5); +} + +.btn-outline-info.disabled, .btn-outline-info:disabled { + color: #36b9cc; + background-color: transparent; +} + +.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, +.show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #36b9cc; + border-color: #36b9cc; +} + +.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-info.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(54, 185, 204, 0.5); + box-shadow: 0 0 0 0.2rem rgba(54, 185, 204, 0.5); +} + +.btn-outline-warning { + color: #f6c23e; + border-color: #f6c23e; +} + +.btn-outline-warning:hover { + color: #fff; + background-color: #f6c23e; + border-color: #f6c23e; +} + +.btn-outline-warning:focus, .btn-outline-warning.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(246, 194, 62, 0.5); + box-shadow: 0 0 0 0.2rem rgba(246, 194, 62, 0.5); +} + +.btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #f6c23e; + background-color: transparent; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, +.show > .btn-outline-warning.dropdown-toggle { + color: #fff; + background-color: #f6c23e; + border-color: #f6c23e; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-warning.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(246, 194, 62, 0.5); + box-shadow: 0 0 0 0.2rem rgba(246, 194, 62, 0.5); +} + +.btn-outline-danger { + color: #e74a3b; + border-color: #e74a3b; +} + +.btn-outline-danger:hover { + color: #fff; + background-color: #e74a3b; + border-color: #e74a3b; +} + +.btn-outline-danger:focus, .btn-outline-danger.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.5); + box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.5); +} + +.btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #e74a3b; + background-color: transparent; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, +.show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #e74a3b; + border-color: #e74a3b; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-danger.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.5); + box-shadow: 0 0 0 0.2rem rgba(231, 74, 59, 0.5); +} + +.btn-outline-light { + color: #f8f9fc; + border-color: #f8f9fc; +} + +.btn-outline-light:hover { + color: #3a3b45; + background-color: #f8f9fc; + border-color: #f8f9fc; +} + +.btn-outline-light:focus, .btn-outline-light.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 252, 0.5); + box-shadow: 0 0 0 0.2rem rgba(248, 249, 252, 0.5); +} + +.btn-outline-light.disabled, .btn-outline-light:disabled { + color: #f8f9fc; + background-color: transparent; +} + +.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, +.show > .btn-outline-light.dropdown-toggle { + color: #3a3b45; + background-color: #f8f9fc; + border-color: #f8f9fc; +} + +.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-light.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(248, 249, 252, 0.5); + box-shadow: 0 0 0 0.2rem rgba(248, 249, 252, 0.5); +} + +.btn-outline-dark { + color: #5a5c69; + border-color: #5a5c69; +} + +.btn-outline-dark:hover { + color: #fff; + background-color: #5a5c69; + border-color: #5a5c69; +} + +.btn-outline-dark:focus, .btn-outline-dark.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(90, 92, 105, 0.5); + box-shadow: 0 0 0 0.2rem rgba(90, 92, 105, 0.5); +} + +.btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #5a5c69; + background-color: transparent; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, +.show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #5a5c69; + border-color: #5a5c69; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, +.show > .btn-outline-dark.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(90, 92, 105, 0.5); + box-shadow: 0 0 0 0.2rem rgba(90, 92, 105, 0.5); +} + +.btn-link { + font-weight: 400; + color: #4e73df; +} + +.btn-link:hover { + color: #224abe; + text-decoration: underline; +} + +.btn-link:focus, .btn-link.focus { + text-decoration: underline; + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn-link:disabled, .btn-link.disabled { + color: #858796; + pointer-events: none; +} + +.btn-lg, .btn-group-lg > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +.btn-sm, .btn-group-sm > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.btn-block { + display: block; + width: 100%; +} + +.btn-block + .btn-block { + margin-top: 0.5rem; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.fade { + -webkit-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} + +@media screen and (prefers-reduced-motion: reduce) { + .fade { + -webkit-transition: none; + transition: none; + } +} + +.fade:not(.show) { + opacity: 0; +} + +.collapse:not(.show) { + display: none; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.15s ease; + transition: height 0.15s ease; +} + +p.collapsing { + display: none; +} + +@media screen and (prefers-reduced-motion: reduce) { + .collapsing { + -webkit-transition: none; + transition: none; + } +} + +.dropup, +.dropright, +.dropdown, +.dropleft { + position: relative; +} + +.dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; +} + +.dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: 0.5rem 0; + margin: 0.125rem 0 0; + font-size: 1rem; + color: #858796; + text-align: left; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #e3e6f0; + border-radius: 0.35rem; +} + +.dropdown-menu-right { + right: 0; + left: auto; +} + +@media (min-width: 576px) { + .dropdown-menu-sm-right { + right: 0; + left: auto; + } +} + +@media (min-width: 768px) { + .dropdown-menu-md-right { + right: 0; + left: auto; + } +} + +@media (min-width: 1100px) { + .dropdown-menu-lg-right { + right: 0; + left: auto; + } +} + +@media (min-width: 1400px) { + .dropdown-menu-xl-right { + right: 0; + left: auto; + } +} + +.dropdown-menu-left { + right: auto; + left: 0; +} + +@media (min-width: 576px) { + .dropdown-menu-sm-left { + right: auto; + left: 0; + } +} + +@media (min-width: 768px) { + .dropdown-menu-md-left { + right: auto; + left: 0; + } +} + +@media (min-width: 1100px) { + .dropdown-menu-lg-left { + right: auto; + left: 0; + } +} + +@media (min-width: 1400px) { + .dropdown-menu-xl-left { + right: auto; + left: 0; + } +} + +.dropup .dropdown-menu { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: 0.125rem; +} + +.dropup .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; +} + +.dropup .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-menu { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: 0.125rem; +} + +.dropright .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; +} + +.dropright .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-toggle::after { + vertical-align: 0; +} + +.dropleft .dropdown-menu { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: 0.125rem; +} + +.dropleft .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; +} + +.dropleft .dropdown-toggle::after { + display: none; +} + +.dropleft .dropdown-toggle::before { + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; +} + +.dropleft .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle::before { + vertical-align: 0; +} + +.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { + right: auto; + bottom: auto; +} + +.dropdown-divider { + height: 0; + margin: 0.5rem 0; + overflow: hidden; + border-top: 1px solid #eaecf4; +} + +.dropdown-item { + display: block; + width: 100%; + padding: 0.25rem 1.5rem; + clear: both; + font-weight: 400; + color: #3a3b45; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0; +} + +.dropdown-item:first-child { + border-top-left-radius: calc(0.35rem - 1px); + border-top-right-radius: calc(0.35rem - 1px); +} + +.dropdown-item:last-child { + border-bottom-right-radius: calc(0.35rem - 1px); + border-bottom-left-radius: calc(0.35rem - 1px); +} + +.dropdown-item:hover, .dropdown-item:focus { + color: #2e2f37; + text-decoration: none; + background-color: #f8f9fc; +} + +.dropdown-item.active, .dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: #4e73df; +} + +.dropdown-item.disabled, .dropdown-item:disabled { + color: #858796; + pointer-events: none; + background-color: transparent; +} + +.dropdown-menu.show { + display: block; +} + +.dropdown-header { + display: block; + padding: 0.5rem 1.5rem; + margin-bottom: 0; + font-size: 0.875rem; + color: #858796; + white-space: nowrap; +} + +.dropdown-item-text { + display: block; + padding: 0.25rem 1.5rem; + color: #3a3b45; +} + +.btn-group, +.btn-group-vertical { + position: relative; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + vertical-align: middle; +} + +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + -webkit-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; +} + +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover { + z-index: 1; +} + +.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, +.btn-group-vertical > .btn:focus, +.btn-group-vertical > .btn:active, +.btn-group-vertical > .btn.active { + z-index: 1; +} + +.btn-toolbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.btn-toolbar .input-group { + width: auto; +} + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) { + margin-left: -1px; +} + +.btn-group > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.dropdown-toggle-split { + padding-right: 0.5625rem; + padding-left: 0.5625rem; +} + +.dropdown-toggle-split::after, +.dropup .dropdown-toggle-split::after, +.dropright .dropdown-toggle-split::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle-split::before { + margin-right: 0; +} + +.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { + padding-right: 0.375rem; + padding-left: 0.375rem; +} + +.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.btn-group-vertical { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group { + width: 100%; +} + +.btn-group-vertical > .btn:not(:first-child), +.btn-group-vertical > .btn-group:not(:first-child) { + margin-top: -1px; +} + +.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group-vertical > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical > .btn:not(:first-child), +.btn-group-vertical > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.btn-group-toggle > .btn, +.btn-group-toggle > .btn-group > .btn { + margin-bottom: 0; +} + +.btn-group-toggle > .btn input[type="radio"], +.btn-group-toggle > .btn input[type="checkbox"], +.btn-group-toggle > .btn-group > .btn input[type="radio"], +.btn-group-toggle > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} + +.input-group { + position: relative; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + width: 100%; +} + +.input-group > .form-control, +.input-group > .form-control-plaintext, +.input-group > .custom-select, +.input-group > .custom-file { + position: relative; + -webkit-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + width: 1%; + margin-bottom: 0; +} + +.input-group > .form-control + .form-control, +.input-group > .form-control + .custom-select, +.input-group > .form-control + .custom-file, +.input-group > .form-control-plaintext + .form-control, +.input-group > .form-control-plaintext + .custom-select, +.input-group > .form-control-plaintext + .custom-file, +.input-group > .custom-select + .form-control, +.input-group > .custom-select + .custom-select, +.input-group > .custom-select + .custom-file, +.input-group > .custom-file + .form-control, +.input-group > .custom-file + .custom-select, +.input-group > .custom-file + .custom-file { + margin-left: -1px; +} + +.input-group > .form-control:focus, +.input-group > .custom-select:focus, +.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { + z-index: 3; +} + +.input-group > .custom-file .custom-file-input:focus { + z-index: 4; +} + +.input-group > .form-control:not(:last-child), +.input-group > .custom-select:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .form-control:not(:first-child), +.input-group > .custom-select:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group > .custom-file { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.input-group > .custom-file:not(:last-child) .custom-file-label, +.input-group > .custom-file:not(:last-child) .custom-file-label::after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .custom-file:not(:first-child) .custom-file-label { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group-prepend, +.input-group-append { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +.input-group-prepend .btn, +.input-group-append .btn { + position: relative; + z-index: 2; +} + +.input-group-prepend .btn:focus, +.input-group-append .btn:focus { + z-index: 3; +} + +.input-group-prepend .btn + .btn, +.input-group-prepend .btn + .input-group-text, +.input-group-prepend .input-group-text + .input-group-text, +.input-group-prepend .input-group-text + .btn, +.input-group-append .btn + .btn, +.input-group-append .btn + .input-group-text, +.input-group-append .input-group-text + .input-group-text, +.input-group-append .input-group-text + .btn { + margin-left: -1px; +} + +.input-group-prepend { + margin-right: -1px; +} + +.input-group-append { + margin-left: -1px; +} + +.input-group-text { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 0.375rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #6e707e; + text-align: center; + white-space: nowrap; + background-color: #eaecf4; + border: 1px solid #d1d3e2; + border-radius: 0.35rem; +} + +.input-group-text input[type="radio"], +.input-group-text input[type="checkbox"] { + margin-top: 0; +} + +.input-group-lg > .form-control:not(textarea), +.input-group-lg > .custom-select { + height: calc(2.875rem + 2px); +} + +.input-group-lg > .form-control, +.input-group-lg > .custom-select, +.input-group-lg > .input-group-prepend > .input-group-text, +.input-group-lg > .input-group-append > .input-group-text, +.input-group-lg > .input-group-prepend > .btn, +.input-group-lg > .input-group-append > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; +} + +.input-group-sm > .form-control:not(textarea), +.input-group-sm > .custom-select { + height: calc(1.8125rem + 2px); +} + +.input-group-sm > .form-control, +.input-group-sm > .custom-select, +.input-group-sm > .input-group-prepend > .input-group-text, +.input-group-sm > .input-group-append > .input-group-text, +.input-group-sm > .input-group-prepend > .btn, +.input-group-sm > .input-group-append > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; +} + +.input-group-lg > .custom-select, +.input-group-sm > .custom-select { + padding-right: 1.75rem; +} + +.input-group > .input-group-prepend > .btn, +.input-group > .input-group-prepend > .input-group-text, +.input-group > .input-group-append:not(:last-child) > .btn, +.input-group > .input-group-append:not(:last-child) > .input-group-text, +.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group > .input-group-append > .btn, +.input-group > .input-group-append > .input-group-text, +.input-group > .input-group-prepend:not(:first-child) > .btn, +.input-group > .input-group-prepend:not(:first-child) > .input-group-text, +.input-group > .input-group-prepend:first-child > .btn:not(:first-child), +.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.custom-control { + position: relative; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem; +} + +.custom-control-inline { + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + margin-right: 1rem; +} + +.custom-control-input { + position: absolute; + z-index: -1; + opacity: 0; +} + +.custom-control-input:checked ~ .custom-control-label::before { + color: #fff; + border-color: #4e73df; + background-color: #4e73df; +} + +.custom-control-input:focus ~ .custom-control-label::before { + -webkit-box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); + box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); +} + +.custom-control-input:focus:not(:checked) ~ .custom-control-label::before { + border-color: #bac8f3; +} + +.custom-control-input:not(:disabled):active ~ .custom-control-label::before { + color: #fff; + background-color: #e5ebfa; + border-color: #e5ebfa; +} + +.custom-control-input:disabled ~ .custom-control-label { + color: #858796; +} + +.custom-control-input:disabled ~ .custom-control-label::before { + background-color: #eaecf4; +} + +.custom-control-label { + position: relative; + margin-bottom: 0; + vertical-align: top; +} + +.custom-control-label::before { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + background-color: #fff; + border: #b7b9cc solid 1px; +} + +.custom-control-label::after { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background-repeat: no-repeat; + background-position: center center; + background-size: 50% 50%; +} + +.custom-checkbox .custom-control-label::before { + border-radius: 0.35rem; +} + +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e"); +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { + border-color: #4e73df; + background-color: #4e73df; +} + +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); +} + +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(78, 115, 223, 0.5); +} + +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { + background-color: rgba(78, 115, 223, 0.5); +} + +.custom-radio .custom-control-label::before { + border-radius: 50%; +} + +.custom-radio .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); +} + +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(78, 115, 223, 0.5); +} + +.custom-switch { + padding-left: 2.25rem; +} + +.custom-switch .custom-control-label::before { + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: 0.5rem; +} + +.custom-switch .custom-control-label::after { + top: calc(0.25rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #b7b9cc; + border-radius: 0.5rem; + -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-switch .custom-control-label::after { + -webkit-transition: none; + transition: none; + } +} + +.custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: #fff; + -webkit-transform: translateX(0.75rem); + transform: translateX(0.75rem); +} + +.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(78, 115, 223, 0.5); +} + +.custom-select { + display: inline-block; + width: 100%; + height: calc(2.25rem + 2px); + padding: 0.375rem 1.75rem 0.375rem 0.75rem; + font-weight: 400; + line-height: 1.5; + color: #6e707e; + vertical-align: middle; + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; + background-color: #fff; + border: 1px solid #d1d3e2; + border-radius: 0.35rem; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.custom-select:focus { + border-color: #bac8f3; + outline: 0; + -webkit-box-shadow: 0 0 0 0.2rem rgba(186, 200, 243, 0.5); + box-shadow: 0 0 0 0.2rem rgba(186, 200, 243, 0.5); +} + +.custom-select:focus::-ms-value { + color: #6e707e; + background-color: #fff; +} + +.custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: 0.75rem; + background-image: none; +} + +.custom-select:disabled { + color: #858796; + background-color: #eaecf4; +} + +.custom-select::-ms-expand { + opacity: 0; +} + +.custom-select-sm { + height: calc(1.8125rem + 2px); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + font-size: 0.875rem; +} + +.custom-select-lg { + height: calc(2.875rem + 2px); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + font-size: 1.25rem; +} + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(2.25rem + 2px); + margin-bottom: 0; +} + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(2.25rem + 2px); + margin: 0; + opacity: 0; +} + +.custom-file-input:focus ~ .custom-file-label { + border-color: #bac8f3; + -webkit-box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); + box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); +} + +.custom-file-input:disabled ~ .custom-file-label { + background-color: #eaecf4; +} + +.custom-file-input:lang(en) ~ .custom-file-label::after { + content: "Browse"; +} + +.custom-file-input ~ .custom-file-label[data-browse]::after { + content: attr(data-browse); +} + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(2.25rem + 2px); + padding: 0.375rem 0.75rem; + font-weight: 400; + line-height: 1.5; + color: #6e707e; + background-color: #fff; + border: 1px solid #d1d3e2; + border-radius: 0.35rem; +} + +.custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: 2.25rem; + padding: 0.375rem 0.75rem; + line-height: 1.5; + color: #6e707e; + content: "Browse"; + background-color: #eaecf4; + border-left: inherit; + border-radius: 0 0.35rem 0.35rem 0; +} + +.custom-range { + width: 100%; + height: calc(1rem + 0.4rem); + padding: 0; + background-color: transparent; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.custom-range:focus { + outline: none; +} + +.custom-range:focus::-webkit-slider-thumb { + -webkit-box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(78, 115, 223, 0.25); + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(78, 115, 223, 0.25); +} + +.custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(78, 115, 223, 0.25); +} + +.custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(78, 115, 223, 0.25); +} + +.custom-range::-moz-focus-outer { + border: 0; +} + +.custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + background-color: #4e73df; + border: 0; + border-radius: 1rem; + -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + -webkit-appearance: none; + appearance: none; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-range::-webkit-slider-thumb { + -webkit-transition: none; + transition: none; + } +} + +.custom-range::-webkit-slider-thumb:active { + background-color: #e5ebfa; +} + +.custom-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dddfeb; + border-color: transparent; + border-radius: 1rem; +} + +.custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #4e73df; + border: 0; + border-radius: 1rem; + -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + -moz-appearance: none; + appearance: none; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-range::-moz-range-thumb { + -webkit-transition: none; + transition: none; + } +} + +.custom-range::-moz-range-thumb:active { + background-color: #e5ebfa; +} + +.custom-range::-moz-range-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dddfeb; + border-color: transparent; + border-radius: 1rem; +} + +.custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: 0.2rem; + margin-left: 0.2rem; + background-color: #4e73df; + border: 0; + border-radius: 1rem; + -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + appearance: none; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-range::-ms-thumb { + -webkit-transition: none; + transition: none; + } +} + +.custom-range::-ms-thumb:active { + background-color: #e5ebfa; +} + +.custom-range::-ms-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: 0.5rem; +} + +.custom-range::-ms-fill-lower { + background-color: #dddfeb; + border-radius: 1rem; +} + +.custom-range::-ms-fill-upper { + margin-right: 15px; + background-color: #dddfeb; + border-radius: 1rem; +} + +.custom-range:disabled::-webkit-slider-thumb { + background-color: #b7b9cc; +} + +.custom-range:disabled::-webkit-slider-runnable-track { + cursor: default; +} + +.custom-range:disabled::-moz-range-thumb { + background-color: #b7b9cc; +} + +.custom-range:disabled::-moz-range-track { + cursor: default; +} + +.custom-range:disabled::-ms-thumb { + background-color: #b7b9cc; +} + +.custom-control-label::before, +.custom-file-label, +.custom-select { + -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-box-shadow 0.15s ease-in-out; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-control-label::before, + .custom-file-label, + .custom-select { + -webkit-transition: none; + transition: none; + } +} + +.nav { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav-link { + display: block; + padding: 0.5rem 1rem; +} + +.nav-link:hover, .nav-link:focus { + text-decoration: none; +} + +.nav-link.disabled { + color: #858796; + pointer-events: none; + cursor: default; +} + +.nav-tabs { + border-bottom: 1px solid #dddfeb; +} + +.nav-tabs .nav-item { + margin-bottom: -1px; +} + +.nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: 0.35rem; + border-top-right-radius: 0.35rem; +} + +.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { + border-color: #eaecf4 #eaecf4 #dddfeb; +} + +.nav-tabs .nav-link.disabled { + color: #858796; + background-color: transparent; + border-color: transparent; +} + +.nav-tabs .nav-link.active, +.nav-tabs .nav-item.show .nav-link { + color: #6e707e; + background-color: #fff; + border-color: #dddfeb #dddfeb #fff; +} + +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.nav-pills .nav-link { + border-radius: 0.35rem; +} + +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: #fff; +} + +.nav-fill .nav-item { + -webkit-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + text-align: center; +} + +.nav-justified .nav-item { + -ms-flex-preferred-size: 0; + flex-basis: 0; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + text-align: center; +} + +.tab-content > .tab-pane { + display: none; +} + +.tab-content > .active { + display: block; +} + +.navbar { + position: relative; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 0.5rem 1rem; +} + +.navbar > .container, +.navbar > .container-fluid { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.navbar-brand { + display: inline-block; + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; +} + +.navbar-brand:hover, .navbar-brand:focus { + text-decoration: none; +} + +.navbar-nav { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; +} + +.navbar-nav .dropdown-menu { + position: static; + float: none; +} + +.navbar-text { + display: inline-block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.navbar-collapse { + -ms-flex-preferred-size: 100%; + flex-basis: 100%; + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; +} + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: 0.35rem; +} + +.navbar-toggler:hover, .navbar-toggler:focus { + text-decoration: none; +} + +.navbar-toggler:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: no-repeat center center; + background-size: 100% 100%; +} + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 576px) { + .navbar-expand-sm { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + } + + .navbar-expand-sm .navbar-nav { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + } + + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + + .navbar-expand-sm .navbar-collapse { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + + .navbar-expand-sm .navbar-toggler { + display: none; + } +} + +@media (max-width: 767.98px) { + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 768px) { + .navbar-expand-md { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + } + + .navbar-expand-md .navbar-nav { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + } + + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + + .navbar-expand-md .navbar-collapse { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + + .navbar-expand-md .navbar-toggler { + display: none; + } +} + +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 1100px) { + .navbar-expand-lg { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + } + + .navbar-expand-lg .navbar-nav { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + } + + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + + .navbar-expand-lg .navbar-collapse { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + + .navbar-expand-lg .navbar-toggler { + display: none; + } +} + +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 1400px) { + .navbar-expand-xl { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; + } + + .navbar-expand-xl .navbar-nav { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + } + + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; + } + + .navbar-expand-xl .navbar-collapse { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; + } + + .navbar-expand-xl .navbar-toggler { + display: none; + } +} + +.navbar-expand { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row nowrap; + flex-flow: row nowrap; + -webkit-box-pack: start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.navbar-expand > .container, +.navbar-expand > .container-fluid { + padding-right: 0; + padding-left: 0; +} + +.navbar-expand .navbar-nav { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; +} + +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute; +} + +.navbar-expand .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; +} + +.navbar-expand > .container, +.navbar-expand > .container-fluid { + -ms-flex-wrap: nowrap; + flex-wrap: nowrap; +} + +.navbar-expand .navbar-collapse { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-preferred-size: auto; + flex-basis: auto; +} + +.navbar-expand .navbar-toggler { + display: none; +} + +.navbar-light .navbar-brand { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-nav .nav-link { + color: rgba(0, 0, 0, 0.5); +} + +.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { + color: rgba(0, 0, 0, 0.7); +} + +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, 0.3); +} + +.navbar-light .navbar-nav .show > .nav-link, +.navbar-light .navbar-nav .active > .nav-link, +.navbar-light .navbar-nav .nav-link.show, +.navbar-light .navbar-nav .nav-link.active { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-toggler { + color: rgba(0, 0, 0, 0.5); + border-color: rgba(0, 0, 0, 0.1); +} + +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} + +.navbar-light .navbar-text { + color: rgba(0, 0, 0, 0.5); +} + +.navbar-light .navbar-text a { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { + color: rgba(0, 0, 0, 0.9); +} + +.navbar-dark .navbar-brand { + color: #fff; +} + +.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { + color: #fff; +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.5); +} + +.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { + color: rgba(255, 255, 255, 0.75); +} + +.navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); +} + +.navbar-dark .navbar-nav .show > .nav-link, +.navbar-dark .navbar-nav .active > .nav-link, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .nav-link.active { + color: #fff; +} + +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, 0.5); + border-color: rgba(255, 255, 255, 0.1); +} + +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} + +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, 0.5); +} + +.navbar-dark .navbar-text a { + color: #fff; +} + +.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { + color: #fff; +} + +.card { + position: relative; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #fff; + background-clip: border-box; + border: 1px solid #e3e6f0; + border-radius: 0.35rem; +} + +.card > hr { + margin-right: 0; + margin-left: 0; +} + +.card > .list-group:first-child .list-group-item:first-child { + border-top-left-radius: 0.35rem; + border-top-right-radius: 0.35rem; +} + +.card > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: 0.35rem; + border-bottom-left-radius: 0.35rem; +} + +.card-body { + -webkit-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + padding: 1.25rem; +} + +.card-body p + p { + margin-top: 1rem; +} + +.card-body p { + margin: 0; +} + +.card-title { + margin-bottom: 0.75rem; +} + +.card-subtitle { + margin-top: -0.375rem; + margin-bottom: 0; +} + +.card-text:last-child { + margin-bottom: 0; +} + +.card-link:hover { + text-decoration: none; +} + +.card-link + .card-link { + margin-left: 1.25rem; +} + +.card-header { + padding: 0.75rem 1.25rem; + margin-bottom: 0; + color: inherit; + background-color: #f8f9fc; + border-bottom: 1px solid #e3e6f0; +} + +.card-header:first-child { + border-radius: calc(0.35rem - 1px) calc(0.35rem - 1px) 0 0; +} + +.card-header + .list-group .list-group-item:first-child { + border-top: 0; +} + +.card-footer { + padding: 0.75rem 1.25rem; + background-color: #f8f9fc; + border-top: 1px solid #e3e6f0; +} + +.card-footer:last-child { + border-radius: 0 0 calc(0.35rem - 1px) calc(0.35rem - 1px); +} + +.card-header-tabs { + margin-right: -0.625rem; + margin-bottom: -0.75rem; + margin-left: -0.625rem; + border-bottom: 0; +} + +.card-header-pills { + margin-right: -0.625rem; + margin-left: -0.625rem; +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem; +} + +.card-img { + width: 100%; + border-radius: calc(0.35rem - 1px); +} + +.card-img-top { + width: 100%; + border-top-left-radius: calc(0.35rem - 1px); + border-top-right-radius: calc(0.35rem - 1px); +} + +.card-img-bottom { + width: 100%; + border-bottom-right-radius: calc(0.35rem - 1px); + border-bottom-left-radius: calc(0.35rem - 1px); +} + +.card-deck { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; +} + +.card-deck .card { + margin-bottom: 0.75rem; +} + +@media (min-width: 576px) { + .card-deck { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + margin-right: -0.75rem; + margin-left: -0.75rem; + } + + .card-deck .card { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + margin-right: 0.75rem; + margin-bottom: 0; + margin-left: 0.75rem; + } +} + +.card-group { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; +} + +.card-group > .card { + margin-bottom: 0.75rem; +} + +@media (min-width: 576px) { + .card-group { + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-flow: row wrap; + flex-flow: row wrap; + } + + .card-group > .card { + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + margin-bottom: 0; + } + + .card-group > .card + .card { + margin-left: 0; + border-left: 0; + } + + .card-group > .card:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + .card-group > .card:first-child .card-img-top, + .card-group > .card:first-child .card-header { + border-top-right-radius: 0; + } + + .card-group > .card:first-child .card-img-bottom, + .card-group > .card:first-child .card-footer { + border-bottom-right-radius: 0; + } + + .card-group > .card:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + .card-group > .card:last-child .card-img-top, + .card-group > .card:last-child .card-header { + border-top-left-radius: 0; + } + + .card-group > .card:last-child .card-img-bottom, + .card-group > .card:last-child .card-footer { + border-bottom-left-radius: 0; + } + + .card-group > .card:only-child { + border-radius: 0.35rem; + } + + .card-group > .card:only-child .card-img-top, + .card-group > .card:only-child .card-header { + border-top-left-radius: 0.35rem; + border-top-right-radius: 0.35rem; + } + + .card-group > .card:only-child .card-img-bottom, + .card-group > .card:only-child .card-footer { + border-bottom-right-radius: 0.35rem; + border-bottom-left-radius: 0.35rem; + } + + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) { + border-radius: 0; + } + + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-top, + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom, + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-header, + .card-group > .card:not(:first-child):not(:last-child):not(:only-child) .card-footer { + border-radius: 0; + } +} + +.card-columns .card { + margin-bottom: 0.75rem; +} + +@media (min-width: 576px) { + .card-columns { + -webkit-column-count: 3; + column-count: 3; + -webkit-column-gap: 1.25rem; + column-gap: 1.25rem; + orphans: 1; + widows: 1; + } + + .card-columns .card { + display: inline-block; + width: 100%; + } +} + +.accordion .card { + overflow: hidden; +} + +.accordion .card:not(:first-of-type) .card-header:first-child { + border-radius: 0; +} + +.accordion .card:not(:first-of-type):not(:last-of-type) { + border-bottom: 0; + border-radius: 0; +} + +.accordion .card:first-of-type { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.accordion .card:last-of-type { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.accordion .card .card-header { + margin-bottom: -1px; +} + +.breadcrumb { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #eaecf4; + border-radius: 0.35rem; +} + +.breadcrumb-item + .breadcrumb-item { + padding-left: 0.5rem; +} + +.breadcrumb-item + .breadcrumb-item::before { + display: inline-block; + padding-right: 0.5rem; + color: #858796; + content: "/"; +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: underline; +} + +.breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: none; +} + +.breadcrumb-item.active { + color: #858796; +} + +.pagination { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + padding-left: 0; + list-style: none; + border-radius: 0.35rem; +} + +.page-link { + position: relative; + display: block; + padding: 0.5rem 0.75rem; + margin-left: -1px; + line-height: 1.25; + color: #4e73df; + background-color: #fff; + border: 1px solid #dddfeb; +} + +.page-link:hover { + z-index: 2; + color: #224abe; + text-decoration: none; + background-color: #eaecf4; + border-color: #dddfeb; +} + +.page-link:focus { + z-index: 2; + outline: 0; + -webkit-box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); + box-shadow: 0 0 0 0.2rem rgba(78, 115, 223, 0.25); +} + +.page-link:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: 0.35rem; + border-bottom-left-radius: 0.35rem; +} + +.page-item:last-child .page-link { + border-top-right-radius: 0.35rem; + border-bottom-right-radius: 0.35rem; +} + +.page-item.active .page-link { + z-index: 1; + color: #fff; + background-color: #4e73df; + border-color: #4e73df; +} + +.page-item.disabled .page-link { + color: #858796; + pointer-events: none; + cursor: auto; + background-color: #fff; + border-color: #dddfeb; +} + +.pagination-lg .page-link { + padding: 0.75rem 1.5rem; + font-size: 1.25rem; + line-height: 1.5; +} + +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} + +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: 0.3rem; + border-bottom-right-radius: 0.3rem; +} + +.pagination-sm .page-link { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; +} + +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; +} + +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: 0.2rem; + border-bottom-right-radius: 0.2rem; +} + +.badge { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.35rem; +} + +a.badge:hover, a.badge:focus { + text-decoration: none; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +.badge-pill { + padding-right: 0.6em; + padding-left: 0.6em; + border-radius: 10rem; +} + +.badge-primary { + color: #fff; + background-color: #4e73df; +} + +a.badge-primary:hover, a.badge-primary:focus { + color: #fff; + background-color: #2653d4; +} + +.badge-secondary { + color: #fff; + background-color: #858796; +} + +a.badge-secondary:hover, a.badge-secondary:focus { + color: #fff; + background-color: #6b6d7d; +} + +.badge-success { + color: #fff; + background-color: #1cc88a; +} + +a.badge-success:hover, a.badge-success:focus { + color: #fff; + background-color: #169b6b; +} + +.badge-info { + color: #fff; + background-color: #36b9cc; +} + +a.badge-info:hover, a.badge-info:focus { + color: #fff; + background-color: #2a96a5; +} + +.badge-warning { + color: #fff; + background-color: #f6c23e; +} + +a.badge-warning:hover, a.badge-warning:focus { + color: #fff; + background-color: #f4b30d; +} + +.badge-danger { + color: #fff; + background-color: #e74a3b; +} + +a.badge-danger:hover, a.badge-danger:focus { + color: #fff; + background-color: #d52a1a; +} + +.badge-light { + color: #3a3b45; + background-color: #f8f9fc; +} + +a.badge-light:hover, a.badge-light:focus { + color: #3a3b45; + background-color: #d4daed; +} + +.badge-dark { + color: #fff; + background-color: #5a5c69; +} + +a.badge-dark:hover, a.badge-dark:focus { + color: #fff; + background-color: #42444e; +} + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #eaecf4; + border-radius: 0.3rem; +} + +@media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem; + } +} + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0; +} + +.alert { + position: relative; + padding: 0.75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: 0.35rem; +} + +.alert-heading { + color: inherit; +} + +.alert-link { + font-weight: 700; +} + +.alert-dismissible { + padding-right: 4rem; +} + +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: 0.75rem 1.25rem; + color: inherit; +} + +.alert-primary { + color: #293c74; + background-color: #dce3f9; + border-color: #cdd8f6; +} + +.alert-primary hr { + border-top-color: #b7c7f2; +} + +.alert-primary .alert-link { + color: #1c294e; +} + +.alert-secondary { + color: #45464e; + background-color: #e7e7ea; + border-color: #dddde2; +} + +.alert-secondary hr { + border-top-color: #cfcfd6; +} + +.alert-secondary .alert-link { + color: #2d2e33; +} + +.alert-success { + color: #0f6848; + background-color: #d2f4e8; + border-color: #bff0de; +} + +.alert-success hr { + border-top-color: #aaebd3; +} + +.alert-success .alert-link { + color: #093b29; +} + +.alert-info { + color: #1c606a; + background-color: #d7f1f5; + border-color: #c7ebf1; +} + +.alert-info hr { + border-top-color: #b3e4ec; +} + +.alert-info .alert-link { + color: #113b42; +} + +.alert-warning { + color: #806520; + background-color: #fdf3d8; + border-color: #fceec9; +} + +.alert-warning hr { + border-top-color: #fbe6b1; +} + +.alert-warning .alert-link { + color: #574516; +} + +.alert-danger { + color: #78261f; + background-color: #fadbd8; + border-color: #f8ccc8; +} + +.alert-danger hr { + border-top-color: #f5b7b1; +} + +.alert-danger .alert-link { + color: #4f1915; +} + +.alert-light { + color: #818183; + background-color: #fefefe; + border-color: #fdfdfe; +} + +.alert-light hr { + border-top-color: #ececf6; +} + +.alert-light .alert-link { + color: #686869; +} + +.alert-dark { + color: #2f3037; + background-color: #dedee1; + border-color: #d1d1d5; +} + +.alert-dark hr { + border-top-color: #c4c4c9; +} + +.alert-dark .alert-link { + color: #18181c; +} + +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 1rem 0; + } + to { + background-position: 0 0; + } +} + +@keyframes progress-bar-stripes { + from { + background-position: 1rem 0; + } + to { + background-position: 0 0; + } +} + +.progress { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + height: 1rem; + overflow: hidden; + font-size: 0.75rem; + background-color: #eaecf4; + border-radius: 0.35rem; +} + +.progress-bar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #4e73df; + -webkit-transition: width 0.6s ease; + transition: width 0.6s ease; +} + +@media screen and (prefers-reduced-motion: reduce) { + .progress-bar { + -webkit-transition: none; + transition: none; + } +} + +.progress-bar-striped { + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 1rem 1rem; +} + +.progress-bar-animated { + -webkit-animation: progress-bar-stripes 1s linear infinite; + animation: progress-bar-stripes 1s linear infinite; +} + +.media { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; +} + +.media-body { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.list-group { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; +} + +.list-group-item-action { + width: 100%; + color: #6e707e; + text-align: inherit; +} + +.list-group-item-action:hover, .list-group-item-action:focus { + color: #6e707e; + text-decoration: none; + background-color: #f8f9fc; +} + +.list-group-item-action:active { + color: #858796; + background-color: #eaecf4; +} + +.list-group-item { + position: relative; + display: block; + padding: 0.75rem 1.25rem; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.125); +} + +.list-group-item:first-child { + border-top-left-radius: 0.35rem; + border-top-right-radius: 0.35rem; +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 0.35rem; + border-bottom-left-radius: 0.35rem; +} + +.list-group-item:hover, .list-group-item:focus { + z-index: 1; + text-decoration: none; +} + +.list-group-item.disabled, .list-group-item:disabled { + color: #858796; + pointer-events: none; + background-color: #fff; +} + +.list-group-item.active { + z-index: 2; + color: #fff; + background-color: #4e73df; + border-color: #4e73df; +} + +.list-group-flush .list-group-item { + border-right: 0; + border-left: 0; + border-radius: 0; +} + +.list-group-flush .list-group-item:last-child { + margin-bottom: -1px; +} + +.list-group-flush:first-child .list-group-item:first-child { + border-top: 0; +} + +.list-group-flush:last-child .list-group-item:last-child { + margin-bottom: 0; + border-bottom: 0; +} + +.list-group-item-primary { + color: #293c74; + background-color: #cdd8f6; +} + +.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { + color: #293c74; + background-color: #b7c7f2; +} + +.list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #293c74; + border-color: #293c74; +} + +.list-group-item-secondary { + color: #45464e; + background-color: #dddde2; +} + +.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { + color: #45464e; + background-color: #cfcfd6; +} + +.list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #45464e; + border-color: #45464e; +} + +.list-group-item-success { + color: #0f6848; + background-color: #bff0de; +} + +.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { + color: #0f6848; + background-color: #aaebd3; +} + +.list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #0f6848; + border-color: #0f6848; +} + +.list-group-item-info { + color: #1c606a; + background-color: #c7ebf1; +} + +.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { + color: #1c606a; + background-color: #b3e4ec; +} + +.list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #1c606a; + border-color: #1c606a; +} + +.list-group-item-warning { + color: #806520; + background-color: #fceec9; +} + +.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { + color: #806520; + background-color: #fbe6b1; +} + +.list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #806520; + border-color: #806520; +} + +.list-group-item-danger { + color: #78261f; + background-color: #f8ccc8; +} + +.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { + color: #78261f; + background-color: #f5b7b1; +} + +.list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #78261f; + border-color: #78261f; +} + +.list-group-item-light { + color: #818183; + background-color: #fdfdfe; +} + +.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { + color: #818183; + background-color: #ececf6; +} + +.list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #818183; + border-color: #818183; +} + +.list-group-item-dark { + color: #2f3037; + background-color: #d1d1d5; +} + +.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { + color: #2f3037; + background-color: #c4c4c9; +} + +.list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #2f3037; + border-color: #2f3037; +} + +.close { + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5; +} + +.close:hover { + color: #000; + text-decoration: none; +} + +.close:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { + opacity: .75; +} + +button.close { + padding: 0; + background-color: transparent; + border: 0; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +a.close.disabled { + pointer-events: none; +} + +.toast { + max-width: 350px; + overflow: hidden; + font-size: 0.875rem; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 0.25rem; + -webkit-box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); + box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + opacity: 0; +} + +.toast:not(:last-child) { + margin-bottom: 0.75rem; +} + +.toast.showing { + opacity: 1; +} + +.toast.show { + display: block; + opacity: 1; +} + +.toast.hide { + display: none; +} + +.toast-header { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 0.25rem 0.75rem; + color: #858796; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); +} + +.toast-body { + padding: 0.75rem; +} + +.modal-open { + overflow: hidden; +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +.modal { + position: fixed; + top: 0; + left: 0; + z-index: 1050; + display: none; + width: 100%; + height: 100%; + overflow: hidden; + outline: 0; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 0.5rem; + pointer-events: none; +} + +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform 0.3s ease-out; + transition: -webkit-transform 0.3s ease-out; + transition: transform 0.3s ease-out; + transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out; + -webkit-transform: translate(0, -50px); + transform: translate(0, -50px); +} + +@media screen and (prefers-reduced-motion: reduce) { + .modal.fade .modal-dialog { + -webkit-transition: none; + transition: none; + } +} + +.modal.show .modal-dialog { + -webkit-transform: none; + transform: none; +} + +.modal-dialog-centered { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + min-height: calc(100% - (0.5rem * 2)); +} + +.modal-dialog-centered::before { + display: block; + height: calc(100vh - (0.5rem * 2)); + content: ""; +} + +.modal-content { + position: relative; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; + outline: 0; +} + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop.show { + opacity: 0.5; +} + +.modal-header { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; + padding: 1rem 1rem; + border-bottom: 1px solid #eaecf4; + border-top-left-radius: 0.3rem; + border-top-right-radius: 0.3rem; +} + +.modal-header .close { + padding: 1rem 1rem; + margin: -1rem -1rem -1rem auto; +} + +.modal-title { + margin-bottom: 0; + line-height: 1.5; +} + +.modal-body { + position: relative; + -webkit-box-flex: 1; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + padding: 1rem; +} + +.modal-footer { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: end; + -ms-flex-pack: end; + justify-content: flex-end; + padding: 1rem; + border-top: 1px solid #eaecf4; + border-bottom-right-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; +} + +.modal-footer > :not(:first-child) { + margin-left: .25rem; +} + +.modal-footer > :not(:last-child) { + margin-right: .25rem; +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto; + } + + .modal-dialog-centered { + min-height: calc(100% - (1.75rem * 2)); + } + + .modal-dialog-centered::before { + height: calc(100vh - (1.75rem * 2)); + } + + .modal-sm { + max-width: 300px; + } +} + +@media (min-width: 1100px) { + .modal-lg, + .modal-xl { + max-width: 800px; + } +} + +@media (min-width: 1400px) { + .modal-xl { + max-width: 1140px; + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + opacity: 0; +} + +.tooltip.show { + opacity: 0.9; +} + +.tooltip .arrow { + position: absolute; + display: block; + width: 0.8rem; + height: 0.4rem; +} + +.tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { + padding: 0.4rem 0; +} + +.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { + bottom: 0; +} + +.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { + top: 0; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; +} + +.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { + padding: 0 0.4rem; +} + +.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { + right: 0; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; +} + +.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { + padding: 0.4rem 0; +} + +.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { + top: 0; +} + +.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { + bottom: 0; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; +} + +.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { + padding: 0 0.4rem; +} + +.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { + left: 0; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; +} + +.tooltip-inner { + max-width: 200px; + padding: 0.25rem 0.5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 0.35rem; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; +} + +.popover .arrow { + position: absolute; + display: block; + width: 1rem; + height: 0.5rem; + margin: 0 0.3rem; +} + +.popover .arrow::before, .popover .arrow::after { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-popover-top, .bs-popover-auto[x-placement^="top"] { + margin-bottom: 0.5rem; +} + +.bs-popover-top .arrow, .bs-popover-auto[x-placement^="top"] .arrow { + bottom: calc((0.5rem + 1px) * -1); +} + +.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^="top"] .arrow::before, +.bs-popover-top .arrow::after, +.bs-popover-auto[x-placement^="top"] .arrow::after { + border-width: 0.5rem 0.5rem 0; +} + +.bs-popover-top .arrow::before, .bs-popover-auto[x-placement^="top"] .arrow::before { + bottom: 0; + border-top-color: rgba(0, 0, 0, 0.25); +} + + +.bs-popover-top .arrow::after, +.bs-popover-auto[x-placement^="top"] .arrow::after { + bottom: 1px; + border-top-color: #fff; +} + +.bs-popover-right, .bs-popover-auto[x-placement^="right"] { + margin-left: 0.5rem; +} + +.bs-popover-right .arrow, .bs-popover-auto[x-placement^="right"] .arrow { + left: calc((0.5rem + 1px) * -1); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} + +.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^="right"] .arrow::before, +.bs-popover-right .arrow::after, +.bs-popover-auto[x-placement^="right"] .arrow::after { + border-width: 0.5rem 0.5rem 0.5rem 0; +} + +.bs-popover-right .arrow::before, .bs-popover-auto[x-placement^="right"] .arrow::before { + left: 0; + border-right-color: rgba(0, 0, 0, 0.25); +} + + +.bs-popover-right .arrow::after, +.bs-popover-auto[x-placement^="right"] .arrow::after { + left: 1px; + border-right-color: #fff; +} + +.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { + margin-top: 0.5rem; +} + +.bs-popover-bottom .arrow, .bs-popover-auto[x-placement^="bottom"] .arrow { + top: calc((0.5rem + 1px) * -1); +} + +.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^="bottom"] .arrow::before, +.bs-popover-bottom .arrow::after, +.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-width: 0 0.5rem 0.5rem 0.5rem; +} + +.bs-popover-bottom .arrow::before, .bs-popover-auto[x-placement^="bottom"] .arrow::before { + top: 0; + border-bottom-color: rgba(0, 0, 0, 0.25); +} + + +.bs-popover-bottom .arrow::after, +.bs-popover-auto[x-placement^="bottom"] .arrow::after { + top: 1px; + border-bottom-color: #fff; +} + +.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7; +} + +.bs-popover-left, .bs-popover-auto[x-placement^="left"] { + margin-right: 0.5rem; +} + +.bs-popover-left .arrow, .bs-popover-auto[x-placement^="left"] .arrow { + right: calc((0.5rem + 1px) * -1); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; +} + +.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^="left"] .arrow::before, +.bs-popover-left .arrow::after, +.bs-popover-auto[x-placement^="left"] .arrow::after { + border-width: 0.5rem 0 0.5rem 0.5rem; +} + +.bs-popover-left .arrow::before, .bs-popover-auto[x-placement^="left"] .arrow::before { + right: 0; + border-left-color: rgba(0, 0, 0, 0.25); +} + + +.bs-popover-left .arrow::after, +.bs-popover-auto[x-placement^="left"] .arrow::after { + right: 1px; + border-left-color: #fff; +} + +.popover-header { + padding: 0.5rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + color: inherit; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} + +.popover-header:empty { + display: none; +} + +.popover-body { + padding: 0.5rem 0.75rem; + color: #858796; +} + +.carousel { + position: relative; +} + +.carousel.pointer-event { + -ms-touch-action: pan-y; + touch-action: pan-y; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-inner::after { + display: block; + clear: both; + content: ""; +} + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transition: -webkit-transform 0.6s ease-in-out; + transition: -webkit-transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out; + transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out; +} + +@media screen and (prefers-reduced-motion: reduce) { + .carousel-item { + -webkit-transition: none; + transition: none; + } +} + +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; +} + +.carousel-item-next:not(.carousel-item-left), +.active.carousel-item-right { + -webkit-transform: translateX(100%); + transform: translateX(100%); +} + +.carousel-item-prev:not(.carousel-item-right), +.active.carousel-item-left { + -webkit-transform: translateX(-100%); + transform: translateX(-100%); +} + +.carousel-fade .carousel-item { + opacity: 0; + -webkit-transition-property: opacity; + transition-property: opacity; + -webkit-transform: none; + transform: none; +} + +.carousel-fade .carousel-item.active, +.carousel-fade .carousel-item-next.carousel-item-left, +.carousel-fade .carousel-item-prev.carousel-item-right { + z-index: 1; + opacity: 1; +} + +.carousel-fade .active.carousel-item-left, +.carousel-fade .active.carousel-item-right { + z-index: 0; + opacity: 0; + -webkit-transition: 0s 0.6s opacity; + transition: 0s 0.6s opacity; +} + +@media screen and (prefers-reduced-motion: reduce) { + .carousel-fade .active.carousel-item-left, + .carousel-fade .active.carousel-item-right { + -webkit-transition: none; + transition: none; + } +} + +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + width: 15%; + color: #fff; + text-align: center; + opacity: 0.5; + -webkit-transition: opacity 0.15s ease; + transition: opacity 0.15s ease; +} + +@media screen and (prefers-reduced-motion: reduce) { + .carousel-control-prev, + .carousel-control-next { + -webkit-transition: none; + transition: none; + } +} + +.carousel-control-prev:hover, .carousel-control-prev:focus, +.carousel-control-next:hover, +.carousel-control-next:focus { + color: #fff; + text-decoration: none; + outline: 0; + opacity: 0.9; +} + +.carousel-control-prev { + left: 0; +} + +.carousel-control-next { + right: 0; +} + +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: 20px; + height: 20px; + background: transparent no-repeat center center; + background-size: 100% 100%; +} + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e"); +} + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e"); +} + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 15; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none; +} + +.carousel-indicators li { + -webkit-box-sizing: content-box; + box-sizing: content-box; + -webkit-box-flex: 0; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: .5; + -webkit-transition: opacity 0.6s ease; + transition: opacity 0.6s ease; +} + +@media screen and (prefers-reduced-motion: reduce) { + .carousel-indicators li { + -webkit-transition: none; + transition: none; + } +} + +.carousel-indicators .active { + opacity: 1; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; +} + +@-webkit-keyframes spinner-border { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +@keyframes spinner-border { + to { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} + +.spinner-border { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + border: 0.25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + -webkit-animation: spinner-border .75s linear infinite; + animation: spinner-border .75s linear infinite; +} + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: 0.2em; +} + +@-webkit-keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + } + 50% { + opacity: 1; + } +} + +@keyframes spinner-grow { + 0% { + -webkit-transform: scale(0); + transform: scale(0); + } + 50% { + opacity: 1; + } +} + +.spinner-grow { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + background-color: currentColor; + border-radius: 50%; + opacity: 0; + -webkit-animation: spinner-grow .75s linear infinite; + animation: spinner-grow .75s linear infinite; +} + +.spinner-grow-sm { + width: 1rem; + height: 1rem; +} + +.align-baseline { + vertical-align: baseline !important; +} + +.align-top { + vertical-align: top !important; +} + +.align-middle { + vertical-align: middle !important; +} + +.align-bottom { + vertical-align: bottom !important; +} + +.align-text-bottom { + vertical-align: text-bottom !important; +} + +.align-text-top { + vertical-align: text-top !important; +} + +.bg-primary { + background-color: #4e73df !important; +} + +a.bg-primary:hover, a.bg-primary:focus, +button.bg-primary:hover, +button.bg-primary:focus { + background-color: #2653d4 !important; +} + +.bg-secondary { + background-color: #858796 !important; +} + +a.bg-secondary:hover, a.bg-secondary:focus, +button.bg-secondary:hover, +button.bg-secondary:focus { + background-color: #6b6d7d !important; +} + +.bg-success { + background-color: #1cc88a !important; +} + +a.bg-success:hover, a.bg-success:focus, +button.bg-success:hover, +button.bg-success:focus { + background-color: #169b6b !important; +} + +.bg-info { + background-color: #36b9cc !important; +} + +a.bg-info:hover, a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #2a96a5 !important; +} + +.bg-warning { + background-color: #f6c23e !important; +} + +a.bg-warning:hover, a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #f4b30d !important; +} + +.bg-danger { + background-color: #e74a3b !important; +} + +a.bg-danger:hover, a.bg-danger:focus, +button.bg-danger:hover, +button.bg-danger:focus { + background-color: #d52a1a !important; +} + +.bg-light { + background-color: #f8f9fc !important; +} + +a.bg-light:hover, a.bg-light:focus, +button.bg-light:hover, +button.bg-light:focus { + background-color: #d4daed !important; +} + +.bg-dark { + background-color: #5a5c69 !important; +} + +a.bg-dark:hover, a.bg-dark:focus, +button.bg-dark:hover, +button.bg-dark:focus { + background-color: #42444e !important; +} + +.bg-transparent { + background-color: transparent !important; +} + +.border { + border: 1px solid #e3e6f0 !important; +} + +.border-top { + border-top: 1px solid #e3e6f0 !important; +} + +.border-right { + border-right: 1px solid #e3e6f0 !important; +} + +.border-bottom { + border-bottom: 1px solid #e3e6f0 !important; +} + +.border-left { + border-left: 1px solid #e3e6f0 !important; +} + +.border-0 { + border: 0 !important; +} + +.border-top-0 { + border-top: 0 !important; +} + +.border-right-0 { + border-right: 0 !important; +} + +.border-bottom-0 { + border-bottom: 0 !important; +} + +.border-left-0 { + border-left: 0 !important; +} + +.border-primary { + border-color: #4e73df !important; +} + +.border-secondary { + border-color: #858796 !important; +} + +.border-success { + border-color: #1cc88a !important; +} + +.border-info { + border-color: #36b9cc !important; +} + +.border-warning { + border-color: #f6c23e !important; +} + +.border-danger { + border-color: #e74a3b !important; +} + +.border-light { + border-color: #f8f9fc !important; +} + +.border-dark { + border-color: #5a5c69 !important; +} + +.border-white { + border-color: #fff !important; +} + +.rounded { + border-radius: 0.35rem !important; +} + +.rounded-top { + border-top-left-radius: 0.35rem !important; + border-top-right-radius: 0.35rem !important; +} + +.rounded-right { + border-top-right-radius: 0.35rem !important; + border-bottom-right-radius: 0.35rem !important; +} + +.rounded-bottom { + border-bottom-right-radius: 0.35rem !important; + border-bottom-left-radius: 0.35rem !important; +} + +.rounded-left { + border-top-left-radius: 0.35rem !important; + border-bottom-left-radius: 0.35rem !important; +} + +.rounded-circle { + border-radius: 50% !important; +} + +.rounded-pill { + border-radius: 50rem !important; +} + +.rounded-0 { + border-radius: 0 !important; +} + +.clearfix::after { + display: block; + clear: both; + content: ""; +} + +.d-none { + display: none !important; +} + +.d-inline { + display: inline !important; +} + +.d-inline-block { + display: inline-block !important; +} + +.d-block { + display: block !important; +} + +.d-table { + display: table !important; +} + +.d-table-row { + display: table-row !important; +} + +.d-table-cell { + display: table-cell !important; +} + +.d-flex { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; +} + +.d-inline-flex { + display: -webkit-inline-box !important; + display: -ms-inline-flexbox !important; + display: inline-flex !important; +} + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; + } + + .d-sm-inline { + display: inline !important; + } + + .d-sm-inline-block { + display: inline-block !important; + } + + .d-sm-block { + display: block !important; + } + + .d-sm-table { + display: table !important; + } + + .d-sm-table-row { + display: table-row !important; + } + + .d-sm-table-cell { + display: table-cell !important; + } + + .d-sm-flex { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + } + + .d-sm-inline-flex { + display: -webkit-inline-box !important; + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 768px) { + .d-md-none { + display: none !important; + } + + .d-md-inline { + display: inline !important; + } + + .d-md-inline-block { + display: inline-block !important; + } + + .d-md-block { + display: block !important; + } + + .d-md-table { + display: table !important; + } + + .d-md-table-row { + display: table-row !important; + } + + .d-md-table-cell { + display: table-cell !important; + } + + .d-md-flex { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + } + + .d-md-inline-flex { + display: -webkit-inline-box !important; + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 1100px) { + .d-lg-none { + display: none !important; + } + + .d-lg-inline { + display: inline !important; + } + + .d-lg-inline-block { + display: inline-block !important; + } + + .d-lg-block { + display: block !important; + } + + .d-lg-table { + display: table !important; + } + + .d-lg-table-row { + display: table-row !important; + } + + .d-lg-table-cell { + display: table-cell !important; + } + + .d-lg-flex { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + } + + .d-lg-inline-flex { + display: -webkit-inline-box !important; + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media (min-width: 1400px) { + .d-xl-none { + display: none !important; + } + + .d-xl-inline { + display: inline !important; + } + + .d-xl-inline-block { + display: inline-block !important; + } + + .d-xl-block { + display: block !important; + } + + .d-xl-table { + display: table !important; + } + + .d-xl-table-row { + display: table-row !important; + } + + .d-xl-table-cell { + display: table-cell !important; + } + + .d-xl-flex { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + } + + .d-xl-inline-flex { + display: -webkit-inline-box !important; + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +@media print { + .d-print-none { + display: none !important; + } + + .d-print-inline { + display: inline !important; + } + + .d-print-inline-block { + display: inline-block !important; + } + + .d-print-block { + display: block !important; + } + + .d-print-table { + display: table !important; + } + + .d-print-table-row { + display: table-row !important; + } + + .d-print-table-cell { + display: table-cell !important; + } + + .d-print-flex { + display: -webkit-box !important; + display: -ms-flexbox !important; + display: flex !important; + } + + .d-print-inline-flex { + display: -webkit-inline-box !important; + display: -ms-inline-flexbox !important; + display: inline-flex !important; + } +} + +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden; +} + +.embed-responsive::before { + display: block; + content: ""; +} + +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +.embed-responsive-21by9::before { + padding-top: 42.85714%; +} + +.embed-responsive-16by9::before { + padding-top: 56.25%; +} + +.embed-responsive-3by4::before { + padding-top: 133.33333%; +} + +.embed-responsive-1by1::before { + padding-top: 100%; +} + +.flex-row { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: row !important; + flex-direction: row !important; +} + +.flex-column { + -webkit-box-orient: vertical !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: column !important; + flex-direction: column !important; +} + +.flex-row-reverse { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; +} + +.flex-column-reverse { + -webkit-box-orient: vertical !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; +} + +.flex-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; +} + +.flex-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; +} + +.flex-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; +} + +.flex-fill { + -webkit-box-flex: 1 !important; + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; +} + +.flex-grow-0 { + -webkit-box-flex: 0 !important; + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; +} + +.flex-grow-1 { + -webkit-box-flex: 1 !important; + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; +} + +.flex-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; +} + +.flex-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; +} + +.justify-content-start { + -webkit-box-pack: start !important; + -ms-flex-pack: start !important; + justify-content: flex-start !important; +} + +.justify-content-end { + -webkit-box-pack: end !important; + -ms-flex-pack: end !important; + justify-content: flex-end !important; +} + +.justify-content-center { + -webkit-box-pack: center !important; + -ms-flex-pack: center !important; + justify-content: center !important; +} + +.justify-content-between { + -webkit-box-pack: justify !important; + -ms-flex-pack: justify !important; + justify-content: space-between !important; +} + +.justify-content-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; +} + +.align-items-start { + -webkit-box-align: start !important; + -ms-flex-align: start !important; + align-items: flex-start !important; +} + +.align-items-end { + -webkit-box-align: end !important; + -ms-flex-align: end !important; + align-items: flex-end !important; +} + +.align-items-center { + -webkit-box-align: center !important; + -ms-flex-align: center !important; + align-items: center !important; +} + +.align-items-baseline { + -webkit-box-align: baseline !important; + -ms-flex-align: baseline !important; + align-items: baseline !important; +} + +.align-items-stretch { + -webkit-box-align: stretch !important; + -ms-flex-align: stretch !important; + align-items: stretch !important; +} + +.align-content-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; +} + +.align-content-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; +} + +.align-content-center { + -ms-flex-line-pack: center !important; + align-content: center !important; +} + +.align-content-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; +} + +.align-content-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; +} + +.align-content-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; +} + +.align-self-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; +} + +.align-self-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; +} + +.align-self-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; +} + +.align-self-center { + -ms-flex-item-align: center !important; + align-self: center !important; +} + +.align-self-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; +} + +.align-self-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; +} + +@media (min-width: 576px) { + .flex-sm-row { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: row !important; + flex-direction: row !important; + } + + .flex-sm-column { + -webkit-box-orient: vertical !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: column !important; + flex-direction: column !important; + } + + .flex-sm-row-reverse { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + + .flex-sm-column-reverse { + -webkit-box-orient: vertical !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + + .flex-sm-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + + .flex-sm-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + + .flex-sm-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + + .flex-sm-fill { + -webkit-box-flex: 1 !important; + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + + .flex-sm-grow-0 { + -webkit-box-flex: 0 !important; + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + + .flex-sm-grow-1 { + -webkit-box-flex: 1 !important; + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + + .flex-sm-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + + .flex-sm-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + + .justify-content-sm-start { + -webkit-box-pack: start !important; + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + + .justify-content-sm-end { + -webkit-box-pack: end !important; + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + + .justify-content-sm-center { + -webkit-box-pack: center !important; + -ms-flex-pack: center !important; + justify-content: center !important; + } + + .justify-content-sm-between { + -webkit-box-pack: justify !important; + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + + .justify-content-sm-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + + .align-items-sm-start { + -webkit-box-align: start !important; + -ms-flex-align: start !important; + align-items: flex-start !important; + } + + .align-items-sm-end { + -webkit-box-align: end !important; + -ms-flex-align: end !important; + align-items: flex-end !important; + } + + .align-items-sm-center { + -webkit-box-align: center !important; + -ms-flex-align: center !important; + align-items: center !important; + } + + .align-items-sm-baseline { + -webkit-box-align: baseline !important; + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + + .align-items-sm-stretch { + -webkit-box-align: stretch !important; + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + + .align-content-sm-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + + .align-content-sm-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + + .align-content-sm-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + + .align-content-sm-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + + .align-content-sm-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + + .align-content-sm-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + + .align-self-sm-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + + .align-self-sm-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + + .align-self-sm-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + + .align-self-sm-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + + .align-self-sm-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + + .align-self-sm-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 768px) { + .flex-md-row { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: row !important; + flex-direction: row !important; + } + + .flex-md-column { + -webkit-box-orient: vertical !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: column !important; + flex-direction: column !important; + } + + .flex-md-row-reverse { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + + .flex-md-column-reverse { + -webkit-box-orient: vertical !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + + .flex-md-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + + .flex-md-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + + .flex-md-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + + .flex-md-fill { + -webkit-box-flex: 1 !important; + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + + .flex-md-grow-0 { + -webkit-box-flex: 0 !important; + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + + .flex-md-grow-1 { + -webkit-box-flex: 1 !important; + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + + .flex-md-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + + .flex-md-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + + .justify-content-md-start { + -webkit-box-pack: start !important; + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + + .justify-content-md-end { + -webkit-box-pack: end !important; + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + + .justify-content-md-center { + -webkit-box-pack: center !important; + -ms-flex-pack: center !important; + justify-content: center !important; + } + + .justify-content-md-between { + -webkit-box-pack: justify !important; + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + + .justify-content-md-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + + .align-items-md-start { + -webkit-box-align: start !important; + -ms-flex-align: start !important; + align-items: flex-start !important; + } + + .align-items-md-end { + -webkit-box-align: end !important; + -ms-flex-align: end !important; + align-items: flex-end !important; + } + + .align-items-md-center { + -webkit-box-align: center !important; + -ms-flex-align: center !important; + align-items: center !important; + } + + .align-items-md-baseline { + -webkit-box-align: baseline !important; + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + + .align-items-md-stretch { + -webkit-box-align: stretch !important; + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + + .align-content-md-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + + .align-content-md-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + + .align-content-md-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + + .align-content-md-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + + .align-content-md-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + + .align-content-md-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + + .align-self-md-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + + .align-self-md-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + + .align-self-md-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + + .align-self-md-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + + .align-self-md-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + + .align-self-md-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 1100px) { + .flex-lg-row { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: row !important; + flex-direction: row !important; + } + + .flex-lg-column { + -webkit-box-orient: vertical !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: column !important; + flex-direction: column !important; + } + + .flex-lg-row-reverse { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + + .flex-lg-column-reverse { + -webkit-box-orient: vertical !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + + .flex-lg-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + + .flex-lg-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + + .flex-lg-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + + .flex-lg-fill { + -webkit-box-flex: 1 !important; + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + + .flex-lg-grow-0 { + -webkit-box-flex: 0 !important; + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + + .flex-lg-grow-1 { + -webkit-box-flex: 1 !important; + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + + .flex-lg-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + + .flex-lg-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + + .justify-content-lg-start { + -webkit-box-pack: start !important; + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + + .justify-content-lg-end { + -webkit-box-pack: end !important; + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + + .justify-content-lg-center { + -webkit-box-pack: center !important; + -ms-flex-pack: center !important; + justify-content: center !important; + } + + .justify-content-lg-between { + -webkit-box-pack: justify !important; + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + + .justify-content-lg-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + + .align-items-lg-start { + -webkit-box-align: start !important; + -ms-flex-align: start !important; + align-items: flex-start !important; + } + + .align-items-lg-end { + -webkit-box-align: end !important; + -ms-flex-align: end !important; + align-items: flex-end !important; + } + + .align-items-lg-center { + -webkit-box-align: center !important; + -ms-flex-align: center !important; + align-items: center !important; + } + + .align-items-lg-baseline { + -webkit-box-align: baseline !important; + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + + .align-items-lg-stretch { + -webkit-box-align: stretch !important; + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + + .align-content-lg-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + + .align-content-lg-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + + .align-content-lg-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + + .align-content-lg-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + + .align-content-lg-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + + .align-content-lg-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + + .align-self-lg-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + + .align-self-lg-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + + .align-self-lg-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + + .align-self-lg-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + + .align-self-lg-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + + .align-self-lg-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +@media (min-width: 1400px) { + .flex-xl-row { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: row !important; + flex-direction: row !important; + } + + .flex-xl-column { + -webkit-box-orient: vertical !important; + -webkit-box-direction: normal !important; + -ms-flex-direction: column !important; + flex-direction: column !important; + } + + .flex-xl-row-reverse { + -webkit-box-orient: horizontal !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: row-reverse !important; + flex-direction: row-reverse !important; + } + + .flex-xl-column-reverse { + -webkit-box-orient: vertical !important; + -webkit-box-direction: reverse !important; + -ms-flex-direction: column-reverse !important; + flex-direction: column-reverse !important; + } + + .flex-xl-wrap { + -ms-flex-wrap: wrap !important; + flex-wrap: wrap !important; + } + + .flex-xl-nowrap { + -ms-flex-wrap: nowrap !important; + flex-wrap: nowrap !important; + } + + .flex-xl-wrap-reverse { + -ms-flex-wrap: wrap-reverse !important; + flex-wrap: wrap-reverse !important; + } + + .flex-xl-fill { + -webkit-box-flex: 1 !important; + -ms-flex: 1 1 auto !important; + flex: 1 1 auto !important; + } + + .flex-xl-grow-0 { + -webkit-box-flex: 0 !important; + -ms-flex-positive: 0 !important; + flex-grow: 0 !important; + } + + .flex-xl-grow-1 { + -webkit-box-flex: 1 !important; + -ms-flex-positive: 1 !important; + flex-grow: 1 !important; + } + + .flex-xl-shrink-0 { + -ms-flex-negative: 0 !important; + flex-shrink: 0 !important; + } + + .flex-xl-shrink-1 { + -ms-flex-negative: 1 !important; + flex-shrink: 1 !important; + } + + .justify-content-xl-start { + -webkit-box-pack: start !important; + -ms-flex-pack: start !important; + justify-content: flex-start !important; + } + + .justify-content-xl-end { + -webkit-box-pack: end !important; + -ms-flex-pack: end !important; + justify-content: flex-end !important; + } + + .justify-content-xl-center { + -webkit-box-pack: center !important; + -ms-flex-pack: center !important; + justify-content: center !important; + } + + .justify-content-xl-between { + -webkit-box-pack: justify !important; + -ms-flex-pack: justify !important; + justify-content: space-between !important; + } + + .justify-content-xl-around { + -ms-flex-pack: distribute !important; + justify-content: space-around !important; + } + + .align-items-xl-start { + -webkit-box-align: start !important; + -ms-flex-align: start !important; + align-items: flex-start !important; + } + + .align-items-xl-end { + -webkit-box-align: end !important; + -ms-flex-align: end !important; + align-items: flex-end !important; + } + + .align-items-xl-center { + -webkit-box-align: center !important; + -ms-flex-align: center !important; + align-items: center !important; + } + + .align-items-xl-baseline { + -webkit-box-align: baseline !important; + -ms-flex-align: baseline !important; + align-items: baseline !important; + } + + .align-items-xl-stretch { + -webkit-box-align: stretch !important; + -ms-flex-align: stretch !important; + align-items: stretch !important; + } + + .align-content-xl-start { + -ms-flex-line-pack: start !important; + align-content: flex-start !important; + } + + .align-content-xl-end { + -ms-flex-line-pack: end !important; + align-content: flex-end !important; + } + + .align-content-xl-center { + -ms-flex-line-pack: center !important; + align-content: center !important; + } + + .align-content-xl-between { + -ms-flex-line-pack: justify !important; + align-content: space-between !important; + } + + .align-content-xl-around { + -ms-flex-line-pack: distribute !important; + align-content: space-around !important; + } + + .align-content-xl-stretch { + -ms-flex-line-pack: stretch !important; + align-content: stretch !important; + } + + .align-self-xl-auto { + -ms-flex-item-align: auto !important; + align-self: auto !important; + } + + .align-self-xl-start { + -ms-flex-item-align: start !important; + align-self: flex-start !important; + } + + .align-self-xl-end { + -ms-flex-item-align: end !important; + align-self: flex-end !important; + } + + .align-self-xl-center { + -ms-flex-item-align: center !important; + align-self: center !important; + } + + .align-self-xl-baseline { + -ms-flex-item-align: baseline !important; + align-self: baseline !important; + } + + .align-self-xl-stretch { + -ms-flex-item-align: stretch !important; + align-self: stretch !important; + } +} + +.float-left { + float: left !important; +} + +.float-right { + float: right !important; +} + +.float-none { + float: none !important; +} + +@media (min-width: 576px) { + .float-sm-left { + float: left !important; + } + + .float-sm-right { + float: right !important; + } + + .float-sm-none { + float: none !important; + } +} + +@media (min-width: 768px) { + .float-md-left { + float: left !important; + } + + .float-md-right { + float: right !important; + } + + .float-md-none { + float: none !important; + } +} + +@media (min-width: 1100px) { + .float-lg-left { + float: left !important; + } + + .float-lg-right { + float: right !important; + } + + .float-lg-none { + float: none !important; + } +} + +@media (min-width: 1400px) { + .float-xl-left { + float: left !important; + } + + .float-xl-right { + float: right !important; + } + + .float-xl-none { + float: none !important; + } +} + +.overflow-auto { + overflow: auto !important; +} + +.overflow-hidden { + overflow: hidden !important; +} + +.position-static { + position: static !important; +} + +.position-relative { + position: relative !important; +} + +.position-absolute { + position: absolute !important; +} + +.position-fixed { + position: fixed !important; +} + +.position-sticky { + position: -webkit-sticky !important; + position: sticky !important; +} + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; +} + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; +} + +@supports ((position: -webkit-sticky) or (position: sticky)) { + .sticky-top { + position: -webkit-sticky; + position: sticky; + top: 0; + z-index: 1020; + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal; +} + +.shadow-sm { + -webkit-box-shadow: 0 0.125rem 0.25rem 0 rgba(58, 59, 69, 0.2) !important; + box-shadow: 0 0.125rem 0.25rem 0 rgba(58, 59, 69, 0.2) !important; +} + +.shadow { + -webkit-box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15) !important; + box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15) !important; +} + +.shadow-lg { + -webkit-box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; + box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; +} + +.shadow-none { + -webkit-box-shadow: none !important; + box-shadow: none !important; +} + +.w-22 { + width: 22% !important; +} + +.w-25 { + width: 25% !important; +} + +.w-50 { + width: 50% !important; +} + +.w-75 { + width: 75% !important; +} + +.w-100 { + width: 100% !important; +} + +.w-auto { + width: auto !important; +} + +.h-25 { + height: 25% !important; +} + +.h-50 { + height: 50% !important; +} + +.h-75 { + height: 75% !important; +} + +.h-100 { + height: 100% !important; +} + +.h-auto { + height: auto !important; +} + +.mw-100 { + max-width: 100% !important; +} + +.mh-100 { + max-height: 100% !important; +} + +.min-vw-100 { + min-width: 100vw !important; +} + +.min-vh-100 { + min-height: 100vh !important; +} + +.vw-100 { + width: 100vw !important; +} + +.vh-100 { + height: 100vh !important; +} + +.m-0 { + margin: 0 !important; +} + +.mt-0, +.my-0 { + margin-top: 0 !important; +} + +.mr-0, +.mx-0 { + margin-right: 0 !important; +} + +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} + +.ml-0, +.mx-0 { + margin-left: 0 !important; +} + +.m-1 { + margin: 0.25rem !important; +} + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; +} + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; +} + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} + +.m-2 { + margin: 0.5rem !important; +} + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} + +.m-3 { + margin: 1rem !important; +} + +.mt-3, +.my-3 { + margin-top: 1rem !important; +} + +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} + +.mb-3, +.my-3 { + margin-bottom: 1rem !important; +} + +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} + +.m-4 { + margin: 1.5rem !important; +} + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; +} + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} + +.m-5 { + margin: 3rem !important; +} + +.mt-5, +.my-5 { + margin-top: 3rem !important; +} + +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} + +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} + +.p-0 { + padding: 0 !important; +} + +.pt-0, +.py-0 { + padding-top: 0 !important; +} + +.pr-0, +.px-0 { + padding-right: 0 !important; +} + +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} + +.pl-0, +.px-0 { + padding-left: 0 !important; +} + +.p-1 { + padding: 0.25rem !important; +} + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} + +.p-2 { + padding: 0.5rem !important; +} + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} + +.p-3 { + padding: 1rem !important; +} + +.pt-3, +.py-3 { + padding-top: 1rem !important; +} + +.pr-3, +.px-3 { + padding-right: 1rem !important; +} + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} + +.pl-3, +.px-3 { + padding-left: 1rem !important; +} + +.p-4 { + padding: 1.5rem !important; +} + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} + +.p-5 { + padding: 3rem !important; +} + +.pt-5, +.py-5 { + padding-top: 3rem !important; +} + +.pr-5, +.px-5 { + padding-right: 3rem !important; +} + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} + +.pl-5, +.px-5 { + padding-left: 3rem !important; +} + +.m-n1 { + margin: -0.25rem !important; +} + +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; +} + +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; +} + +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; +} + +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; +} + +.m-n2 { + margin: -0.5rem !important; +} + +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; +} + +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; +} + +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; +} + +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; +} + +.m-n3 { + margin: -1rem !important; +} + +.mt-n3, +.my-n3 { + margin-top: -1rem !important; +} + +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; +} + +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; +} + +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; +} + +.m-n4 { + margin: -1.5rem !important; +} + +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; +} + +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; +} + +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; +} + +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; +} + +.m-n5 { + margin: -3rem !important; +} + +.mt-n5, +.my-n5 { + margin-top: -3rem !important; +} + +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; +} + +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; +} + +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; +} + +.m-auto { + margin: auto !important; +} + +.mt-auto, +.my-auto { + margin-top: auto !important; +} + +.mr-auto, +.mx-auto { + margin-right: auto !important; +} + +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} + +.ml-auto, +.mx-auto { + margin-left: auto !important; +} + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; + } + + .mt-sm-0, + .my-sm-0 { + margin-top: 0 !important; + } + + .mr-sm-0, + .mx-sm-0 { + margin-right: 0 !important; + } + + .mb-sm-0, + .my-sm-0 { + margin-bottom: 0 !important; + } + + .ml-sm-0, + .mx-sm-0 { + margin-left: 0 !important; + } + + .m-sm-1 { + margin: 0.25rem !important; + } + + .mt-sm-1, + .my-sm-1 { + margin-top: 0.25rem !important; + } + + .mr-sm-1, + .mx-sm-1 { + margin-right: 0.25rem !important; + } + + .mb-sm-1, + .my-sm-1 { + margin-bottom: 0.25rem !important; + } + + .ml-sm-1, + .mx-sm-1 { + margin-left: 0.25rem !important; + } + + .m-sm-2 { + margin: 0.5rem !important; + } + + .mt-sm-2, + .my-sm-2 { + margin-top: 0.5rem !important; + } + + .mr-sm-2, + .mx-sm-2 { + margin-right: 0.5rem !important; + } + + .mb-sm-2, + .my-sm-2 { + margin-bottom: 0.5rem !important; + } + + .ml-sm-2, + .mx-sm-2 { + margin-left: 0.5rem !important; + } + + .m-sm-3 { + margin: 1rem !important; + } + + .mt-sm-3, + .my-sm-3 { + margin-top: 1rem !important; + } + + .mr-sm-3, + .mx-sm-3 { + margin-right: 1rem !important; + } + + .mb-sm-3, + .my-sm-3 { + margin-bottom: 1rem !important; + } + + .ml-sm-3, + .mx-sm-3 { + margin-left: 1rem !important; + } + + .m-sm-4 { + margin: 1.5rem !important; + } + + .mt-sm-4, + .my-sm-4 { + margin-top: 1.5rem !important; + } + + .mr-sm-4, + .mx-sm-4 { + margin-right: 1.5rem !important; + } + + .mb-sm-4, + .my-sm-4 { + margin-bottom: 1.5rem !important; + } + + .ml-sm-4, + .mx-sm-4 { + margin-left: 1.5rem !important; + } + + .m-sm-5 { + margin: 3rem !important; + } + + .mt-sm-5, + .my-sm-5 { + margin-top: 3rem !important; + } + + .mr-sm-5, + .mx-sm-5 { + margin-right: 3rem !important; + } + + .mb-sm-5, + .my-sm-5 { + margin-bottom: 3rem !important; + } + + .ml-sm-5, + .mx-sm-5 { + margin-left: 3rem !important; + } + + .p-sm-0 { + padding: 0 !important; + } + + .pt-sm-0, + .py-sm-0 { + padding-top: 0 !important; + } + + .pr-sm-0, + .px-sm-0 { + padding-right: 0 !important; + } + + .pb-sm-0, + .py-sm-0 { + padding-bottom: 0 !important; + } + + .pl-sm-0, + .px-sm-0 { + padding-left: 0 !important; + } + + .p-sm-1 { + padding: 0.25rem !important; + } + + .pt-sm-1, + .py-sm-1 { + padding-top: 0.25rem !important; + } + + .pr-sm-1, + .px-sm-1 { + padding-right: 0.25rem !important; + } + + .pb-sm-1, + .py-sm-1 { + padding-bottom: 0.25rem !important; + } + + .pl-sm-1, + .px-sm-1 { + padding-left: 0.25rem !important; + } + + .p-sm-2 { + padding: 0.5rem !important; + } + + .pt-sm-2, + .py-sm-2 { + padding-top: 0.5rem !important; + } + + .pr-sm-2, + .px-sm-2 { + padding-right: 0.5rem !important; + } + + .pb-sm-2, + .py-sm-2 { + padding-bottom: 0.5rem !important; + } + + .pl-sm-2, + .px-sm-2 { + padding-left: 0.5rem !important; + } + + .p-sm-3 { + padding: 1rem !important; + } + + .pt-sm-3, + .py-sm-3 { + padding-top: 1rem !important; + } + + .pr-sm-3, + .px-sm-3 { + padding-right: 1rem !important; + } + + .pb-sm-3, + .py-sm-3 { + padding-bottom: 1rem !important; + } + + .pl-sm-3, + .px-sm-3 { + padding-left: 1rem !important; + } + + .p-sm-4 { + padding: 1.5rem !important; + } + + .pt-sm-4, + .py-sm-4 { + padding-top: 1.5rem !important; + } + + .pr-sm-4, + .px-sm-4 { + padding-right: 1.5rem !important; + } + + .pb-sm-4, + .py-sm-4 { + padding-bottom: 1.5rem !important; + } + + .pl-sm-4, + .px-sm-4 { + padding-left: 1.5rem !important; + } + + .p-sm-5 { + padding: 3rem !important; + } + + .pt-sm-5, + .py-sm-5 { + padding-top: 3rem !important; + } + + .pr-sm-5, + .px-sm-5 { + padding-right: 3rem !important; + } + + .pb-sm-5, + .py-sm-5 { + padding-bottom: 3rem !important; + } + + .pl-sm-5, + .px-sm-5 { + padding-left: 3rem !important; + } + + .m-sm-n1 { + margin: -0.25rem !important; + } + + .mt-sm-n1, + .my-sm-n1 { + margin-top: -0.25rem !important; + } + + .mr-sm-n1, + .mx-sm-n1 { + margin-right: -0.25rem !important; + } + + .mb-sm-n1, + .my-sm-n1 { + margin-bottom: -0.25rem !important; + } + + .ml-sm-n1, + .mx-sm-n1 { + margin-left: -0.25rem !important; + } + + .m-sm-n2 { + margin: -0.5rem !important; + } + + .mt-sm-n2, + .my-sm-n2 { + margin-top: -0.5rem !important; + } + + .mr-sm-n2, + .mx-sm-n2 { + margin-right: -0.5rem !important; + } + + .mb-sm-n2, + .my-sm-n2 { + margin-bottom: -0.5rem !important; + } + + .ml-sm-n2, + .mx-sm-n2 { + margin-left: -0.5rem !important; + } + + .m-sm-n3 { + margin: -1rem !important; + } + + .mt-sm-n3, + .my-sm-n3 { + margin-top: -1rem !important; + } + + .mr-sm-n3, + .mx-sm-n3 { + margin-right: -1rem !important; + } + + .mb-sm-n3, + .my-sm-n3 { + margin-bottom: -1rem !important; + } + + .ml-sm-n3, + .mx-sm-n3 { + margin-left: -1rem !important; + } + + .m-sm-n4 { + margin: -1.5rem !important; + } + + .mt-sm-n4, + .my-sm-n4 { + margin-top: -1.5rem !important; + } + + .mr-sm-n4, + .mx-sm-n4 { + margin-right: -1.5rem !important; + } + + .mb-sm-n4, + .my-sm-n4 { + margin-bottom: -1.5rem !important; + } + + .ml-sm-n4, + .mx-sm-n4 { + margin-left: -1.5rem !important; + } + + .m-sm-n5 { + margin: -3rem !important; + } + + .mt-sm-n5, + .my-sm-n5 { + margin-top: -3rem !important; + } + + .mr-sm-n5, + .mx-sm-n5 { + margin-right: -3rem !important; + } + + .mb-sm-n5, + .my-sm-n5 { + margin-bottom: -3rem !important; + } + + .ml-sm-n5, + .mx-sm-n5 { + margin-left: -3rem !important; + } + + .m-sm-auto { + margin: auto !important; + } + + .mt-sm-auto, + .my-sm-auto { + margin-top: auto !important; + } + + .mr-sm-auto, + .mx-sm-auto { + margin-right: auto !important; + } + + .mb-sm-auto, + .my-sm-auto { + margin-bottom: auto !important; + } + + .ml-sm-auto, + .mx-sm-auto { + margin-left: auto !important; + } +} + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; + } + + .mt-md-0, + .my-md-0 { + margin-top: 0 !important; + } + + .mr-md-0, + .mx-md-0 { + margin-right: 0 !important; + } + + .mb-md-0, + .my-md-0 { + margin-bottom: 0 !important; + } + + .ml-md-0, + .mx-md-0 { + margin-left: 0 !important; + } + + .m-md-1 { + margin: 0.25rem !important; + } + + .mt-md-1, + .my-md-1 { + margin-top: 0.25rem !important; + } + + .mr-md-1, + .mx-md-1 { + margin-right: 0.25rem !important; + } + + .mb-md-1, + .my-md-1 { + margin-bottom: 0.25rem !important; + } + + .ml-md-1, + .mx-md-1 { + margin-left: 0.25rem !important; + } + + .m-md-2 { + margin: 0.5rem !important; + } + + .mt-md-2, + .my-md-2 { + margin-top: 0.5rem !important; + } + + .mr-md-2, + .mx-md-2 { + margin-right: 0.5rem !important; + } + + .mb-md-2, + .my-md-2 { + margin-bottom: 0.5rem !important; + } + + .ml-md-2, + .mx-md-2 { + margin-left: 0.5rem !important; + } + + .m-md-3 { + margin: 1rem !important; + } + + .mt-md-3, + .my-md-3 { + margin-top: 1rem !important; + } + + .mr-md-3, + .mx-md-3 { + margin-right: 1rem !important; + } + + .mb-md-3, + .my-md-3 { + margin-bottom: 1rem !important; + } + + .ml-md-3, + .mx-md-3 { + margin-left: 1rem !important; + } + + .m-md-4 { + margin: 1.5rem !important; + } + + .mt-md-4, + .my-md-4 { + margin-top: 1.5rem !important; + } + + .mr-md-4, + .mx-md-4 { + margin-right: 1.5rem !important; + } + + .mb-md-4, + .my-md-4 { + margin-bottom: 1.5rem !important; + } + + .ml-md-4, + .mx-md-4 { + margin-left: 1.5rem !important; + } + + .m-md-5 { + margin: 3rem !important; + } + + .mt-md-5, + .my-md-5 { + margin-top: 3rem !important; + } + + .mr-md-5, + .mx-md-5 { + margin-right: 3rem !important; + } + + .mb-md-5, + .my-md-5 { + margin-bottom: 3rem !important; + } + + .ml-md-5, + .mx-md-5 { + margin-left: 3rem !important; + } + + .p-md-0 { + padding: 0 !important; + } + + .pt-md-0, + .py-md-0 { + padding-top: 0 !important; + } + + .pr-md-0, + .px-md-0 { + padding-right: 0 !important; + } + + .pb-md-0, + .py-md-0 { + padding-bottom: 0 !important; + } + + .pl-md-0, + .px-md-0 { + padding-left: 0 !important; + } + + .p-md-1 { + padding: 0.25rem !important; + } + + .pt-md-1, + .py-md-1 { + padding-top: 0.25rem !important; + } + + .pr-md-1, + .px-md-1 { + padding-right: 0.25rem !important; + } + + .pb-md-1, + .py-md-1 { + padding-bottom: 0.25rem !important; + } + + .pl-md-1, + .px-md-1 { + padding-left: 0.25rem !important; + } + + .p-md-2 { + padding: 0.5rem !important; + } + + .pt-md-2, + .py-md-2 { + padding-top: 0.5rem !important; + } + + .pr-md-2, + .px-md-2 { + padding-right: 0.5rem !important; + } + + .pb-md-2, + .py-md-2 { + padding-bottom: 0.5rem !important; + } + + .pl-md-2, + .px-md-2 { + padding-left: 0.5rem !important; + } + + .p-md-3 { + padding: 1rem !important; + } + + .pt-md-3, + .py-md-3 { + padding-top: 1rem !important; + } + + .pr-md-3, + .px-md-3 { + padding-right: 1rem !important; + } + + .pb-md-3, + .py-md-3 { + padding-bottom: 1rem !important; + } + + .pl-md-3, + .px-md-3 { + padding-left: 1rem !important; + } + + .p-md-4 { + padding: 1.5rem !important; + } + + .pt-md-4, + .py-md-4 { + padding-top: 1.5rem !important; + } + + .pr-md-4, + .px-md-4 { + padding-right: 1.5rem !important; + } + + .pb-md-4, + .py-md-4 { + padding-bottom: 1.5rem !important; + } + + .pl-md-4, + .px-md-4 { + padding-left: 1.5rem !important; + } + + .p-md-5 { + padding: 3rem !important; + } + + .pt-md-5, + .py-md-5 { + padding-top: 3rem !important; + } + + .pr-md-5, + .px-md-5 { + padding-right: 3rem !important; + } + + .pb-md-5, + .py-md-5 { + padding-bottom: 3rem !important; + } + + .pl-md-5, + .px-md-5 { + padding-left: 3rem !important; + } + + .m-md-n1 { + margin: -0.25rem !important; + } + + .mt-md-n1, + .my-md-n1 { + margin-top: -0.25rem !important; + } + + .mr-md-n1, + .mx-md-n1 { + margin-right: -0.25rem !important; + } + + .mb-md-n1, + .my-md-n1 { + margin-bottom: -0.25rem !important; + } + + .ml-md-n1, + .mx-md-n1 { + margin-left: -0.25rem !important; + } + + .m-md-n2 { + margin: -0.5rem !important; + } + + .mt-md-n2, + .my-md-n2 { + margin-top: -0.5rem !important; + } + + .mr-md-n2, + .mx-md-n2 { + margin-right: -0.5rem !important; + } + + .mb-md-n2, + .my-md-n2 { + margin-bottom: -0.5rem !important; + } + + .ml-md-n2, + .mx-md-n2 { + margin-left: -0.5rem !important; + } + + .m-md-n3 { + margin: -1rem !important; + } + + .mt-md-n3, + .my-md-n3 { + margin-top: -1rem !important; + } + + .mr-md-n3, + .mx-md-n3 { + margin-right: -1rem !important; + } + + .mb-md-n3, + .my-md-n3 { + margin-bottom: -1rem !important; + } + + .ml-md-n3, + .mx-md-n3 { + margin-left: -1rem !important; + } + + .m-md-n4 { + margin: -1.5rem !important; + } + + .mt-md-n4, + .my-md-n4 { + margin-top: -1.5rem !important; + } + + .mr-md-n4, + .mx-md-n4 { + margin-right: -1.5rem !important; + } + + .mb-md-n4, + .my-md-n4 { + margin-bottom: -1.5rem !important; + } + + .ml-md-n4, + .mx-md-n4 { + margin-left: -1.5rem !important; + } + + .m-md-n5 { + margin: -3rem !important; + } + + .mt-md-n5, + .my-md-n5 { + margin-top: -3rem !important; + } + + .mr-md-n5, + .mx-md-n5 { + margin-right: -3rem !important; + } + + .mb-md-n5, + .my-md-n5 { + margin-bottom: -3rem !important; + } + + .ml-md-n5, + .mx-md-n5 { + margin-left: -3rem !important; + } + + .m-md-auto { + margin: auto !important; + } + + .mt-md-auto, + .my-md-auto { + margin-top: auto !important; + } + + .mr-md-auto, + .mx-md-auto { + margin-right: auto !important; + } + + .mb-md-auto, + .my-md-auto { + margin-bottom: auto !important; + } + + .ml-md-auto, + .mx-md-auto { + margin-left: auto !important; + } +} + +@media (min-width: 1100px) { + .m-lg-0 { + margin: 0 !important; + } + + .mt-lg-0, + .my-lg-0 { + margin-top: 0 !important; + } + + .mr-lg-0, + .mx-lg-0 { + margin-right: 0 !important; + } + + .mb-lg-0, + .my-lg-0 { + margin-bottom: 0 !important; + } + + .ml-lg-0, + .mx-lg-0 { + margin-left: 0 !important; + } + + .m-lg-1 { + margin: 0.25rem !important; + } + + .mt-lg-1, + .my-lg-1 { + margin-top: 0.25rem !important; + } + + .mr-lg-1, + .mx-lg-1 { + margin-right: 0.25rem !important; + } + + .mb-lg-1, + .my-lg-1 { + margin-bottom: 0.25rem !important; + } + + .ml-lg-1, + .mx-lg-1 { + margin-left: 0.25rem !important; + } + + .m-lg-2 { + margin: 0.5rem !important; + } + + .mt-lg-2, + .my-lg-2 { + margin-top: 0.5rem !important; + } + + .mr-lg-2, + .mx-lg-2 { + margin-right: 0.5rem !important; + } + + .mb-lg-2, + .my-lg-2 { + margin-bottom: 0.5rem !important; + } + + .ml-lg-2, + .mx-lg-2 { + margin-left: 0.5rem !important; + } + + .m-lg-3 { + margin: 1rem !important; + } + + .mt-lg-3, + .my-lg-3 { + margin-top: 1rem !important; + } + + .mr-lg-3, + .mx-lg-3 { + margin-right: 1rem !important; + } + + .mb-lg-3, + .my-lg-3 { + margin-bottom: 1rem !important; + } + + .ml-lg-3, + .mx-lg-3 { + margin-left: 1rem !important; + } + + .m-lg-4 { + margin: 1.5rem !important; + } + + .mt-lg-4, + .my-lg-4 { + margin-top: 1.5rem !important; + } + + .mr-lg-4, + .mx-lg-4 { + margin-right: 1.5rem !important; + } + + .mb-lg-4, + .my-lg-4 { + margin-bottom: 1.5rem !important; + } + + .ml-lg-4, + .mx-lg-4 { + margin-left: 1.5rem !important; + } + + .m-lg-5 { + margin: 3rem !important; + } + + .mt-lg-5, + .my-lg-5 { + margin-top: 3rem !important; + } + + .mr-lg-5, + .mx-lg-5 { + margin-right: 3rem !important; + } + + .mb-lg-5, + .my-lg-5 { + margin-bottom: 3rem !important; + } + + .ml-lg-5, + .mx-lg-5 { + margin-left: 3rem !important; + } + + .p-lg-0 { + padding: 0 !important; + } + + .pt-lg-0, + .py-lg-0 { + padding-top: 0 !important; + } + + .pr-lg-0, + .px-lg-0 { + padding-right: 0 !important; + } + + .pb-lg-0, + .py-lg-0 { + padding-bottom: 0 !important; + } + + .pl-lg-0, + .px-lg-0 { + padding-left: 0 !important; + } + + .p-lg-1 { + padding: 0.25rem !important; + } + + .pt-lg-1, + .py-lg-1 { + padding-top: 0.25rem !important; + } + + .pr-lg-1, + .px-lg-1 { + padding-right: 0.25rem !important; + } + + .pb-lg-1, + .py-lg-1 { + padding-bottom: 0.25rem !important; + } + + .pl-lg-1, + .px-lg-1 { + padding-left: 0.25rem !important; + } + + .p-lg-2 { + padding: 0.5rem !important; + } + + .pt-lg-2, + .py-lg-2 { + padding-top: 0.5rem !important; + } + + .pr-lg-2, + .px-lg-2 { + padding-right: 0.5rem !important; + } + + .pb-lg-2, + .py-lg-2 { + padding-bottom: 0.5rem !important; + } + + .pl-lg-2, + .px-lg-2 { + padding-left: 0.5rem !important; + } + + .p-lg-3 { + padding: 1rem !important; + } + + .pt-lg-3, + .py-lg-3 { + padding-top: 1rem !important; + } + + .pr-lg-3, + .px-lg-3 { + padding-right: 1rem !important; + } + + .pb-lg-3, + .py-lg-3 { + padding-bottom: 1rem !important; + } + + .pl-lg-3, + .px-lg-3 { + padding-left: 1rem !important; + } + + .p-lg-4 { + padding: 1.5rem !important; + } + + .pt-lg-4, + .py-lg-4 { + padding-top: 1.5rem !important; + } + + .pr-lg-4, + .px-lg-4 { + padding-right: 1.5rem !important; + } + + .pb-lg-4, + .py-lg-4 { + padding-bottom: 1.5rem !important; + } + + .pl-lg-4, + .px-lg-4 { + padding-left: 1.5rem !important; + } + + .p-lg-5 { + padding: 3rem !important; + } + + .pt-lg-5, + .py-lg-5 { + padding-top: 3rem !important; + } + + .pr-lg-5, + .px-lg-5 { + padding-right: 3rem !important; + } + + .pb-lg-5, + .py-lg-5 { + padding-bottom: 3rem !important; + } + + .pl-lg-5, + .px-lg-5 { + padding-left: 3rem !important; + } + + .m-lg-n1 { + margin: -0.25rem !important; + } + + .mt-lg-n1, + .my-lg-n1 { + margin-top: -0.25rem !important; + } + + .mr-lg-n1, + .mx-lg-n1 { + margin-right: -0.25rem !important; + } + + .mb-lg-n1, + .my-lg-n1 { + margin-bottom: -0.25rem !important; + } + + .ml-lg-n1, + .mx-lg-n1 { + margin-left: -0.25rem !important; + } + + .m-lg-n2 { + margin: -0.5rem !important; + } + + .mt-lg-n2, + .my-lg-n2 { + margin-top: -0.5rem !important; + } + + .mr-lg-n2, + .mx-lg-n2 { + margin-right: -0.5rem !important; + } + + .mb-lg-n2, + .my-lg-n2 { + margin-bottom: -0.5rem !important; + } + + .ml-lg-n2, + .mx-lg-n2 { + margin-left: -0.5rem !important; + } + + .m-lg-n3 { + margin: -1rem !important; + } + + .mt-lg-n3, + .my-lg-n3 { + margin-top: -1rem !important; + } + + .mr-lg-n3, + .mx-lg-n3 { + margin-right: -1rem !important; + } + + .mb-lg-n3, + .my-lg-n3 { + margin-bottom: -1rem !important; + } + + .ml-lg-n3, + .mx-lg-n3 { + margin-left: -1rem !important; + } + + .m-lg-n4 { + margin: -1.5rem !important; + } + + .mt-lg-n4, + .my-lg-n4 { + margin-top: -1.5rem !important; + } + + .mr-lg-n4, + .mx-lg-n4 { + margin-right: -1.5rem !important; + } + + .mb-lg-n4, + .my-lg-n4 { + margin-bottom: -1.5rem !important; + } + + .ml-lg-n4, + .mx-lg-n4 { + margin-left: -1.5rem !important; + } + + .m-lg-n5 { + margin: -3rem !important; + } + + .mt-lg-n5, + .my-lg-n5 { + margin-top: -3rem !important; + } + + .mr-lg-n5, + .mx-lg-n5 { + margin-right: -3rem !important; + } + + .mb-lg-n5, + .my-lg-n5 { + margin-bottom: -3rem !important; + } + + .ml-lg-n5, + .mx-lg-n5 { + margin-left: -3rem !important; + } + + .m-lg-auto { + margin: auto !important; + } + + .mt-lg-auto, + .my-lg-auto { + margin-top: auto !important; + } + + .mr-lg-auto, + .mx-lg-auto { + margin-right: auto !important; + } + + .mb-lg-auto, + .my-lg-auto { + margin-bottom: auto !important; + } + + .ml-lg-auto, + .mx-lg-auto { + margin-left: auto !important; + } +} + +@media (min-width: 1400px) { + .m-xl-0 { + margin: 0 !important; + } + + .mt-xl-0, + .my-xl-0 { + margin-top: 0 !important; + } + + .mr-xl-0, + .mx-xl-0 { + margin-right: 0 !important; + } + + .mb-xl-0, + .my-xl-0 { + margin-bottom: 0 !important; + } + + .ml-xl-0, + .mx-xl-0 { + margin-left: 0 !important; + } + + .m-xl-1 { + margin: 0.25rem !important; + } + + .mt-xl-1, + .my-xl-1 { + margin-top: 0.25rem !important; + } + + .mr-xl-1, + .mx-xl-1 { + margin-right: 0.25rem !important; + } + + .mb-xl-1, + .my-xl-1 { + margin-bottom: 0.25rem !important; + } + + .ml-xl-1, + .mx-xl-1 { + margin-left: 0.25rem !important; + } + + .m-xl-2 { + margin: 0.5rem !important; + } + + .mt-xl-2, + .my-xl-2 { + margin-top: 0.5rem !important; + } + + .mr-xl-2, + .mx-xl-2 { + margin-right: 0.5rem !important; + } + + .mb-xl-2, + .my-xl-2 { + margin-bottom: 0.5rem !important; + } + + .ml-xl-2, + .mx-xl-2 { + margin-left: 0.5rem !important; + } + + .m-xl-3 { + margin: 1rem !important; + } + + .mt-xl-3, + .my-xl-3 { + margin-top: 1rem !important; + } + + .mr-xl-3, + .mx-xl-3 { + margin-right: 1rem !important; + } + + .mb-xl-3, + .my-xl-3 { + margin-bottom: 1rem !important; + } + + .ml-xl-3, + .mx-xl-3 { + margin-left: 1rem !important; + } + + .m-xl-4 { + margin: 1.5rem !important; + } + + .mt-xl-4, + .my-xl-4 { + margin-top: 1.5rem !important; + } + + .mr-xl-4, + .mx-xl-4 { + margin-right: 1.5rem !important; + } + + .mb-xl-4, + .my-xl-4 { + margin-bottom: 1.5rem !important; + } + + .ml-xl-4, + .mx-xl-4 { + margin-left: 1.5rem !important; + } + + .m-xl-5 { + margin: 3rem !important; + } + + .mt-xl-5, + .my-xl-5 { + margin-top: 3rem !important; + } + + .mr-xl-5, + .mx-xl-5 { + margin-right: 3rem !important; + } + + .mb-xl-5, + .my-xl-5 { + margin-bottom: 3rem !important; + } + + .ml-xl-5, + .mx-xl-5 { + margin-left: 3rem !important; + } + + .p-xl-0 { + padding: 0 !important; + } + + .pt-xl-0, + .py-xl-0 { + padding-top: 0 !important; + } + + .pr-xl-0, + .px-xl-0 { + padding-right: 0 !important; + } + + .pb-xl-0, + .py-xl-0 { + padding-bottom: 0 !important; + } + + .pl-xl-0, + .px-xl-0 { + padding-left: 0 !important; + } + + .p-xl-1 { + padding: 0.25rem !important; + } + + .pt-xl-1, + .py-xl-1 { + padding-top: 0.25rem !important; + } + + .pr-xl-1, + .px-xl-1 { + padding-right: 0.25rem !important; + } + + .pb-xl-1, + .py-xl-1 { + padding-bottom: 0.25rem !important; + } + + .pl-xl-1, + .px-xl-1 { + padding-left: 0.25rem !important; + } + + .p-xl-2 { + padding: 0.5rem !important; + } + + .pt-xl-2, + .py-xl-2 { + padding-top: 0.5rem !important; + } + + .pr-xl-2, + .px-xl-2 { + padding-right: 0.5rem !important; + } + + .pb-xl-2, + .py-xl-2 { + padding-bottom: 0.5rem !important; + } + + .pl-xl-2, + .px-xl-2 { + padding-left: 0.5rem !important; + } + + .p-xl-3 { + padding: 1rem !important; + } + + .pt-xl-3, + .py-xl-3 { + padding-top: 1rem !important; + } + + .pr-xl-3, + .px-xl-3 { + padding-right: 1rem !important; + } + + .pb-xl-3, + .py-xl-3 { + padding-bottom: 1rem !important; + } + + .pl-xl-3, + .px-xl-3 { + padding-left: 1rem !important; + } + + .p-xl-4 { + padding: 1.5rem !important; + } + + .pt-xl-4, + .py-xl-4 { + padding-top: 1.5rem !important; + } + + .pr-xl-4, + .px-xl-4 { + padding-right: 1.5rem !important; + } + + .pb-xl-4, + .py-xl-4 { + padding-bottom: 1.5rem !important; + } + + .pl-xl-4, + .px-xl-4 { + padding-left: 1.5rem !important; + } + + .p-xl-5 { + padding: 3rem !important; + } + + .pt-xl-5, + .py-xl-5 { + padding-top: 3rem !important; + } + + .pr-xl-5, + .px-xl-5 { + padding-right: 3rem !important; + } + + .pb-xl-5, + .py-xl-5 { + padding-bottom: 3rem !important; + } + + .pl-xl-5, + .px-xl-5 { + padding-left: 3rem !important; + } + + .m-xl-n1 { + margin: -0.25rem !important; + } + + .mt-xl-n1, + .my-xl-n1 { + margin-top: -0.25rem !important; + } + + .mr-xl-n1, + .mx-xl-n1 { + margin-right: -0.25rem !important; + } + + .mb-xl-n1, + .my-xl-n1 { + margin-bottom: -0.25rem !important; + } + + .ml-xl-n1, + .mx-xl-n1 { + margin-left: -0.25rem !important; + } + + .m-xl-n2 { + margin: -0.5rem !important; + } + + .mt-xl-n2, + .my-xl-n2 { + margin-top: -0.5rem !important; + } + + .mr-xl-n2, + .mx-xl-n2 { + margin-right: -0.5rem !important; + } + + .mb-xl-n2, + .my-xl-n2 { + margin-bottom: -0.5rem !important; + } + + .ml-xl-n2, + .mx-xl-n2 { + margin-left: -0.5rem !important; + } + + .m-xl-n3 { + margin: -1rem !important; + } + + .mt-xl-n3, + .my-xl-n3 { + margin-top: -1rem !important; + } + + .mr-xl-n3, + .mx-xl-n3 { + margin-right: -1rem !important; + } + + .mb-xl-n3, + .my-xl-n3 { + margin-bottom: -1rem !important; + } + + .ml-xl-n3, + .mx-xl-n3 { + margin-left: -1rem !important; + } + + .m-xl-n4 { + margin: -1.5rem !important; + } + + .mt-xl-n4, + .my-xl-n4 { + margin-top: -1.5rem !important; + } + + .mr-xl-n4, + .mx-xl-n4 { + margin-right: -1.5rem !important; + } + + .mb-xl-n4, + .my-xl-n4 { + margin-bottom: -1.5rem !important; + } + + .ml-xl-n4, + .mx-xl-n4 { + margin-left: -1.5rem !important; + } + + .m-xl-n5 { + margin: -3rem !important; + } + + .mt-xl-n5, + .my-xl-n5 { + margin-top: -3rem !important; + } + + .mr-xl-n5, + .mx-xl-n5 { + margin-right: -3rem !important; + } + + .mb-xl-n5, + .my-xl-n5 { + margin-bottom: -3rem !important; + } + + .ml-xl-n5, + .mx-xl-n5 { + margin-left: -3rem !important; + } + + .m-xl-auto { + margin: auto !important; + } + + .mt-xl-auto, + .my-xl-auto { + margin-top: auto !important; + } + + .mr-xl-auto, + .mx-xl-auto { + margin-right: auto !important; + } + + .mb-xl-auto, + .my-xl-auto { + margin-bottom: auto !important; + } + + .ml-xl-auto, + .mx-xl-auto { + margin-left: auto !important; + } +} + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +.text-justify { + text-align: justify !important; +} + +.text-wrap { + white-space: normal !important; +} + +.text-nowrap { + white-space: nowrap !important; +} + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important; + } + + .text-sm-right { + text-align: right !important; + } + + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .text-md-left { + text-align: left !important; + } + + .text-md-right { + text-align: right !important; + } + + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 1100px) { + .text-lg-left { + text-align: left !important; + } + + .text-lg-right { + text-align: right !important; + } + + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1400px) { + .text-xl-left { + text-align: left !important; + } + + .text-xl-right { + text-align: right !important; + } + + .text-xl-center { + text-align: center !important; + } +} + +.text-lowercase { + text-transform: lowercase !important; +} + +.text-uppercase, .dropdown .dropdown-menu .dropdown-header, .sidebar .sidebar-heading { + text-transform: uppercase !important; +} + +.text-capitalize { + text-transform: capitalize !important; +} + +.font-weight-light { + font-weight: 300 !important; +} + +.font-weight-lighter { + font-weight: lighter !important; +} + +.font-weight-normal { + font-weight: 400 !important; +} + +.font-weight-bold { + font-weight: 700 !important; +} + +.font-weight-bolder { + font-weight: bolder !important; +} + +.font-italic { + font-style: italic !important; +} + +.text-white { + color: #fff !important; +} + +.text-primary { + color: #4e73df !important; +} + +a.text-primary:hover, a.text-primary:focus { + color: #224abe !important; +} + +.text-secondary { + color: #858796 !important; +} + +a.text-secondary:hover, a.text-secondary:focus { + color: #60616f !important; +} + +.text-success { + color: #1cc88a !important; +} + +a.text-success:hover, a.text-success:focus { + color: #13855c !important; +} + +.text-info { + color: #36b9cc !important; +} + +a.text-info:hover, a.text-info:focus { + color: #258391 !important; +} + +.text-warning { + color: #f6c23e !important; +} + +a.text-warning:hover, a.text-warning:focus { + color: #dda20a !important; +} + +.text-danger { + color: #e74a3b !important; +} + +a.text-danger:hover, a.text-danger:focus { + color: #be2617 !important; +} + +.text-light { + color: #f8f9fc !important; +} + +a.text-light:hover, a.text-light:focus { + color: #c2cbe5 !important; +} + +.text-dark { + color: #5a5c69 !important; +} + +a.text-dark:hover, a.text-dark:focus { + color: #373840 !important; +} + +.text-body { + color: #858796 !important; +} + +.text-muted { + color: #858796 !important; +} + +.text-black-50 { + color: rgba(0, 0, 0, 0.5) !important; +} + +.text-white-50 { + color: rgba(255, 255, 255, 0.5) !important; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.text-decoration-none { + text-decoration: none !important; +} + +.text-reset { + color: inherit !important; +} + +.visible { + visibility: visible !important; +} + +.invisible { + visibility: hidden !important; +} + +@media print { + *, + *::before, + *::after { + text-shadow: none !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + + a:not(.btn) { + text-decoration: underline; + } + + abbr[title]::after { + content: " (" attr(title) ")"; + } + + pre { + white-space: pre-wrap !important; + } + + pre, + blockquote { + border: 1px solid #b7b9cc; + page-break-inside: avoid; + } + + thead { + display: table-header-group; + } + + tr, + img { + page-break-inside: avoid; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } + + @page { + size: a3; + } + + body { + min-width: 992px !important; + } + + .container { + min-width: 992px !important; + } + + .navbar { + display: none; + } + + .badge { + border: 1px solid #000; + } + + .table { + border-collapse: collapse !important; + } + + .table td, + .table th { + background-color: #fff !important; + } + + .table-bordered th, + .table-bordered td { + border: 1px solid #dddfeb !important; + } + + .table-dark { + color: inherit; + } + + .table-dark th, + .table-dark td, + .table-dark thead th, + .table-dark tbody + tbody { + border-color: #dddfeb; + } + + .table .thead-dark th { + color: inherit; + border-color: #dddfeb; + } +} + +html { + position: relative; + min-height: 100%; +} + +body { + height: 100%; +} + +a:focus { + outline: none; +} + +#wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; +} + +#wrapper #content-wrapper { + background-color: #f8f9fc; + width: 100%; + overflow-x: hidden; +} + +#wrapper #content-wrapper #content { + -webkit-box-flex: 1; + -ms-flex: 1 0 auto; + flex: 1 0 auto; +} + +.container, +.container-fluid { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.scroll-to-top { + position: fixed; + right: 1rem; + bottom: 1rem; + display: none; + width: 2.75rem; + height: 2.75rem; + text-align: center; + color: #fff; + background: rgba(90, 92, 105, 0.5); + line-height: 46px; +} + +.scroll-to-top:focus, .scroll-to-top:hover { + color: white; +} + +.scroll-to-top:hover { + background: #5a5c69; +} + +.scroll-to-top i { + font-weight: 800; +} + +@-webkit-keyframes growIn { + 0% { + -webkit-transform: scale(0.9); + transform: scale(0.9); + opacity: 0; + } + 100% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } +} + +@keyframes growIn { + 0% { + -webkit-transform: scale(0.9); + transform: scale(0.9); + opacity: 0; + } + 100% { + -webkit-transform: scale(1); + transform: scale(1); + opacity: 1; + } +} + +.animated--grow-in, .sidebar .nav-item .collapse { + -webkit-animation-name: growIn; + animation-name: growIn; + -webkit-animation-duration: 200ms; + animation-duration: 200ms; + -webkit-animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1); + animation-timing-function: transform cubic-bezier(0.18, 1.25, 0.4, 1), opacity cubic-bezier(0, 1, 0.4, 1); +} + +@-webkit-keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +@keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +.animated--fade-in { + -webkit-animation-name: fadeIn; + animation-name: fadeIn; + -webkit-animation-duration: 200ms; + animation-duration: 200ms; + -webkit-animation-timing-function: opacity cubic-bezier(0, 1, 0.4, 1); + animation-timing-function: opacity cubic-bezier(0, 1, 0.4, 1); +} + +.bg-gradient-primary { + background-color: #4e73df; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(10%, #4e73df), to(#224abe)); + background-image: linear-gradient(180deg, #4e73df 10%, #224abe 100%); + background-size: cover; +} + +.bg-gradient-success { + background-color: #1cc88a; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(10%, #1cc88a), to(#13855c)); + background-image: linear-gradient(180deg, #1cc88a 10%, #13855c 100%); + background-size: cover; +} + +.bg-gradient-info { + background-color: #36b9cc; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(10%, #36b9cc), to(#258391)); + background-image: linear-gradient(180deg, #36b9cc 10%, #258391 100%); + background-size: cover; +} + +.bg-gradient-warning { + background-color: #f6c23e; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(10%, #f6c23e), to(#dda20a)); + background-image: linear-gradient(180deg, #f6c23e 10%, #dda20a 100%); + background-size: cover; +} + +.bg-gradient-danger { + background-color: #e74a3b; + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(10%, #e74a3b), to(#be2617)); + background-image: linear-gradient(180deg, #e74a3b 10%, #be2617 100%); + background-size: cover; +} + +.bg-gray-100 { + background-color: #f8f9fc !important; +} + +.bg-gray-200 { + background-color: #eaecf4 !important; +} + +.bg-gray-300 { + background-color: #dddfeb !important; +} + +.bg-gray-400 { + background-color: #d1d3e2 !important; +} + +.bg-gray-500 { + background-color: #b7b9cc !important; +} + +.bg-gray-600 { + background-color: #858796 !important; +} + +.bg-gray-700 { + background-color: #6e707e !important; +} + +.bg-gray-800 { + background-color: #5a5c69 !important; +} + +.bg-gray-900 { + background-color: #3a3b45 !important; +} + +.o-hidden { + overflow: hidden !important; +} + +.text-xs { + font-size: .7rem; +} + +.text-lg { + font-size: 1.2rem; +} + +.text-gray-100 { + color: #f8f9fc; +} + +.text-gray-200 { + color: #eaecf4; +} + +.text-gray-300 { + color: #dddfeb; +} + +.text-gray-400 { + color: #d1d3e2; +} + +.text-gray-500 { + color: #b7b9cc; +} + +.text-gray-600 { + color: #858796; +} + +.text-gray-700 { + color: #6e707e; +} + +.text-gray-800 { + color: #5a5c69; +} + +.text-gray-900 { + color: #3a3b45; +} + +.icon-circle { + height: 2.5rem; + width: 2.5rem; + border-radius: 100%; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.border-left-primary { + border-left: 0.25rem solid #4e73df !important; +} + +.border-left-success { + border-left: 0.25rem solid #1cc88a !important; +} + +.border-left-info { + border-left: 0.25rem solid #36b9cc !important; +} + +.border-left-warning { + border-left: 0.25rem solid #f6c23e !important; +} + +.border-left-danger { + border-left: 0.25rem solid #e74a3b !important; +} + +.border-bottom-primary { + border-bottom: 0.25rem solid #4e73df !important; +} + +.border-bottom-success { + border-bottom: 0.25rem solid #1cc88a !important; +} + +.border-bottom-info { + border-bottom: 0.25rem solid #36b9cc !important; +} + +.border-bottom-warning { + border-bottom: 0.25rem solid #f6c23e !important; +} + +.border-bottom-danger { + border-bottom: 0.25rem solid #e74a3b !important; +} + +.progress-sm { + height: .5rem; +} + +.rotate-15 { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); +} + +.rotate-n-15 { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); +} + +.dropdown .dropdown-menu { + font-size: 0.85rem; +} + +.dropdown .dropdown-menu .dropdown-header { + font-weight: 800; + font-size: 0.65rem; + color: #b7b9cc; +} + +.dropdown.no-arrow .dropdown-toggle::after { + display: none; +} + +.sidebar .nav-item.dropdown .dropdown-toggle::after, +.topbar .nav-item.dropdown .dropdown-toggle::after { + width: 1rem; + text-align: center; + float: right; + vertical-align: 0; + border: 0; + font-weight: 900; + content: '\f105'; + font-family: 'Font Awesome 5 Free'; +} + +.sidebar .nav-item.dropdown.show .dropdown-toggle::after, +.topbar .nav-item.dropdown.show .dropdown-toggle::after { + content: '\f107'; +} + +.sidebar .nav-item .nav-link, +.topbar .nav-item .nav-link { + position: relative; +} + +.sidebar .nav-item .nav-link .badge-counter, +.topbar .nav-item .nav-link .badge-counter { + position: absolute; + -webkit-transform: scale(0.7); + transform: scale(0.7); + -webkit-transform-origin: top right; + transform-origin: top right; + right: .25rem; + margin-top: -.25rem; +} + +.sidebar .nav-item .nav-link .img-profile, +.topbar .nav-item .nav-link .img-profile { + height: 2rem; + width: 2rem; +} + +.topbar { + height: 4.375rem; +} + +.topbar #sidebarToggleTop { + height: 2.5rem; + width: 2.5rem; +} + +.topbar #sidebarToggleTop:hover { + background-color: #eaecf4; +} + +.topbar #sidebarToggleTop:active { + background-color: #dddfeb; +} + +.topbar .navbar-search { + width: 25rem; +} + +.topbar .navbar-search input { + font-size: 0.85rem; +} + +.topbar .topbar-divider { + width: 0; + border-right: 1px solid #e3e6f0; + height: calc(4.375rem - 2rem); + margin: auto 1rem; +} + +.topbar .nav-item .nav-link { + height: 4.375rem; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + padding: 0 0.75rem; +} + +.topbar .nav-item .nav-link:focus { + outline: none; +} + +.topbar .nav-item:focus { + outline: none; +} + +.topbar .dropdown { + position: static; +} + +.topbar .dropdown .dropdown-menu { + width: calc(100% - 1.5rem); + right: 0.75rem; +} + +.topbar .dropdown-list { + padding: 0; + border: none; + overflow: hidden; +} + +.topbar .dropdown-list .dropdown-header { + background-color: #4e73df; + border: 1px solid #4e73df; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #fff; +} + +.topbar .dropdown-list .dropdown-item { + white-space: normal; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + border-left: 1px solid #e3e6f0; + border-right: 1px solid #e3e6f0; + border-bottom: 1px solid #e3e6f0; + line-height: 1.3rem; +} + +.topbar .dropdown-list .dropdown-item .dropdown-list-image { + position: relative; + height: 2.5rem; + width: 2.5rem; +} + +.topbar .dropdown-list .dropdown-item .dropdown-list-image img { + height: 2.5rem; + width: 2.5rem; +} + +.topbar .dropdown-list .dropdown-item .dropdown-list-image .status-indicator { + background-color: #eaecf4; + height: 0.75rem; + width: 0.75rem; + border-radius: 100%; + position: absolute; + bottom: 0; + right: 0; + border: 0.125rem solid #fff; +} + +.topbar .dropdown-list .dropdown-item .text-truncate { + max-width: 10rem; +} + +.topbar .dropdown-list .dropdown-item:active { + background-color: #eaecf4; + color: #3a3b45; +} + +@media (min-width: 576px) { + .topbar .dropdown { + position: relative; + } + + .topbar .dropdown .dropdown-menu { + width: auto; + right: 0; + } + + .topbar .dropdown-list { + width: 20rem !important; + } + + .topbar .dropdown-list .dropdown-item .text-truncate { + max-width: 13.375rem; + } +} + +.topbar.navbar-light .navbar-nav .nav-item .nav-link { + color: #d1d3e2; +} + +.topbar.navbar-light .navbar-nav .nav-item .nav-link:hover { + color: #b7b9cc; +} + +.topbar.navbar-light .navbar-nav .nav-item .nav-link:active { + color: #858796; +} + +.nav-link.sidebar-toggler { + padding-top: 0 !important; +} + +.sidebar-toggler { + padding: 0.6rem; + display: inline; + cursor: pointer; +} + +.sidebar-close-modal { + position: absolute; + opacity: 0.7; + background-color: #000; + width: 100%; + height: 100%; + top: 0; + z-index: 1; + cursor: pointer; +} + +.hidden { + width: 0 !important; +} + +.hidden .sidebar .sidebar-brand img { + display: none; +} + +@media (min-width: 1400px) { + .sidebar-toggler { + display: none !important; + } +} + +.sidebar { + width: 6.5rem; + min-height: 100vh; +} + +.sidebar .nav-item { + position: relative; +} + +.sidebar .nav-item:last-child { + margin-bottom: 1rem; +} + +.sidebar .nav-item .nav-link { + text-align: center; + padding: 0.75rem 1rem; + width: 6.5rem; +} + +.sidebar .nav-item .nav-link span { + font-size: 0.65rem; + display: block; +} + +.sidebar .nav-item.active .nav-link { + font-weight: 700; +} + +.sidebar .nav-item .collapse { + position: absolute; + left: calc(6.5rem + 1.5rem / 2); + z-index: 1; + top: 2px; +} + +.sidebar .nav-item .collapse .collapse-inner { + border-radius: 0.35rem; + -webkit-box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15); + box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15); +} + +.sidebar .nav-item .collapsing { + display: none; + -webkit-transition: none; + transition: none; +} + +.sidebar .nav-item .collapse .collapse-inner, +.sidebar .nav-item .collapsing .collapse-inner { + padding: .5rem 0; + min-width: 10rem; + font-size: 0.85rem; + margin: 0 0 1rem 0; +} + +.sidebar .nav-item .collapse .collapse-inner .collapse-header, +.sidebar .nav-item .collapsing .collapse-inner .collapse-header { + margin: 0; + white-space: nowrap; + padding: .5rem 1.5rem; + text-transform: uppercase; + font-weight: 800; + font-size: 0.65rem; + color: #b7b9cc; +} + +.sidebar .nav-item .collapse .collapse-inner .collapse-item, +.sidebar .nav-item .collapsing .collapse-inner .collapse-item { + padding: 0.5rem 1rem; + margin: 0 0.5rem; + display: block; + color: #3a3b45; + text-decoration: none; + border-radius: 0.35rem; + white-space: nowrap; +} + +.sidebar .nav-item .collapse .collapse-inner .collapse-item:hover, +.sidebar .nav-item .collapsing .collapse-inner .collapse-item:hover { + background-color: #eaecf4; +} + +.sidebar .nav-item .collapse .collapse-inner .collapse-item:active, +.sidebar .nav-item .collapsing .collapse-inner .collapse-item:active { + background-color: #dddfeb; +} + +.sidebar .nav-item .collapse .collapse-inner .collapse-item.active, +.sidebar .nav-item .collapsing .collapse-inner .collapse-item.active { + font-weight: 700; +} + +.sidebar .sidebar-brand { + height: 4.375rem; + text-decoration: none; + font-size: 1rem; + font-weight: 800; + padding: 1.5rem 1rem; + text-align: center; + text-transform: uppercase; + letter-spacing: 0.05rem; + z-index: 1; +} + +.sidebar .sidebar-brand .sidebar-brand-icon i { + font-size: 2rem; +} + +.sidebar .sidebar-brand .sidebar-brand-text { + display: none; +} + +.sidebar hr.sidebar-divider { + margin: 0 1rem 1rem; +} + +.sidebar .sidebar-heading { + text-align: center; + padding: 0 1rem; + font-weight: 800; + font-size: 0.65rem; +} + +@media (min-width: 0px) { + .sidebar { + width: 14rem; + } + + .sidebar .nav-item .collapse { + position: relative; + left: 0; + z-index: 1; + top: 0; + -webkit-animation: none; + animation: none; + } + + .sidebar .nav-item .collapse .collapse-inner { + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + + .sidebar .nav-item .collapsing { + display: block; + -webkit-transition: height 0.15s ease; + transition: height 0.15s ease; + } + + .sidebar .nav-item .collapse, + .sidebar .nav-item .collapsing { + margin: 0 1rem; + } + + .sidebar .nav-item .nav-link { + display: block; + width: 100%; + text-align: left; + padding: 1rem; + width: 14rem; + } + + .sidebar .nav-item .nav-link i { + font-size: 0.85rem; + margin-right: 0.25rem; + } + + .sidebar .nav-item .nav-link span { + font-size: 0.85rem; + display: inline; + } + + .sidebar .nav-item .nav-link[data-toggle="collapse"]::after { + width: 1rem; + text-align: center; + float: right; + vertical-align: 0; + border: 0; + font-weight: 900; + content: '\f107'; + font-family: 'Font Awesome 5 Free'; + } + + .sidebar .nav-item .nav-link[data-toggle="collapse"].collapsed::after { + content: '\f105'; + } + + .sidebar .sidebar-brand .sidebar-brand-icon i { + font-size: 2rem; + } + + .sidebar .sidebar-brand .sidebar-brand-text { + display: inline; + } + + .sidebar .sidebar-heading { + text-align: left; + } +} + +.sidebar-light .sidebar-brand { + color: #6e707e; +} + +.sidebar-light hr.sidebar-divider { + border-top: 1px solid #eaecf4; +} + +.sidebar-light .sidebar-heading { + color: #b7b9cc; +} + +.sidebar-light .nav-item .nav-link { + color: #858796; +} + +.sidebar-light .nav-item .nav-link i { + color: #d1d3e2; +} + +.sidebar-light .nav-item .nav-link:active, .sidebar-light .nav-item .nav-link:focus, .sidebar-light .nav-item .nav-link:hover { + color: #6e707e; +} + +.sidebar-light .nav-item .nav-link:active i, .sidebar-light .nav-item .nav-link:focus i, .sidebar-light .nav-item .nav-link:hover i { + color: #6e707e; +} + +.sidebar-light .nav-item .nav-link[data-toggle="collapse"]::after { + color: #b7b9cc; +} + +.sidebar-light .nav-item.active .nav-link { + color: #6e707e; +} + +.sidebar-light .nav-item.active .nav-link i { + color: #6e707e; +} + +.sidebar-light #sidebarToggle { + background-color: #eaecf4; +} + +.sidebar-light #sidebarToggle::after { + color: #b7b9cc; +} + +.sidebar-light #sidebarToggle:hover { + background-color: #dddfeb; +} + +.sidebar-dark .sidebar-brand { + color: #fff; +} + +.sidebar-dark hr.sidebar-divider { + border-top: 1px solid rgba(255, 255, 255, 0.15); +} + +.sidebar-dark .sidebar-heading { + color: rgba(255, 255, 255, 0.4); +} + +.sidebar-dark .nav-item .nav-link { + color: rgba(255, 255, 255, 0.8); +} + +.sidebar-dark .nav-item .nav-link i { + color: rgba(255, 255, 255, 0.3); +} + +.sidebar-dark .nav-item .nav-link:active, .sidebar-dark .nav-item .nav-link:focus, .sidebar-dark .nav-item .nav-link:hover { + color: #fff; +} + +.sidebar-dark .nav-item .nav-link:active i, .sidebar-dark .nav-item .nav-link:focus i, .sidebar-dark .nav-item .nav-link:hover i { + color: #fff; +} + +.sidebar-dark .nav-item .nav-link[data-toggle="collapse"]::after { + color: rgba(255, 255, 255, 0.5); +} + +.sidebar-dark .nav-item.active .nav-link { + color: #fff; +} + +.sidebar-dark .nav-item.active .nav-link i { + color: #fff; +} + +.btn-circle { + border-radius: 100%; + height: 2.5rem; + width: 2.5rem; + font-size: 1rem; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.btn-circle.btn-sm, .btn-group-sm > .btn-circle.btn { + height: 1.8rem; + width: 1.8rem; + font-size: 0.75rem; +} + +.btn-circle.btn-lg, .btn-group-lg > .btn-circle.btn { + height: 3.5rem; + width: 3.5rem; + font-size: 1.35rem; +} + +.btn-icon-split { + padding: 0; + overflow: hidden; + display: -webkit-inline-box; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.btn-icon-split .icon { + background: rgba(0, 0, 0, 0.15); + display: inline-block; + padding: 0.375rem 0.75rem; +} + +.btn-icon-split .text { + display: inline-block; + padding: 0.375rem 0.75rem; +} + +.btn-icon-split.btn-sm .icon, .btn-group-sm > .btn-icon-split.btn .icon { + padding: 0.25rem 0.5rem; +} + +.btn-icon-split.btn-sm .text, .btn-group-sm > .btn-icon-split.btn .text { + padding: 0.25rem 0.5rem; +} + +.btn-icon-split.btn-lg .icon, .btn-group-lg > .btn-icon-split.btn .icon { + padding: 0.5rem 1rem; +} + +.btn-icon-split.btn-lg .text, .btn-group-lg > .btn-icon-split.btn .text { + padding: 0.5rem 1rem; +} + +.card .card-header .dropdown { + line-height: 1; +} + +.card .card-header .dropdown .dropdown-menu { + line-height: 1.5; +} + +.card .card-header[data-toggle="collapse"] { + text-decoration: none; + position: relative; + padding: 0.75rem 3.25rem 0.75rem 1.25rem; +} + +.card .card-header[data-toggle="collapse"]::after { + position: absolute; + right: 0; + top: 0; + padding-right: 1.725rem; + line-height: 51px; + font-weight: 900; + content: '\f107'; + font-family: 'Font Awesome 5 Free'; + color: #d1d3e2; +} + +.card .card-header[data-toggle="collapse"].collapsed { + border-radius: 0.35rem; +} + +.card .card-header[data-toggle="collapse"].collapsed::after { + content: '\f105'; +} + +.chart-area { + position: relative; + height: 22rem; + width: 100%; + padding: 0.5rem; + text-align: center; +} +.chart-bar { + position: relative; + height: 20rem; + width: 100%; +} + +.chart-pie { + position: relative; + height: calc(20rem - 43px); + width: 100%; +} + +.bg-login-image { + background: url("https://source.unsplash.com/K4mSJ7kc0As/600x800"); + background-position: center; + background-size: cover; +} + +.bg-register-image { + background: url("https://source.unsplash.com/Mv9hjnEUHR4/600x800"); + background-position: center; + background-size: cover; +} + +.bg-password-image { + background: url("https://source.unsplash.com/oWTW-jNGl9I/600x800"); + background-position: center; + background-size: cover; +} + +form.user .custom-checkbox.small label { + line-height: 1.5rem; +} + +form.user .form-control-user { + font-size: 0.8rem; + border-radius: 10rem; + padding: 1.5rem 1rem; +} + +form.user .btn-user { + font-size: 0.8rem; + border-radius: 10rem; + padding: 0.75rem 1rem; +} + +.btn-google { + color: #fff; + background-color: #ea4335; + border-color: #fff; +} + +.btn-google:hover { + color: #fff; + background-color: #e12717; + border-color: #e6e6e6; +} + +.btn-google:focus, .btn-google.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); + box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); +} + +.btn-google.disabled, .btn-google:disabled { + color: #fff; + background-color: #ea4335; + border-color: #fff; +} + +.btn-google:not(:disabled):not(.disabled):active, .btn-google:not(:disabled):not(.disabled).active, +.show > .btn-google.dropdown-toggle { + color: #fff; + background-color: #d62516; + border-color: #dfdfdf; +} + +.btn-google:not(:disabled):not(.disabled):active:focus, .btn-google:not(:disabled):not(.disabled).active:focus, +.show > .btn-google.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); + box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); +} + +.btn-facebook { + color: #fff; + background-color: #3b5998; + border-color: #fff; +} + +.btn-facebook:hover { + color: #fff; + background-color: #30497c; + border-color: #e6e6e6; +} + +.btn-facebook:focus, .btn-facebook.focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); + box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); +} + +.btn-facebook.disabled, .btn-facebook:disabled { + color: #fff; + background-color: #3b5998; + border-color: #fff; +} + +.btn-facebook:not(:disabled):not(.disabled):active, .btn-facebook:not(:disabled):not(.disabled).active, +.show > .btn-facebook.dropdown-toggle { + color: #fff; + background-color: #2d4373; + border-color: #dfdfdf; +} + +.btn-facebook:not(:disabled):not(.disabled):active:focus, .btn-facebook:not(:disabled):not(.disabled).active:focus, +.show > .btn-facebook.dropdown-toggle:focus { + -webkit-box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); + box-shadow: 0 0 0 0.2rem rgba(255, 255, 255, 0.5); +} + +.error { + color: #5a5c69; + font-size: 7rem; + position: relative; + line-height: 1; + width: 12.5rem; +} + +@-webkit-keyframes noise-anim { + 0% { + clip: rect(70px, 9999px, 93px, 0); + } + 5% { + clip: rect(56px, 9999px, 21px, 0); + } + 10% { + clip: rect(77px, 9999px, 78px, 0); + } + 15% { + clip: rect(5px, 9999px, 61px, 0); + } + 20% { + clip: rect(58px, 9999px, 47px, 0); + } + 25% { + clip: rect(53px, 9999px, 53px, 0); + } + 30% { + clip: rect(100px, 9999px, 100px, 0); + } + 35% { + clip: rect(70px, 9999px, 15px, 0); + } + 40% { + clip: rect(79px, 9999px, 39px, 0); + } + 45% { + clip: rect(10px, 9999px, 12px, 0); + } + 50% { + clip: rect(100px, 9999px, 89px, 0); + } + 55% { + clip: rect(14px, 9999px, 18px, 0); + } + 60% { + clip: rect(71px, 9999px, 42px, 0); + } + 65% { + clip: rect(25px, 9999px, 99px, 0); + } + 70% { + clip: rect(39px, 9999px, 38px, 0); + } + 75% { + clip: rect(58px, 9999px, 59px, 0); + } + 80% { + clip: rect(41px, 9999px, 46px, 0); + } + 85% { + clip: rect(90px, 9999px, 30px, 0); + } + 90% { + clip: rect(84px, 9999px, 52px, 0); + } + 95% { + clip: rect(65px, 9999px, 66px, 0); + } + 100% { + clip: rect(20px, 9999px, 20px, 0); + } +} + +@keyframes noise-anim { + 0% { + clip: rect(70px, 9999px, 93px, 0); + } + 5% { + clip: rect(56px, 9999px, 21px, 0); + } + 10% { + clip: rect(77px, 9999px, 78px, 0); + } + 15% { + clip: rect(5px, 9999px, 61px, 0); + } + 20% { + clip: rect(58px, 9999px, 47px, 0); + } + 25% { + clip: rect(53px, 9999px, 53px, 0); + } + 30% { + clip: rect(100px, 9999px, 100px, 0); + } + 35% { + clip: rect(70px, 9999px, 15px, 0); + } + 40% { + clip: rect(79px, 9999px, 39px, 0); + } + 45% { + clip: rect(10px, 9999px, 12px, 0); + } + 50% { + clip: rect(100px, 9999px, 89px, 0); + } + 55% { + clip: rect(14px, 9999px, 18px, 0); + } + 60% { + clip: rect(71px, 9999px, 42px, 0); + } + 65% { + clip: rect(25px, 9999px, 99px, 0); + } + 70% { + clip: rect(39px, 9999px, 38px, 0); + } + 75% { + clip: rect(58px, 9999px, 59px, 0); + } + 80% { + clip: rect(41px, 9999px, 46px, 0); + } + 85% { + clip: rect(90px, 9999px, 30px, 0); + } + 90% { + clip: rect(84px, 9999px, 52px, 0); + } + 95% { + clip: rect(65px, 9999px, 66px, 0); + } + 100% { + clip: rect(20px, 9999px, 20px, 0); + } +} + +.error:after { + content: attr(data-text); + position: absolute; + left: 2px; + text-shadow: -1px 0 #e74a3b; + top: 0; + color: #5a5c69; + background: #f8f9fc; + overflow: hidden; + clip: rect(0, 900px, 0, 0); + animation: noise-anim 2s infinite linear alternate-reverse; +} + +@-webkit-keyframes noise-anim-2 { + 0% { + clip: rect(46px, 9999px, 10px, 0); + } + 5% { + clip: rect(71px, 9999px, 55px, 0); + } + 10% { + clip: rect(68px, 9999px, 74px, 0); + } + 15% { + clip: rect(63px, 9999px, 8px, 0); + } + 20% { + clip: rect(54px, 9999px, 49px, 0); + } + 25% { + clip: rect(11px, 9999px, 72px, 0); + } + 30% { + clip: rect(45px, 9999px, 20px, 0); + } + 35% { + clip: rect(92px, 9999px, 20px, 0); + } + 40% { + clip: rect(9px, 9999px, 13px, 0); + } + 45% { + clip: rect(23px, 9999px, 43px, 0); + } + 50% { + clip: rect(84px, 9999px, 43px, 0); + } + 55% { + clip: rect(68px, 9999px, 11px, 0); + } + 60% { + clip: rect(31px, 9999px, 98px, 0); + } + 65% { + clip: rect(1px, 9999px, 33px, 0); + } + 70% { + clip: rect(30px, 9999px, 74px, 0); + } + 75% { + clip: rect(66px, 9999px, 84px, 0); + } + 80% { + clip: rect(20px, 9999px, 19px, 0); + } + 85% { + clip: rect(91px, 9999px, 17px, 0); + } + 90% { + clip: rect(9px, 9999px, 53px, 0); + } + 95% { + clip: rect(20px, 9999px, 34px, 0); + } + 100% { + clip: rect(5px, 9999px, 99px, 0); + } +} + +@keyframes noise-anim-2 { + 0% { + clip: rect(46px, 9999px, 10px, 0); + } + 5% { + clip: rect(71px, 9999px, 55px, 0); + } + 10% { + clip: rect(68px, 9999px, 74px, 0); + } + 15% { + clip: rect(63px, 9999px, 8px, 0); + } + 20% { + clip: rect(54px, 9999px, 49px, 0); + } + 25% { + clip: rect(11px, 9999px, 72px, 0); + } + 30% { + clip: rect(45px, 9999px, 20px, 0); + } + 35% { + clip: rect(92px, 9999px, 20px, 0); + } + 40% { + clip: rect(9px, 9999px, 13px, 0); + } + 45% { + clip: rect(23px, 9999px, 43px, 0); + } + 50% { + clip: rect(84px, 9999px, 43px, 0); + } + 55% { + clip: rect(68px, 9999px, 11px, 0); + } + 60% { + clip: rect(31px, 9999px, 98px, 0); + } + 65% { + clip: rect(1px, 9999px, 33px, 0); + } + 70% { + clip: rect(30px, 9999px, 74px, 0); + } + 75% { + clip: rect(66px, 9999px, 84px, 0); + } + 80% { + clip: rect(20px, 9999px, 19px, 0); + } + 85% { + clip: rect(91px, 9999px, 17px, 0); + } + 90% { + clip: rect(9px, 9999px, 53px, 0); + } + 95% { + clip: rect(20px, 9999px, 34px, 0); + } + 100% { + clip: rect(5px, 9999px, 99px, 0); + } +} + +.error:before { + content: attr(data-text); + position: absolute; + left: -2px; + text-shadow: 1px 0 #4e73df; + top: 0; + color: #5a5c69; + background: #f8f9fc; + overflow: hidden; + clip: rect(0, 900px, 0, 0); + animation: noise-anim-2 3s infinite linear alternate-reverse; +} + +footer.sticky-footer { + padding: 2rem 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +footer.sticky-footer .copyright { + line-height: 1; + font-size: 0.8rem; +} + +body.sidebar-toggled footer.sticky-footer { + width: 100%; +} diff --git a/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.min.css b/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.min.css new file mode 100644 index 000000000..860e27fa8 --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/css/sb-admin-2.min.css @@ -0,0 +1,10 @@ +/*! + * Start Bootstrap - SB Admin 2 v4.0.0 (https://startbootstrap.com/template-overviews/sb-admin-2) + * Copyright 2013-2019 Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-sb-admin-2/blob/master/LICENSE) + *//*! + * Bootstrap v4.2.1 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#4e73df;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#e74a3b;--orange:#fd7e14;--yellow:#f6c23e;--green:#1cc88a;--teal:#20c9a6;--cyan:#36b9cc;--white:#fff;--gray:#858796;--gray-dark:#5a5c69;--primary:#4e73df;--secondary:#858796;--success:#1cc88a;--info:#36b9cc;--warning:#f6c23e;--danger:#e74a3b;--light:#f8f9fc;--dark:#5a5c69;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:"Nunito",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{-webkit-box-sizing:border-box;box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:Nunito,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#858796;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#4e73df;text-decoration:none;background-color:transparent}a:hover{color:#224abe;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#858796;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:400;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#858796}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dddfeb;border-radius:.35rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#858796}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#3a3b45;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#3a3b45}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:.75rem;padding-left:.75rem;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:.75rem;padding-left:.75rem;margin-right:auto;margin-left:auto}.row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-.75rem;margin-left:-.75rem}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:.75rem;padding-left:.75rem}.col{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-1{margin-left:8.33333%}.offset-2{margin-left:16.66667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333%}.offset-5{margin-left:41.66667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333%}.offset-8{margin-left:66.66667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333%}.offset-11{margin-left:91.66667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-sm-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-sm-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-sm-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-sm-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-sm-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-sm-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-sm-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-sm-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-sm-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-sm-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-sm-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-sm-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-sm-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-sm-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-md-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-md-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-md-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-md-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-md-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-md-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-md-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-md-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-md-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-md-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-md-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-md-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-md-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-md-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-lg-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-lg-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-lg-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-lg-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-lg-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-lg-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-lg-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-lg-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-lg-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-lg-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-lg-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-lg-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-lg-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-lg-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-webkit-box-flex:0;-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{-webkit-box-flex:0;-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{-webkit-box-flex:0;-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-webkit-box-flex:0;-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{-webkit-box-flex:0;-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{-webkit-box-flex:0;-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-webkit-box-flex:0;-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{-webkit-box-flex:0;-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{-webkit-box-flex:0;-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-webkit-box-flex:0;-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{-webkit-box-flex:0;-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{-webkit-box-flex:0;-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.order-xl-last{-webkit-box-ordinal-group:14;-ms-flex-order:13;order:13}.order-xl-0{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.order-xl-1{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.order-xl-2{-webkit-box-ordinal-group:3;-ms-flex-order:2;order:2}.order-xl-3{-webkit-box-ordinal-group:4;-ms-flex-order:3;order:3}.order-xl-4{-webkit-box-ordinal-group:5;-ms-flex-order:4;order:4}.order-xl-5{-webkit-box-ordinal-group:6;-ms-flex-order:5;order:5}.order-xl-6{-webkit-box-ordinal-group:7;-ms-flex-order:6;order:6}.order-xl-7{-webkit-box-ordinal-group:8;-ms-flex-order:7;order:7}.order-xl-8{-webkit-box-ordinal-group:9;-ms-flex-order:8;order:8}.order-xl-9{-webkit-box-ordinal-group:10;-ms-flex-order:9;order:9}.order-xl-10{-webkit-box-ordinal-group:11;-ms-flex-order:10;order:10}.order-xl-11{-webkit-box-ordinal-group:12;-ms-flex-order:11;order:11}.order-xl-12{-webkit-box-ordinal-group:13;-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dddfeb}.table thead th{vertical-align:bottom;border-bottom:2px solid #dddfeb}.table tbody+tbody{border-top:2px solid #dddfeb}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dddfeb}.table-bordered td,.table-bordered th{border:1px solid #dddfeb}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#cdd8f6}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#a3b6ee}.table-hover .table-primary:hover{background-color:#b7c7f2}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#b7c7f2}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#dddde2}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#c0c1c8}.table-hover .table-secondary:hover{background-color:#cfcfd6}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cfcfd6}.table-success,.table-success>td,.table-success>th{background-color:#bff0de}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#89e2c2}.table-hover .table-success:hover{background-color:#aaebd3}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#aaebd3}.table-info,.table-info>td,.table-info>th{background-color:#c7ebf1}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#96dbe4}.table-hover .table-info:hover{background-color:#b3e4ec}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#b3e4ec}.table-warning,.table-warning>td,.table-warning>th{background-color:#fceec9}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#fadf9b}.table-hover .table-warning:hover{background-color:#fbe6b1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#fbe6b1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f8ccc8}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#f3a199}.table-hover .table-danger:hover{background-color:#f5b7b1}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f5b7b1}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfd}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#d1d1d5}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#a9aab1}.table-hover .table-dark:hover{background-color:#c4c4c9}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#c4c4c9}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#3a3b45;border-color:#4b4d5a}.table .thead-light th{color:#6e707e;background-color:#eaecf4;border-color:#dddfeb}.table-dark{color:#fff;background-color:#3a3b45}.table-dark td,.table-dark th,.table-dark thead th{border-color:#4b4d5a}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#6e707e;background-color:#fff;background-clip:padding-box;border:1px solid #d1d3e2;border-radius:.35rem;-webkit-transition:border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.form-control{-webkit-transition:none;transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#6e707e;background-color:#fff;border-color:#bac8f3;outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(78,115,223,.25);box-shadow:0 0 0 .2rem rgba(78,115,223,.25)}.form-control::-webkit-input-placeholder{color:#858796;opacity:1}.form-control:-ms-input-placeholder{color:#858796;opacity:1}.form-control::-ms-input-placeholder{color:#858796;opacity:1}.form-control::placeholder{color:#858796;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#eaecf4;opacity:1}select.form-control:focus::-ms-value{color:#6e707e;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#858796;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#858796}.form-check-label{margin-bottom:0}.form-check-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#1cc88a}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(28,200,138,.9);border-radius:.35rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#1cc88a;padding-right:2.25rem;background-repeat:no-repeat;background-position:center right calc(2.25rem / 4);background-size:calc(2.25rem / 2) calc(2.25rem / 2);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%231cc88a' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e")}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#1cc88a;-webkit-box-shadow:0 0 0 .2rem rgba(28,200,138,.25);box-shadow:0 0 0 .2rem rgba(28,200,138,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:2.25rem;background-position:top calc(2.25rem / 4) right calc(2.25rem / 4)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#1cc88a;padding-right:3.4375rem;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%231cc88a' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") no-repeat center right 1.75rem/1.125rem 1.125rem}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#1cc88a;-webkit-box-shadow:0 0 0 .2rem rgba(28,200,138,.25);box-shadow:0 0 0 .2rem rgba(28,200,138,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#1cc88a}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#1cc88a}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#1cc88a}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34e3a4;background-color:#34e3a4}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{-webkit-box-shadow:0 0 0 .2rem rgba(28,200,138,.25);box-shadow:0 0 0 .2rem rgba(28,200,138,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#1cc88a}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#1cc88a}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#1cc88a;-webkit-box-shadow:0 0 0 .2rem rgba(28,200,138,.25);box-shadow:0 0 0 .2rem rgba(28,200,138,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#e74a3b}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(231,74,59,.9);border-radius:.35rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#e74a3b;padding-right:2.25rem;background-repeat:no-repeat;background-position:center right calc(2.25rem / 4);background-size:calc(2.25rem / 2) calc(2.25rem / 2);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23e74a3b' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E")}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#e74a3b;-webkit-box-shadow:0 0 0 .2rem rgba(231,74,59,.25);box-shadow:0 0 0 .2rem rgba(231,74,59,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:2.25rem;background-position:top calc(2.25rem / 4) right calc(2.25rem / 4)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#e74a3b;padding-right:3.4375rem;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23e74a3b' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23d9534f' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") no-repeat center right 1.75rem/1.125rem 1.125rem}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#e74a3b;-webkit-box-shadow:0 0 0 .2rem rgba(231,74,59,.25);box-shadow:0 0 0 .2rem rgba(231,74,59,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#e74a3b}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#e74a3b}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#e74a3b}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#ed7468;background-color:#ed7468}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{-webkit-box-shadow:0 0 0 .2rem rgba(231,74,59,.25);box-shadow:0 0 0 .2rem rgba(231,74,59,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#e74a3b}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#e74a3b}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#e74a3b;-webkit-box-shadow:0 0 0 .2rem rgba(231,74,59,.25);box-shadow:0 0 0 .2rem rgba(231,74,59,.25)}.form-inline{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#858796;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.35rem;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.btn{-webkit-transition:none;transition:none}}.btn:hover{color:#858796;text-decoration:none}.btn.focus,.btn:focus{outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(78,115,223,.25);box-shadow:0 0 0 .2rem rgba(78,115,223,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#4e73df;border-color:#4e73df}.btn-primary:hover{color:#fff;background-color:#2e59d9;border-color:#2653d4}.btn-primary.focus,.btn-primary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(105,136,228,.5);box-shadow:0 0 0 .2rem rgba(105,136,228,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#4e73df;border-color:#4e73df}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#2653d4;border-color:#244ec9}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(105,136,228,.5);box-shadow:0 0 0 .2rem rgba(105,136,228,.5)}.btn-secondary{color:#fff;background-color:#858796;border-color:#858796}.btn-secondary:hover{color:#fff;background-color:#717384;border-color:#6b6d7d}.btn-secondary.focus,.btn-secondary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(151,153,166,.5);box-shadow:0 0 0 .2rem rgba(151,153,166,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#858796;border-color:#858796}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#6b6d7d;border-color:#656776}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(151,153,166,.5);box-shadow:0 0 0 .2rem rgba(151,153,166,.5)}.btn-success{color:#fff;background-color:#1cc88a;border-color:#1cc88a}.btn-success:hover{color:#fff;background-color:#17a673;border-color:#169b6b}.btn-success.focus,.btn-success:focus{-webkit-box-shadow:0 0 0 .2rem rgba(62,208,156,.5);box-shadow:0 0 0 .2rem rgba(62,208,156,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#1cc88a;border-color:#1cc88a}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#169b6b;border-color:#149063}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(62,208,156,.5);box-shadow:0 0 0 .2rem rgba(62,208,156,.5)}.btn-info{color:#fff;background-color:#36b9cc;border-color:#36b9cc}.btn-info:hover{color:#fff;background-color:#2c9faf;border-color:#2a96a5}.btn-info.focus,.btn-info:focus{-webkit-box-shadow:0 0 0 .2rem rgba(84,196,212,.5);box-shadow:0 0 0 .2rem rgba(84,196,212,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#36b9cc;border-color:#36b9cc}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#2a96a5;border-color:#278c9b}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(84,196,212,.5);box-shadow:0 0 0 .2rem rgba(84,196,212,.5)}.btn-warning{color:#fff;background-color:#f6c23e;border-color:#f6c23e}.btn-warning:hover{color:#fff;background-color:#f4b619;border-color:#f4b30d}.btn-warning.focus,.btn-warning:focus{-webkit-box-shadow:0 0 0 .2rem rgba(247,203,91,.5);box-shadow:0 0 0 .2rem rgba(247,203,91,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#fff;background-color:#f6c23e;border-color:#f6c23e}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#fff;background-color:#f4b30d;border-color:#e9aa0b}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(247,203,91,.5);box-shadow:0 0 0 .2rem rgba(247,203,91,.5)}.btn-danger{color:#fff;background-color:#e74a3b;border-color:#e74a3b}.btn-danger:hover{color:#fff;background-color:#e02d1b;border-color:#d52a1a}.btn-danger.focus,.btn-danger:focus{-webkit-box-shadow:0 0 0 .2rem rgba(235,101,88,.5);box-shadow:0 0 0 .2rem rgba(235,101,88,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#e74a3b;border-color:#e74a3b}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#d52a1a;border-color:#ca2819}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(235,101,88,.5);box-shadow:0 0 0 .2rem rgba(235,101,88,.5)}.btn-light{color:#3a3b45;background-color:#f8f9fc;border-color:#f8f9fc}.btn-light:hover{color:#3a3b45;background-color:#dde2f1;border-color:#d4daed}.btn-light.focus,.btn-light:focus{-webkit-box-shadow:0 0 0 .2rem rgba(220,221,225,.5);box-shadow:0 0 0 .2rem rgba(220,221,225,.5)}.btn-light.disabled,.btn-light:disabled{color:#3a3b45;background-color:#f8f9fc;border-color:#f8f9fc}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#3a3b45;background-color:#d4daed;border-color:#cbd3e9}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(220,221,225,.5);box-shadow:0 0 0 .2rem rgba(220,221,225,.5)}.btn-dark{color:#fff;background-color:#5a5c69;border-color:#5a5c69}.btn-dark:hover{color:#fff;background-color:#484a54;border-color:#42444e}.btn-dark.focus,.btn-dark:focus{-webkit-box-shadow:0 0 0 .2rem rgba(115,116,128,.5);box-shadow:0 0 0 .2rem rgba(115,116,128,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#5a5c69;border-color:#5a5c69}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#42444e;border-color:#3d3e47}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(115,116,128,.5);box-shadow:0 0 0 .2rem rgba(115,116,128,.5)}.btn-outline-primary{color:#4e73df;border-color:#4e73df}.btn-outline-primary:hover{color:#fff;background-color:#4e73df;border-color:#4e73df}.btn-outline-primary.focus,.btn-outline-primary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(78,115,223,.5);box-shadow:0 0 0 .2rem rgba(78,115,223,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#4e73df;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#4e73df;border-color:#4e73df}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(78,115,223,.5);box-shadow:0 0 0 .2rem rgba(78,115,223,.5)}.btn-outline-secondary{color:#858796;border-color:#858796}.btn-outline-secondary:hover{color:#fff;background-color:#858796;border-color:#858796}.btn-outline-secondary.focus,.btn-outline-secondary:focus{-webkit-box-shadow:0 0 0 .2rem rgba(133,135,150,.5);box-shadow:0 0 0 .2rem rgba(133,135,150,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#858796;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#858796;border-color:#858796}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(133,135,150,.5);box-shadow:0 0 0 .2rem rgba(133,135,150,.5)}.btn-outline-success{color:#1cc88a;border-color:#1cc88a}.btn-outline-success:hover{color:#fff;background-color:#1cc88a;border-color:#1cc88a}.btn-outline-success.focus,.btn-outline-success:focus{-webkit-box-shadow:0 0 0 .2rem rgba(28,200,138,.5);box-shadow:0 0 0 .2rem rgba(28,200,138,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#1cc88a;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#1cc88a;border-color:#1cc88a}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(28,200,138,.5);box-shadow:0 0 0 .2rem rgba(28,200,138,.5)}.btn-outline-info{color:#36b9cc;border-color:#36b9cc}.btn-outline-info:hover{color:#fff;background-color:#36b9cc;border-color:#36b9cc}.btn-outline-info.focus,.btn-outline-info:focus{-webkit-box-shadow:0 0 0 .2rem rgba(54,185,204,.5);box-shadow:0 0 0 .2rem rgba(54,185,204,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#36b9cc;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#36b9cc;border-color:#36b9cc}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(54,185,204,.5);box-shadow:0 0 0 .2rem rgba(54,185,204,.5)}.btn-outline-warning{color:#f6c23e;border-color:#f6c23e}.btn-outline-warning:hover{color:#fff;background-color:#f6c23e;border-color:#f6c23e}.btn-outline-warning.focus,.btn-outline-warning:focus{-webkit-box-shadow:0 0 0 .2rem rgba(246,194,62,.5);box-shadow:0 0 0 .2rem rgba(246,194,62,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#f6c23e;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#fff;background-color:#f6c23e;border-color:#f6c23e}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(246,194,62,.5);box-shadow:0 0 0 .2rem rgba(246,194,62,.5)}.btn-outline-danger{color:#e74a3b;border-color:#e74a3b}.btn-outline-danger:hover{color:#fff;background-color:#e74a3b;border-color:#e74a3b}.btn-outline-danger.focus,.btn-outline-danger:focus{-webkit-box-shadow:0 0 0 .2rem rgba(231,74,59,.5);box-shadow:0 0 0 .2rem rgba(231,74,59,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#e74a3b;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#e74a3b;border-color:#e74a3b}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(231,74,59,.5);box-shadow:0 0 0 .2rem rgba(231,74,59,.5)}.btn-outline-light{color:#f8f9fc;border-color:#f8f9fc}.btn-outline-light:hover{color:#3a3b45;background-color:#f8f9fc;border-color:#f8f9fc}.btn-outline-light.focus,.btn-outline-light:focus{-webkit-box-shadow:0 0 0 .2rem rgba(248,249,252,.5);box-shadow:0 0 0 .2rem rgba(248,249,252,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fc;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#3a3b45;background-color:#f8f9fc;border-color:#f8f9fc}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(248,249,252,.5);box-shadow:0 0 0 .2rem rgba(248,249,252,.5)}.btn-outline-dark{color:#5a5c69;border-color:#5a5c69}.btn-outline-dark:hover{color:#fff;background-color:#5a5c69;border-color:#5a5c69}.btn-outline-dark.focus,.btn-outline-dark:focus{-webkit-box-shadow:0 0 0 .2rem rgba(90,92,105,.5);box-shadow:0 0 0 .2rem rgba(90,92,105,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#5a5c69;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#5a5c69;border-color:#5a5c69}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(90,92,105,.5);box-shadow:0 0 0 .2rem rgba(90,92,105,.5)}.btn-link{font-weight:400;color:#4e73df}.btn-link:hover{color:#224abe;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;-webkit-box-shadow:none;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#858796;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{-webkit-transition:opacity .15s linear;transition:opacity .15s linear}@media screen and (prefers-reduced-motion:reduce){.fade{-webkit-transition:none;transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .15s ease;transition:height .15s ease}@media screen and (prefers-reduced-motion:reduce){.collapsing{-webkit-transition:none;transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#858796;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #e3e6f0;border-radius:.35rem}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-right{right:0;left:auto}}.dropdown-menu-left{right:auto;left:0}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #eaecf4}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#3a3b45;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:first-child{border-top-left-radius:calc(.35rem - 1px);border-top-right-radius:calc(.35rem - 1px)}.dropdown-item:last-child{border-bottom-right-radius:calc(.35rem - 1px);border-bottom-left-radius:calc(.35rem - 1px)}.dropdown-item:focus,.dropdown-item:hover{color:#2e2f37;text-decoration:none;background-color:#f8f9fc}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#4e73df}.dropdown-item.disabled,.dropdown-item:disabled{color:#858796;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#858796;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#3a3b45}.btn-group,.btn-group-vertical{position:relative;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-webkit-box;display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#6e707e;text-align:center;white-space:nowrap;background-color:#eaecf4;border:1px solid #d1d3e2;border-radius:.35rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(2.875rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.8125rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#4e73df;background-color:#4e73df}.custom-control-input:focus~.custom-control-label::before{-webkit-box-shadow:0 0 0 .2rem rgba(78,115,223,.25);box-shadow:0 0 0 .2rem rgba(78,115,223,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#bac8f3}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#e5ebfa;border-color:#e5ebfa}.custom-control-input:disabled~.custom-control-label{color:#858796}.custom-control-input:disabled~.custom-control-label::before{background-color:#eaecf4}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#b7b9cc solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.35rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#4e73df;background-color:#4e73df}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(78,115,223,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(78,115,223,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(78,115,223,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#b7b9cc;border-radius:.5rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-transform .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-transform .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{-webkit-transition:none;transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(78,115,223,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-weight:400;line-height:1.5;color:#6e707e;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%235a5c69' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #d1d3e2;border-radius:.35rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#bac8f3;outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(186,200,243,.5);box-shadow:0 0 0 .2rem rgba(186,200,243,.5)}.custom-select:focus::-ms-value{color:#6e707e;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#858796;background-color:#eaecf4}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#bac8f3;-webkit-box-shadow:0 0 0 .2rem rgba(78,115,223,.25);box-shadow:0 0 0 .2rem rgba(78,115,223,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#eaecf4}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#6e707e;background-color:#fff;border:1px solid #d1d3e2;border-radius:.35rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:2.25rem;padding:.375rem .75rem;line-height:1.5;color:#6e707e;content:"Browse";background-color:#eaecf4;border-left:inherit;border-radius:0 .35rem .35rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{-webkit-box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(78,115,223,.25);box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(78,115,223,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(78,115,223,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(78,115,223,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#4e73df;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#e5ebfa}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dddfeb;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#4e73df;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-webkit-transition:none;transition:none}}.custom-range::-moz-range-thumb:active{background-color:#e5ebfa}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dddfeb;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#4e73df;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{-webkit-transition:none;transition:none}}.custom-range::-ms-thumb:active{background-color:#e5ebfa}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dddfeb;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dddfeb;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#b7b9cc}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#b7b9cc}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#b7b9cc}.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,-webkit-box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{-webkit-transition:none;transition:none}}.nav{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#858796;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dddfeb}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.35rem;border-top-right-radius:.35rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#eaecf4 #eaecf4 #dddfeb}.nav-tabs .nav-link.disabled{color:#858796;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#6e707e;background-color:#fff;border-color:#dddfeb #dddfeb #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.35rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#4e73df}.nav-fill .nav-item{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.35rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid #e3e6f0;border-radius:.35rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.35rem;border-top-right-radius:.35rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.35rem;border-bottom-left-radius:.35rem}.card-body{-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;color:inherit;background-color:#f8f9fc;border-bottom:1px solid #e3e6f0}.card-header:first-child{border-radius:calc(.35rem - 1px) calc(.35rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:#f8f9fc;border-top:1px solid #e3e6f0}.card-footer:last-child{border-radius:0 0 calc(.35rem - 1px) calc(.35rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.35rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.35rem - 1px);border-top-right-radius:calc(.35rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.35rem - 1px);border-bottom-left-radius:calc(.35rem - 1px)}.card-deck{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:.75rem}@media (min-width:576px){.card-deck{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-.75rem;margin-left:-.75rem}.card-deck .card{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-right:.75rem;margin-bottom:0;margin-left:.75rem}}.card-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-webkit-box-flex:1;-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.35rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.35rem;border-top-right-radius:.35rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.35rem;border-bottom-left-radius:.35rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;column-count:3;-webkit-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card{overflow:hidden}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion .card .card-header{margin-bottom:-1px}.breadcrumb{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#eaecf4;border-radius:.35rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#858796;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#858796}.pagination{display:-webkit-box;display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.35rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#4e73df;background-color:#fff;border:1px solid #dddfeb}.page-link:hover{z-index:2;color:#224abe;text-decoration:none;background-color:#eaecf4;border-color:#dddfeb}.page-link:focus{z-index:2;outline:0;-webkit-box-shadow:0 0 0 .2rem rgba(78,115,223,.25);box-shadow:0 0 0 .2rem rgba(78,115,223,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.35rem;border-bottom-left-radius:.35rem}.page-item:last-child .page-link{border-top-right-radius:.35rem;border-bottom-right-radius:.35rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#4e73df;border-color:#4e73df}.page-item.disabled .page-link{color:#858796;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dddfeb}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.35rem}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#4e73df}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#2653d4}.badge-secondary{color:#fff;background-color:#858796}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#6b6d7d}.badge-success{color:#fff;background-color:#1cc88a}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#169b6b}.badge-info{color:#fff;background-color:#36b9cc}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#2a96a5}.badge-warning{color:#fff;background-color:#f6c23e}a.badge-warning:focus,a.badge-warning:hover{color:#fff;background-color:#f4b30d}.badge-danger{color:#fff;background-color:#e74a3b}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#d52a1a}.badge-light{color:#3a3b45;background-color:#f8f9fc}a.badge-light:focus,a.badge-light:hover{color:#3a3b45;background-color:#d4daed}.badge-dark{color:#fff;background-color:#5a5c69}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#42444e}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#eaecf4;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.35rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#293c74;background-color:#dce3f9;border-color:#cdd8f6}.alert-primary hr{border-top-color:#b7c7f2}.alert-primary .alert-link{color:#1c294e}.alert-secondary{color:#45464e;background-color:#e7e7ea;border-color:#dddde2}.alert-secondary hr{border-top-color:#cfcfd6}.alert-secondary .alert-link{color:#2d2e33}.alert-success{color:#0f6848;background-color:#d2f4e8;border-color:#bff0de}.alert-success hr{border-top-color:#aaebd3}.alert-success .alert-link{color:#093b29}.alert-info{color:#1c606a;background-color:#d7f1f5;border-color:#c7ebf1}.alert-info hr{border-top-color:#b3e4ec}.alert-info .alert-link{color:#113b42}.alert-warning{color:#806520;background-color:#fdf3d8;border-color:#fceec9}.alert-warning hr{border-top-color:#fbe6b1}.alert-warning .alert-link{color:#574516}.alert-danger{color:#78261f;background-color:#fadbd8;border-color:#f8ccc8}.alert-danger hr{border-top-color:#f5b7b1}.alert-danger .alert-link{color:#4f1915}.alert-light{color:#818183;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686869}.alert-dark{color:#2f3037;background-color:#dedee1;border-color:#d1d1d5}.alert-dark hr{border-top-color:#c4c4c9}.alert-dark .alert-link{color:#18181c}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-webkit-box;display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#eaecf4;border-radius:.35rem}.progress-bar{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#4e73df;-webkit-transition:width .6s ease;transition:width .6s ease}@media screen and (prefers-reduced-motion:reduce){.progress-bar{-webkit-transition:none;transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.media-body{-webkit-box-flex:1;-ms-flex:1;flex:1}.list-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#6e707e;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#6e707e;text-decoration:none;background-color:#f8f9fc}.list-group-item-action:active{color:#858796;background-color:#eaecf4}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.35rem;border-top-right-radius:.35rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.35rem;border-bottom-left-radius:.35rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#858796;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#4e73df;border-color:#4e73df}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#293c74;background-color:#cdd8f6}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#293c74;background-color:#b7c7f2}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#293c74;border-color:#293c74}.list-group-item-secondary{color:#45464e;background-color:#dddde2}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#45464e;background-color:#cfcfd6}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#45464e;border-color:#45464e}.list-group-item-success{color:#0f6848;background-color:#bff0de}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f6848;background-color:#aaebd3}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f6848;border-color:#0f6848}.list-group-item-info{color:#1c606a;background-color:#c7ebf1}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#1c606a;background-color:#b3e4ec}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#1c606a;border-color:#1c606a}.list-group-item-warning{color:#806520;background-color:#fceec9}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#806520;background-color:#fbe6b1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#806520;border-color:#806520}.list-group-item-danger{color:#78261f;background-color:#f8ccc8}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#78261f;background-color:#f5b7b1}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#78261f;border-color:#78261f}.list-group-item-light{color:#818183;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818183;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818183;border-color:#818183}.list-group-item-dark{color:#2f3037;background-color:#d1d1d5}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#2f3037;background-color:#c4c4c9}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#2f3037;border-color:#2f3037}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);border-radius:.25rem;-webkit-box-shadow:0 .25rem .75rem rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#858796;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{-webkit-transition:none;transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-centered{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (.5rem * 2));content:""}.modal-content{position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #eaecf4;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-webkit-box-flex:1;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #eaecf4;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:Nunito,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.35rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:Nunito,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-top .arrow::before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-top .arrow::after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-right .arrow::before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-right .arrow::after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-bottom .arrow::before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-bottom .arrow::after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-left .arrow::before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-left .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#858796}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transition:-webkit-transform .6s ease-in-out;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.carousel-item{-webkit-transition:none;transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;-webkit-transition-property:opacity;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;-webkit-transition:0s .6s opacity;transition:0s .6s opacity}@media screen and (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{-webkit-transition:none;transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;-webkit-transition:opacity .15s ease;transition:opacity .15s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{-webkit-transition:none;transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{-webkit-box-sizing:content-box;box-sizing:content-box;-webkit-box-flex:0;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;-webkit-transition:opacity .6s ease;transition:opacity .6s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-indicators li{-webkit-transition:none;transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#4e73df!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#2653d4!important}.bg-secondary{background-color:#858796!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#6b6d7d!important}.bg-success{background-color:#1cc88a!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#169b6b!important}.bg-info{background-color:#36b9cc!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#2a96a5!important}.bg-warning{background-color:#f6c23e!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#f4b30d!important}.bg-danger{background-color:#e74a3b!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#d52a1a!important}.bg-light{background-color:#f8f9fc!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#d4daed!important}.bg-dark{background-color:#5a5c69!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#42444e!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #e3e6f0!important}.border-top{border-top:1px solid #e3e6f0!important}.border-right{border-right:1px solid #e3e6f0!important}.border-bottom{border-bottom:1px solid #e3e6f0!important}.border-left{border-left:1px solid #e3e6f0!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#4e73df!important}.border-secondary{border-color:#858796!important}.border-success{border-color:#1cc88a!important}.border-info{border-color:#36b9cc!important}.border-warning{border-color:#f6c23e!important}.border-danger{border-color:#e74a3b!important}.border-light{border-color:#f8f9fc!important}.border-dark{border-color:#5a5c69!important}.border-white{border-color:#fff!important}.rounded{border-radius:.35rem!important}.rounded-top{border-top-left-radius:.35rem!important;border-top-right-radius:.35rem!important}.rounded-right{border-top-right-radius:.35rem!important;border-bottom-right-radius:.35rem!important}.rounded-bottom{border-bottom-right-radius:.35rem!important;border-bottom-left-radius:.35rem!important}.rounded-left{border-top-left-radius:.35rem!important;border-bottom-left-radius:.35rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-webkit-inline-box!important;display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.85714%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-3by4::before{padding-top:133.33333%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-webkit-box-orient:horizontal!important;-webkit-box-direction:normal!important;-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-webkit-box-orient:vertical!important;-webkit-box-direction:normal!important;-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-webkit-box-orient:horizontal!important;-webkit-box-direction:reverse!important;-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-webkit-box-orient:vertical!important;-webkit-box-direction:reverse!important;-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-webkit-box-flex:1!important;-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-webkit-box-flex:0!important;-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-webkit-box-flex:1!important;-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-webkit-box-pack:start!important;-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-webkit-box-pack:end!important;-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-webkit-box-pack:justify!important;-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-webkit-box-align:start!important;-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-webkit-box-align:end!important;-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-webkit-box-align:center!important;-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-webkit-box-align:baseline!important;-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-webkit-box-align:stretch!important;-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{-webkit-box-shadow:0 .125rem .25rem 0 rgba(58,59,69,.2)!important;box-shadow:0 .125rem .25rem 0 rgba(58,59,69,.2)!important}.shadow{-webkit-box-shadow:0 .15rem 1.75rem 0 rgba(58,59,69,.15)!important;box-shadow:0 .15rem 1.75rem 0 rgba(58,59,69,.15)!important}.shadow-lg{-webkit-box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important;box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{-webkit-box-shadow:none!important;box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.dropdown .dropdown-menu .dropdown-header,.sidebar .sidebar-heading,.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#4e73df!important}a.text-primary:focus,a.text-primary:hover{color:#224abe!important}.text-secondary{color:#858796!important}a.text-secondary:focus,a.text-secondary:hover{color:#60616f!important}.text-success{color:#1cc88a!important}a.text-success:focus,a.text-success:hover{color:#13855c!important}.text-info{color:#36b9cc!important}a.text-info:focus,a.text-info:hover{color:#258391!important}.text-warning{color:#f6c23e!important}a.text-warning:focus,a.text-warning:hover{color:#dda20a!important}.text-danger{color:#e74a3b!important}a.text-danger:focus,a.text-danger:hover{color:#be2617!important}.text-light{color:#f8f9fc!important}a.text-light:focus,a.text-light:hover{color:#c2cbe5!important}.text-dark{color:#5a5c69!important}a.text-dark:focus,a.text-dark:hover{color:#373840!important}.text-body{color:#858796!important}.text-muted{color:#858796!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;-webkit-box-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #b7b9cc;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dddfeb!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dddfeb}.table .thead-dark th{color:inherit;border-color:#dddfeb}}html{position:relative;min-height:100%}body{height:100%}a:focus{outline:0}#wrapper{display:-webkit-box;display:-ms-flexbox;display:flex}#wrapper #content-wrapper{background-color:#f8f9fc;width:100%;overflow-x:hidden}#wrapper #content-wrapper #content{-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto}.container,.container-fluid{padding-left:1.5rem;padding-right:1.5rem}.scroll-to-top{position:fixed;right:1rem;bottom:1rem;display:none;width:2.75rem;height:2.75rem;text-align:center;color:#fff;background:rgba(90,92,105,.5);line-height:46px}.scroll-to-top:focus,.scroll-to-top:hover{color:#fff}.scroll-to-top:hover{background:#5a5c69}.scroll-to-top i{font-weight:800}@-webkit-keyframes growIn{0%{-webkit-transform:scale(.9);transform:scale(.9);opacity:0}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes growIn{0%{-webkit-transform:scale(.9);transform:scale(.9);opacity:0}100%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.animated--grow-in,.sidebar .nav-item .collapse{-webkit-animation-name:growIn;animation-name:growIn;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-timing-function:transform cubic-bezier(.18,1.25,.4,1),opacity cubic-bezier(0,1,.4,1);animation-timing-function:transform cubic-bezier(.18,1.25,.4,1),opacity cubic-bezier(0,1,.4,1)}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.animated--fade-in{-webkit-animation-name:fadeIn;animation-name:fadeIn;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-timing-function:opacity cubic-bezier(0,1,.4,1);animation-timing-function:opacity cubic-bezier(0,1,.4,1)}.bg-gradient-primary{background-color:#4e73df;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(10%,#4e73df),to(#224abe));background-image:linear-gradient(180deg,#4e73df 10%,#224abe 100%);background-size:cover}.bg-gradient-success{background-color:#1cc88a;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(10%,#1cc88a),to(#13855c));background-image:linear-gradient(180deg,#1cc88a 10%,#13855c 100%);background-size:cover}.bg-gradient-info{background-color:#36b9cc;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(10%,#36b9cc),to(#258391));background-image:linear-gradient(180deg,#36b9cc 10%,#258391 100%);background-size:cover}.bg-gradient-warning{background-color:#f6c23e;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(10%,#f6c23e),to(#dda20a));background-image:linear-gradient(180deg,#f6c23e 10%,#dda20a 100%);background-size:cover}.bg-gradient-danger{background-color:#e74a3b;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(10%,#e74a3b),to(#be2617));background-image:linear-gradient(180deg,#e74a3b 10%,#be2617 100%);background-size:cover}.bg-gray-100{background-color:#f8f9fc!important}.bg-gray-200{background-color:#eaecf4!important}.bg-gray-300{background-color:#dddfeb!important}.bg-gray-400{background-color:#d1d3e2!important}.bg-gray-500{background-color:#b7b9cc!important}.bg-gray-600{background-color:#858796!important}.bg-gray-700{background-color:#6e707e!important}.bg-gray-800{background-color:#5a5c69!important}.bg-gray-900{background-color:#3a3b45!important}.o-hidden{overflow:hidden!important}.text-xs{font-size:.7rem}.text-lg{font-size:1.2rem}.text-gray-100{color:#f8f9fc!important}.text-gray-200{color:#eaecf4!important}.text-gray-300{color:#dddfeb!important}.text-gray-400{color:#d1d3e2!important}.text-gray-500{color:#b7b9cc!important}.text-gray-600{color:#858796!important}.text-gray-700{color:#6e707e!important}.text-gray-800{color:#5a5c69!important}.text-gray-900{color:#3a3b45!important}.icon-circle{height:2.5rem;width:2.5rem;border-radius:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.border-left-primary{border-left:.25rem solid #4e73df!important}.border-left-success{border-left:.25rem solid #1cc88a!important}.border-left-info{border-left:.25rem solid #36b9cc!important}.border-left-warning{border-left:.25rem solid #f6c23e!important}.border-left-danger{border-left:.25rem solid #e74a3b!important}.border-bottom-primary{border-bottom:.25rem solid #4e73df!important}.border-bottom-success{border-bottom:.25rem solid #1cc88a!important}.border-bottom-info{border-bottom:.25rem solid #36b9cc!important}.border-bottom-warning{border-bottom:.25rem solid #f6c23e!important}.border-bottom-danger{border-bottom:.25rem solid #e74a3b!important}.progress-sm{height:.5rem}.rotate-15{-webkit-transform:rotate(15deg);transform:rotate(15deg)}.rotate-n-15{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}.dropdown .dropdown-menu{font-size:.85rem}.dropdown .dropdown-menu .dropdown-header{font-weight:800;font-size:.65rem;color:#b7b9cc}.dropdown.no-arrow .dropdown-toggle::after{display:none}.sidebar .nav-item.dropdown .dropdown-toggle::after,.topbar .nav-item.dropdown .dropdown-toggle::after{width:1rem;text-align:center;float:right;vertical-align:0;border:0;font-weight:900;content:'\f105';font-family:'Font Awesome 5 Free'}.sidebar .nav-item.dropdown.show .dropdown-toggle::after,.topbar .nav-item.dropdown.show .dropdown-toggle::after{content:'\f107'}.sidebar .nav-item .nav-link,.topbar .nav-item .nav-link{position:relative}.sidebar .nav-item .nav-link .badge-counter,.topbar .nav-item .nav-link .badge-counter{position:absolute;-webkit-transform:scale(.7);transform:scale(.7);-webkit-transform-origin:top right;transform-origin:top right;right:.25rem;margin-top:-.25rem}.sidebar .nav-item .nav-link .img-profile,.topbar .nav-item .nav-link .img-profile{height:2rem;width:2rem}.topbar{height:4.375rem}.topbar #sidebarToggleTop{height:2.5rem;width:2.5rem}.topbar #sidebarToggleTop:hover{background-color:#eaecf4}.topbar #sidebarToggleTop:active{background-color:#dddfeb}.topbar .navbar-search{width:25rem}.topbar .navbar-search input{font-size:.85rem}.topbar .topbar-divider{width:0;border-right:1px solid #e3e6f0;height:calc(4.375rem - 2rem);margin:auto 1rem}.topbar .nav-item .nav-link{height:4.375rem;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding:0 .75rem}.topbar .nav-item .nav-link:focus{outline:0}.topbar .nav-item:focus{outline:0}.topbar .dropdown{position:static}.topbar .dropdown .dropdown-menu{width:calc(100% - 1.5rem);right:.75rem}.topbar .dropdown-list{padding:0;border:none;overflow:hidden}.topbar .dropdown-list .dropdown-header{background-color:#4e73df;border:1px solid #4e73df;padding-top:.75rem;padding-bottom:.75rem;color:#fff}.topbar .dropdown-list .dropdown-item{white-space:normal;padding-top:.5rem;padding-bottom:.5rem;border-left:1px solid #e3e6f0;border-right:1px solid #e3e6f0;border-bottom:1px solid #e3e6f0;line-height:1.3rem}.topbar .dropdown-list .dropdown-item .dropdown-list-image{position:relative;height:2.5rem;width:2.5rem}.topbar .dropdown-list .dropdown-item .dropdown-list-image img{height:2.5rem;width:2.5rem}.topbar .dropdown-list .dropdown-item .dropdown-list-image .status-indicator{background-color:#eaecf4;height:.75rem;width:.75rem;border-radius:100%;position:absolute;bottom:0;right:0;border:.125rem solid #fff}.topbar .dropdown-list .dropdown-item .text-truncate{max-width:10rem}.topbar .dropdown-list .dropdown-item:active{background-color:#eaecf4;color:#3a3b45}@media (min-width:576px){.topbar .dropdown{position:relative}.topbar .dropdown .dropdown-menu{width:auto;right:0}.topbar .dropdown-list{width:20rem!important}.topbar .dropdown-list .dropdown-item .text-truncate{max-width:13.375rem}}.topbar.navbar-light .navbar-nav .nav-item .nav-link{color:#d1d3e2}.topbar.navbar-light .navbar-nav .nav-item .nav-link:hover{color:#b7b9cc}.topbar.navbar-light .navbar-nav .nav-item .nav-link:active{color:#858796}.sidebar{width:6.5rem;min-height:100vh}.sidebar .nav-item{position:relative}.sidebar .nav-item:last-child{margin-bottom:1rem}.sidebar .nav-item .nav-link{text-align:center;padding:.75rem 1rem;width:6.5rem}.sidebar .nav-item .nav-link span{font-size:.65rem;display:block}.sidebar .nav-item.active .nav-link{font-weight:700}.sidebar .nav-item .collapse{position:absolute;left:calc(6.5rem + 1.5rem / 2);z-index:1;top:2px}.sidebar .nav-item .collapse .collapse-inner{border-radius:.35rem;-webkit-box-shadow:0 .15rem 1.75rem 0 rgba(58,59,69,.15);box-shadow:0 .15rem 1.75rem 0 rgba(58,59,69,.15)}.sidebar .nav-item .collapsing{display:none;-webkit-transition:none;transition:none}.sidebar .nav-item .collapse .collapse-inner,.sidebar .nav-item .collapsing .collapse-inner{padding:.5rem 0;min-width:10rem;font-size:.85rem;margin:0 0 1rem 0}.sidebar .nav-item .collapse .collapse-inner .collapse-header,.sidebar .nav-item .collapsing .collapse-inner .collapse-header{margin:0;white-space:nowrap;padding:.5rem 1.5rem;text-transform:uppercase;font-weight:800;font-size:.65rem;color:#b7b9cc}.sidebar .nav-item .collapse .collapse-inner .collapse-item,.sidebar .nav-item .collapsing .collapse-inner .collapse-item{padding:.5rem 1rem;margin:0 .5rem;display:block;color:#3a3b45;text-decoration:none;border-radius:.35rem;white-space:nowrap}.sidebar .nav-item .collapse .collapse-inner .collapse-item:hover,.sidebar .nav-item .collapsing .collapse-inner .collapse-item:hover{background-color:#eaecf4}.sidebar .nav-item .collapse .collapse-inner .collapse-item:active,.sidebar .nav-item .collapsing .collapse-inner .collapse-item:active{background-color:#dddfeb}.sidebar .nav-item .collapse .collapse-inner .collapse-item.active,.sidebar .nav-item .collapsing .collapse-inner .collapse-item.active{color:#4e73df;font-weight:700}.sidebar #sidebarToggle{width:2.5rem;height:2.5rem;text-align:center;margin-bottom:1rem;cursor:pointer}.sidebar #sidebarToggle::after{font-weight:900;content:'\f104';font-family:'Font Awesome 5 Free';margin-right:.1rem}.sidebar #sidebarToggle:hover{text-decoration:none}.sidebar #sidebarToggle:focus{outline:0}.sidebar.toggled{width:0!important;overflow:hidden}.sidebar.toggled #sidebarToggle::after{content:'\f105';font-family:'Font Awesome 5 Free';margin-left:.25rem}.sidebar .sidebar-brand{height:4.375rem;text-decoration:none;font-size:1rem;font-weight:800;padding:1.5rem 1rem;text-align:center;text-transform:uppercase;letter-spacing:.05rem;z-index:1}.sidebar .sidebar-brand .sidebar-brand-icon i{font-size:2rem}.sidebar .sidebar-brand .sidebar-brand-text{display:none}.sidebar hr.sidebar-divider{margin:0 1rem 1rem}.sidebar .sidebar-heading{text-align:center;padding:0 1rem;font-weight:800;font-size:.65rem}@media (min-width:768px){.sidebar{width:14rem!important}.sidebar .nav-item .collapse{position:relative;left:0;z-index:1;top:0;-webkit-animation:none;animation:none}.sidebar .nav-item .collapse .collapse-inner{border-radius:0;-webkit-box-shadow:none;box-shadow:none}.sidebar .nav-item .collapsing{display:block;-webkit-transition:height .15s ease;transition:height .15s ease}.sidebar .nav-item .collapse,.sidebar .nav-item .collapsing{margin:0 1rem}.sidebar .nav-item .nav-link{display:block;width:100%;text-align:left;padding:1rem;width:14rem}.sidebar .nav-item .nav-link i{font-size:.85rem;margin-right:.25rem}.sidebar .nav-item .nav-link span{font-size:.85rem;display:inline}.sidebar .nav-item .nav-link[data-toggle=collapse]::after{width:1rem;text-align:center;float:right;vertical-align:0;border:0;font-weight:900;content:'\f107';font-family:'Font Awesome 5 Free'}.sidebar .nav-item .nav-link[data-toggle=collapse].collapsed::after{content:'\f105'}.sidebar .sidebar-brand .sidebar-brand-icon i{font-size:2rem}.sidebar .sidebar-brand .sidebar-brand-text{display:inline}.sidebar .sidebar-heading{text-align:left}.sidebar.toggled{overflow:visible;width:6.5rem!important}.sidebar.toggled .nav-item .collapse{position:absolute;left:calc(6.5rem + 1.5rem / 2);z-index:1;top:2px;-webkit-animation-name:growIn;animation-name:growIn;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-timing-function:transform cubic-bezier(.18,1.25,.4,1),opacity cubic-bezier(0,1,.4,1);animation-timing-function:transform cubic-bezier(.18,1.25,.4,1),opacity cubic-bezier(0,1,.4,1)}.sidebar.toggled .nav-item .collapse .collapse-inner{-webkit-box-shadow:0 .15rem 1.75rem 0 rgba(58,59,69,.15);box-shadow:0 .15rem 1.75rem 0 rgba(58,59,69,.15);border-radius:.35rem}.sidebar.toggled .nav-item .collapsing{display:none;-webkit-transition:none;transition:none}.sidebar.toggled .nav-item .collapse,.sidebar.toggled .nav-item .collapsing{margin:0}.sidebar.toggled .nav-item:last-child{margin-bottom:1rem}.sidebar.toggled .nav-item .nav-link{text-align:center;padding:.75rem 1rem;width:6.5rem}.sidebar.toggled .nav-item .nav-link span{font-size:.65rem;display:block}.sidebar.toggled .nav-item .nav-link i{margin-right:0}.sidebar.toggled .nav-item .nav-link[data-toggle=collapse]::after{display:none}.sidebar.toggled .sidebar-brand .sidebar-brand-icon i{font-size:2rem}.sidebar.toggled .sidebar-brand .sidebar-brand-text{display:none}.sidebar.toggled .sidebar-heading{text-align:center}}.sidebar-light .sidebar-brand{color:#6e707e}.sidebar-light hr.sidebar-divider{border-top:1px solid #eaecf4}.sidebar-light .sidebar-heading{color:#b7b9cc}.sidebar-light .nav-item .nav-link{color:#858796}.sidebar-light .nav-item .nav-link i{color:#d1d3e2}.sidebar-light .nav-item .nav-link:active,.sidebar-light .nav-item .nav-link:focus,.sidebar-light .nav-item .nav-link:hover{color:#6e707e}.sidebar-light .nav-item .nav-link:active i,.sidebar-light .nav-item .nav-link:focus i,.sidebar-light .nav-item .nav-link:hover i{color:#6e707e}.sidebar-light .nav-item .nav-link[data-toggle=collapse]::after{color:#b7b9cc}.sidebar-light .nav-item.active .nav-link{color:#6e707e}.sidebar-light .nav-item.active .nav-link i{color:#6e707e}.sidebar-light #sidebarToggle{background-color:#eaecf4}.sidebar-light #sidebarToggle::after{color:#b7b9cc}.sidebar-light #sidebarToggle:hover{background-color:#dddfeb}.sidebar-dark .sidebar-brand{color:#fff}.sidebar-dark hr.sidebar-divider{border-top:1px solid rgba(255,255,255,.15)}.sidebar-dark .sidebar-heading{color:rgba(255,255,255,.4)}.sidebar-dark .nav-item .nav-link{color:rgba(255,255,255,.9)}.sidebar-dark .nav-item .nav-link i{color:rgba(255,255,255,.3)}.sidebar-dark .nav-item .nav-link:active,.sidebar-dark .nav-item .nav-link:focus,.sidebar-dark .nav-item .nav-link:hover{color:#fff}.sidebar-dark .nav-item .nav-link:active i,.sidebar-dark .nav-item .nav-link:focus i,.sidebar-dark .nav-item .nav-link:hover i{color:#fff}.sidebar-dark .nav-item .nav-link[data-toggle=collapse]::after{color:rgba(255,255,255,.5)}.sidebar-dark .nav-item.active .nav-link{color:#fff}.sidebar-dark .nav-item.active .nav-link i{color:#fff}.sidebar-dark #sidebarToggle{background-color:rgba(255,255,255,.2)}.sidebar-dark #sidebarToggle::after{color:rgba(255,255,255,.5)}.sidebar-dark #sidebarToggle:hover{background-color:rgba(255,255,255,.25)}.sidebar-dark.toggled #sidebarToggle::after{color:rgba(255,255,255,.5)}.btn-circle{border-radius:100%;height:2.5rem;width:2.5rem;font-size:1rem;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-circle.btn-sm,.btn-group-sm>.btn-circle.btn{height:1.8rem;width:1.8rem;font-size:.75rem}.btn-circle.btn-lg,.btn-group-lg>.btn-circle.btn{height:3.5rem;width:3.5rem;font-size:1.35rem}.btn-icon-split{padding:0;overflow:hidden;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.btn-icon-split .icon{background:rgba(0,0,0,.15);display:inline-block;padding:.375rem .75rem}.btn-icon-split .text{display:inline-block;padding:.375rem .75rem}.btn-group-sm>.btn-icon-split.btn .icon,.btn-icon-split.btn-sm .icon{padding:.25rem .5rem}.btn-group-sm>.btn-icon-split.btn .text,.btn-icon-split.btn-sm .text{padding:.25rem .5rem}.btn-group-lg>.btn-icon-split.btn .icon,.btn-icon-split.btn-lg .icon{padding:.5rem 1rem}.btn-group-lg>.btn-icon-split.btn .text,.btn-icon-split.btn-lg .text{padding:.5rem 1rem}.card .card-header .dropdown{line-height:1}.card .card-header .dropdown .dropdown-menu{line-height:1.5}.card .card-header[data-toggle=collapse]{text-decoration:none;position:relative;padding:.75rem 3.25rem .75rem 1.25rem}.card .card-header[data-toggle=collapse]::after{position:absolute;right:0;top:0;padding-right:1.725rem;line-height:51px;font-weight:900;content:'\f107';font-family:'Font Awesome 5 Free';color:#d1d3e2}.card .card-header[data-toggle=collapse].collapsed{border-radius:.35rem}.card .card-header[data-toggle=collapse].collapsed::after{content:'\f105'}.chart-area{position:relative;height:10rem;width:100%}@media (min-width:768px){.chart-area{height:20rem}}.chart-bar{position:relative;height:10rem;width:100%}@media (min-width:768px){.chart-bar{height:20rem}}.chart-pie{position:relative;height:15rem;width:100%}@media (min-width:768px){.chart-pie{height:calc(20rem - 43px)!important}}.bg-login-image{background:url(https://source.unsplash.com/K4mSJ7kc0As/600x800);background-position:center;background-size:cover}.bg-register-image{background:url(https://source.unsplash.com/Mv9hjnEUHR4/600x800);background-position:center;background-size:cover}.bg-password-image{background:url(https://source.unsplash.com/oWTW-jNGl9I/600x800);background-position:center;background-size:cover}form.user .custom-checkbox.small label{line-height:1.5rem}form.user .form-control-user{font-size:.8rem;border-radius:10rem;padding:1.5rem 1rem}form.user .btn-user{font-size:.8rem;border-radius:10rem;padding:.75rem 1rem}.btn-google{color:#fff;background-color:#ea4335;border-color:#fff}.btn-google:hover{color:#fff;background-color:#e12717;border-color:#e6e6e6}.btn-google.focus,.btn-google:focus{-webkit-box-shadow:0 0 0 .2rem rgba(255,255,255,.5);box-shadow:0 0 0 .2rem rgba(255,255,255,.5)}.btn-google.disabled,.btn-google:disabled{color:#fff;background-color:#ea4335;border-color:#fff}.btn-google:not(:disabled):not(.disabled).active,.btn-google:not(:disabled):not(.disabled):active,.show>.btn-google.dropdown-toggle{color:#fff;background-color:#d62516;border-color:#dfdfdf}.btn-google:not(:disabled):not(.disabled).active:focus,.btn-google:not(:disabled):not(.disabled):active:focus,.show>.btn-google.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(255,255,255,.5);box-shadow:0 0 0 .2rem rgba(255,255,255,.5)}.btn-facebook{color:#fff;background-color:#3b5998;border-color:#fff}.btn-facebook:hover{color:#fff;background-color:#30497c;border-color:#e6e6e6}.btn-facebook.focus,.btn-facebook:focus{-webkit-box-shadow:0 0 0 .2rem rgba(255,255,255,.5);box-shadow:0 0 0 .2rem rgba(255,255,255,.5)}.btn-facebook.disabled,.btn-facebook:disabled{color:#fff;background-color:#3b5998;border-color:#fff}.btn-facebook:not(:disabled):not(.disabled).active,.btn-facebook:not(:disabled):not(.disabled):active,.show>.btn-facebook.dropdown-toggle{color:#fff;background-color:#2d4373;border-color:#dfdfdf}.btn-facebook:not(:disabled):not(.disabled).active:focus,.btn-facebook:not(:disabled):not(.disabled):active:focus,.show>.btn-facebook.dropdown-toggle:focus{-webkit-box-shadow:0 0 0 .2rem rgba(255,255,255,.5);box-shadow:0 0 0 .2rem rgba(255,255,255,.5)}.error{color:#5a5c69;font-size:7rem;position:relative;line-height:1;width:12.5rem}@-webkit-keyframes noise-anim{0%{clip:rect(70px,9999px,93px,0)}5%{clip:rect(56px,9999px,21px,0)}10%{clip:rect(77px,9999px,78px,0)}15%{clip:rect(5px,9999px,61px,0)}20%{clip:rect(58px,9999px,47px,0)}25%{clip:rect(53px,9999px,53px,0)}30%{clip:rect(100px,9999px,100px,0)}35%{clip:rect(70px,9999px,15px,0)}40%{clip:rect(79px,9999px,39px,0)}45%{clip:rect(10px,9999px,12px,0)}50%{clip:rect(100px,9999px,89px,0)}55%{clip:rect(14px,9999px,18px,0)}60%{clip:rect(71px,9999px,42px,0)}65%{clip:rect(25px,9999px,99px,0)}70%{clip:rect(39px,9999px,38px,0)}75%{clip:rect(58px,9999px,59px,0)}80%{clip:rect(41px,9999px,46px,0)}85%{clip:rect(90px,9999px,30px,0)}90%{clip:rect(84px,9999px,52px,0)}95%{clip:rect(65px,9999px,66px,0)}100%{clip:rect(20px,9999px,20px,0)}}@keyframes noise-anim{0%{clip:rect(70px,9999px,93px,0)}5%{clip:rect(56px,9999px,21px,0)}10%{clip:rect(77px,9999px,78px,0)}15%{clip:rect(5px,9999px,61px,0)}20%{clip:rect(58px,9999px,47px,0)}25%{clip:rect(53px,9999px,53px,0)}30%{clip:rect(100px,9999px,100px,0)}35%{clip:rect(70px,9999px,15px,0)}40%{clip:rect(79px,9999px,39px,0)}45%{clip:rect(10px,9999px,12px,0)}50%{clip:rect(100px,9999px,89px,0)}55%{clip:rect(14px,9999px,18px,0)}60%{clip:rect(71px,9999px,42px,0)}65%{clip:rect(25px,9999px,99px,0)}70%{clip:rect(39px,9999px,38px,0)}75%{clip:rect(58px,9999px,59px,0)}80%{clip:rect(41px,9999px,46px,0)}85%{clip:rect(90px,9999px,30px,0)}90%{clip:rect(84px,9999px,52px,0)}95%{clip:rect(65px,9999px,66px,0)}100%{clip:rect(20px,9999px,20px,0)}}.error:after{content:attr(data-text);position:absolute;left:2px;text-shadow:-1px 0 #e74a3b;top:0;color:#5a5c69;background:#f8f9fc;overflow:hidden;clip:rect(0,900px,0,0);animation:noise-anim 2s infinite linear alternate-reverse}@-webkit-keyframes noise-anim-2{0%{clip:rect(46px,9999px,10px,0)}5%{clip:rect(71px,9999px,55px,0)}10%{clip:rect(68px,9999px,74px,0)}15%{clip:rect(63px,9999px,8px,0)}20%{clip:rect(54px,9999px,49px,0)}25%{clip:rect(11px,9999px,72px,0)}30%{clip:rect(45px,9999px,20px,0)}35%{clip:rect(92px,9999px,20px,0)}40%{clip:rect(9px,9999px,13px,0)}45%{clip:rect(23px,9999px,43px,0)}50%{clip:rect(84px,9999px,43px,0)}55%{clip:rect(68px,9999px,11px,0)}60%{clip:rect(31px,9999px,98px,0)}65%{clip:rect(1px,9999px,33px,0)}70%{clip:rect(30px,9999px,74px,0)}75%{clip:rect(66px,9999px,84px,0)}80%{clip:rect(20px,9999px,19px,0)}85%{clip:rect(91px,9999px,17px,0)}90%{clip:rect(9px,9999px,53px,0)}95%{clip:rect(20px,9999px,34px,0)}100%{clip:rect(5px,9999px,99px,0)}}@keyframes noise-anim-2{0%{clip:rect(46px,9999px,10px,0)}5%{clip:rect(71px,9999px,55px,0)}10%{clip:rect(68px,9999px,74px,0)}15%{clip:rect(63px,9999px,8px,0)}20%{clip:rect(54px,9999px,49px,0)}25%{clip:rect(11px,9999px,72px,0)}30%{clip:rect(45px,9999px,20px,0)}35%{clip:rect(92px,9999px,20px,0)}40%{clip:rect(9px,9999px,13px,0)}45%{clip:rect(23px,9999px,43px,0)}50%{clip:rect(84px,9999px,43px,0)}55%{clip:rect(68px,9999px,11px,0)}60%{clip:rect(31px,9999px,98px,0)}65%{clip:rect(1px,9999px,33px,0)}70%{clip:rect(30px,9999px,74px,0)}75%{clip:rect(66px,9999px,84px,0)}80%{clip:rect(20px,9999px,19px,0)}85%{clip:rect(91px,9999px,17px,0)}90%{clip:rect(9px,9999px,53px,0)}95%{clip:rect(20px,9999px,34px,0)}100%{clip:rect(5px,9999px,99px,0)}}.error:before{content:attr(data-text);position:absolute;left:-2px;text-shadow:1px 0 #4e73df;top:0;color:#5a5c69;background:#f8f9fc;overflow:hidden;clip:rect(0,900px,0,0);animation:noise-anim-2 3s infinite linear alternate-reverse}footer.sticky-footer{padding:2rem 0;-ms-flex-negative:0;flex-shrink:0}footer.sticky-footer .copyright{line-height:1;font-size:.8rem}body.sidebar-toggled footer.sticky-footer{width:100%}span.collapsing {display: none;} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/css/style.css b/Plan/common/src/main/resources/assets/plan/web/css/style.css index 6bb4a8702..75f260b42 100644 --- a/Plan/common/src/main/resources/assets/plan/web/css/style.css +++ b/Plan/common/src/main/resources/assets/plan/web/css/style.css @@ -1,8042 +1,1026 @@ +.page-loader { + position: fixed; + background: #ddd; + width: 100%; + z-index: 50; + height: 100%; + text-align: center; +} + +.loader { + display: inline-block; + width: 2rem; + height: 2rem; + position: absolute; + border: 4px solid #368F17; + border-radius: 5px; + top: 50%; + background-color: #368F17; + animation: loader 2s infinite ease; +} + +.loader-text { + position: relative; + top: 55%; + left: 1rem; +} + +@keyframes loader { + 0% { + transform: rotate(0deg); + } + + 25% { + transform: rotate(180deg); + } + + 50% { + transform: rotate(180deg); + } + + 75% { + transform: rotate(360deg); + } + + 100% { + transform: rotate(360deg); + } +} + +div.scrollbar { + overflow: hidden; + overflow-y: auto; + max-height: 60vh; +} + +.clickable { + cursor: pointer; +} + +.color-chooser { + margin-bottom: 0.3rem; +} + /* Navbar ====================================== */ -@import url(materialize.css); -.navbar { - font-family: "Roboto", sans-serif; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3); - -ms-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3); - box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3); - border: none; - position: fixed; - top: 0; - left: 0; - z-index: 12; - width: 100%; } - .navbar .navbar-brand { - white-space: nowrap; - -ms-text-overflow: ellipsis; - -o-text-overflow: ellipsis; - text-overflow: ellipsis; - overflow: hidden; } - .navbar .navbar-custom-right-menu { - float: right; } - .navbar .navbar-toggle { - text-decoration: none; - color: #fff; - width: 20px; - height: 20px; - margin-top: -4px; - margin-right: 17px; } - .navbar .navbar-toggle:before { - content: '\E8D5'; - font-family: 'Material Icons'; - font-size: 26px; } - .navbar .navbar-collapse.in { - overflow: visible; } - -.ls-closed .sidebar { - margin-left: -300px; } - -.ls-closed section.content { - margin-left: 15px; } - -.ls-closed .bars:after, .ls-closed .bars:before { - font-family: 'Material Icons'; - font-size: 24px; - position: absolute; - top: 18px; - left: 20px; - margin-right: 10px; - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); - -moz-transition: all 0.3s; - -o-transition: all 0.3s; - -webkit-transition: all 0.3s; - transition: all 0.3s; } - -.ls-closed .bars:before { - content: '\E5D2'; - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - -webkit-transform: scale(1); - transform: scale(1); } - -.ls-closed .bars:after { - content: '\E5C4'; - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); } - -.ls-closed .navbar-brand { - margin-left: 30px; } - -.overlay-open .bars:before { - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); } - -.overlay-open .bars:after { - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - -webkit-transform: scale(1); - transform: scale(1); } - -.navbar-header { - padding: 10px 7px; } - .navbar-header .bars { - float: left; - text-decoration: none; } - -.navbar-nav > li > a { - padding: 7px 7px 2px 7px; - margin-top: 17px; - margin-left: 5px; } - -.navbar-nav .dropdown-menu { - margin-top: -40px !important; } - -.label-count { - position: absolute; - top: 2px; - right: 6px; - font-size: 10px; - line-height: 15px; - background-color: #000; - padding: 0 4px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - border-radius: 3px; } - .col-red .navbar .navbar-brand, .col-red .navbar .navbar-brand:hover, .col-red .navbar .navbar-brand:active, .col-red .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-red .navbar .nav > li > a:hover, .col-red .navbar .nav > li > a:focus, .col-red .navbar .nav .open > a, .col-red .navbar .nav .open > a:hover, .col-red .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-red .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-red .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-red .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-pink .navbar .navbar-brand, .col-pink .navbar .navbar-brand:hover, .col-pink .navbar .navbar-brand:active, .col-pink .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-pink .navbar .nav > li > a:hover, .col-pink .navbar .nav > li > a:focus, .col-pink .navbar .nav .open > a, .col-pink .navbar .nav .open > a:hover, .col-pink .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-pink .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-pink .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-pink .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-purple .navbar .navbar-brand, .col-purple .navbar .navbar-brand:hover, .col-purple .navbar .navbar-brand:active, .col-purple .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-purple .navbar .nav > li > a:hover, .col-purple .navbar .nav > li > a:focus, .col-purple .navbar .nav .open > a, .col-purple .navbar .nav .open > a:hover, .col-purple .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-purple .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-purple .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-purple .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-deep-purple .navbar .navbar-brand, .col-deep-purple .navbar .navbar-brand:hover, .col-deep-purple .navbar .navbar-brand:active, .col-deep-purple .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-deep-purple .navbar .nav > li > a:hover, .col-deep-purple .navbar .nav > li > a:focus, .col-deep-purple .navbar .nav .open > a, .col-deep-purple .navbar .nav .open > a:hover, .col-deep-purple .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-deep-purple .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-deep-purple .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-deep-purple .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-indigo .navbar .navbar-brand, .col-indigo .navbar .navbar-brand:hover, .col-indigo .navbar .navbar-brand:active, .col-indigo .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-indigo .navbar .nav > li > a:hover, .col-indigo .navbar .nav > li > a:focus, .col-indigo .navbar .nav .open > a, .col-indigo .navbar .nav .open > a:hover, .col-indigo .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-indigo .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-indigo .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-indigo .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-blue .navbar .navbar-brand, .col-blue .navbar .navbar-brand:hover, .col-blue .navbar .navbar-brand:active, .col-blue .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-blue .navbar .nav > li > a:hover, .col-blue .navbar .nav > li > a:focus, .col-blue .navbar .nav .open > a, .col-blue .navbar .nav .open > a:hover, .col-blue .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-blue .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-blue .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-blue .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-light-blue .navbar .navbar-brand, .col-light-blue .navbar .navbar-brand:hover, .col-light-blue .navbar .navbar-brand:active, .col-light-blue .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-light-blue .navbar .nav > li > a:hover, .col-light-blue .navbar .nav > li > a:focus, .col-light-blue .navbar .nav .open > a, .col-light-blue .navbar .nav .open > a:hover, .col-light-blue .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-light-blue .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-light-blue .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-light-blue .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-cyan .navbar .navbar-brand, .col-cyan .navbar .navbar-brand:hover, .col-cyan .navbar .navbar-brand:active, .col-cyan .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-cyan .navbar .nav > li > a:hover, .col-cyan .navbar .nav > li > a:focus, .col-cyan .navbar .nav .open > a, .col-cyan .navbar .nav .open > a:hover, .col-cyan .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-cyan .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-cyan .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-cyan .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-teal .navbar .navbar-brand, .col-teal .navbar .navbar-brand:hover, .col-teal .navbar .navbar-brand:active, .col-teal .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-teal .navbar .nav > li > a:hover, .col-teal .navbar .nav > li > a:focus, .col-teal .navbar .nav .open > a, .col-teal .navbar .nav .open > a:hover, .col-teal .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-teal .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-teal .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-teal .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-green .navbar .navbar-brand, .col-green .navbar .navbar-brand:hover, .col-green .navbar .navbar-brand:active, .col-green .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-green .navbar .nav > li > a:hover, .col-green .navbar .nav > li > a:focus, .col-green .navbar .nav .open > a, .col-green .navbar .nav .open > a:hover, .col-green .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-green .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-green .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-green .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-light-green .navbar .navbar-brand, .col-light-green .navbar .navbar-brand:hover, .col-light-green .navbar .navbar-brand:active, .col-light-green .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-light-green .navbar .nav > li > a:hover, .col-light-green .navbar .nav > li > a:focus, .col-light-green .navbar .nav .open > a, .col-light-green .navbar .nav .open > a:hover, .col-light-green .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-light-green .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-light-green .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-light-green .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-lime .navbar .navbar-brand, .col-lime .navbar .navbar-brand:hover, .col-lime .navbar .navbar-brand:active, .col-lime .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-lime .navbar .nav > li > a:hover, .col-lime .navbar .nav > li > a:focus, .col-lime .navbar .nav .open > a, .col-lime .navbar .nav .open > a:hover, .col-lime .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-lime .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-lime .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-lime .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-yellow .navbar .navbar-brand, .col-yellow .navbar .navbar-brand:hover, .col-yellow .navbar .navbar-brand:active, .col-yellow .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-yellow .navbar .nav > li > a:hover, .col-yellow .navbar .nav > li > a:focus, .col-yellow .navbar .nav .open > a, .col-yellow .navbar .nav .open > a:hover, .col-yellow .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-yellow .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-yellow .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-yellow .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-amber .navbar .navbar-brand, .col-amber .navbar .navbar-brand:hover, .col-amber .navbar .navbar-brand:active, .col-amber .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-amber .navbar .nav > li > a:hover, .col-amber .navbar .nav > li > a:focus, .col-amber .navbar .nav .open > a, .col-amber .navbar .nav .open > a:hover, .col-amber .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-amber .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-amber .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-amber .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-orange .navbar .navbar-brand, .col-orange .navbar .navbar-brand:hover, .col-orange .navbar .navbar-brand:active, .col-orange .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-orange .navbar .nav > li > a:hover, .col-orange .navbar .nav > li > a:focus, .col-orange .navbar .nav .open > a, .col-orange .navbar .nav .open > a:hover, .col-orange .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-orange .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-orange .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-orange .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-deep-orange .navbar .navbar-brand, .col-deep-orange .navbar .navbar-brand:hover, .col-deep-orange .navbar .navbar-brand:active, .col-deep-orange .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-deep-orange .navbar .nav > li > a:hover, .col-deep-orange .navbar .nav > li > a:focus, .col-deep-orange .navbar .nav .open > a, .col-deep-orange .navbar .nav .open > a:hover, .col-deep-orange .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-deep-orange .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-deep-orange .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-deep-orange .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-brown .navbar .navbar-brand, .col-brown .navbar .navbar-brand:hover, .col-brown .navbar .navbar-brand:active, .col-brown .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-brown .navbar .nav > li > a:hover, .col-brown .navbar .nav > li > a:focus, .col-brown .navbar .nav .open > a, .col-brown .navbar .nav .open > a:hover, .col-brown .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-brown .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-brown .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-brown .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-grey .navbar .navbar-brand, .col-grey .navbar .navbar-brand:hover, .col-grey .navbar .navbar-brand:active, .col-grey .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-grey .navbar .nav > li > a:hover, .col-grey .navbar .nav > li > a:focus, .col-grey .navbar .nav .open > a, .col-grey .navbar .nav .open > a:hover, .col-grey .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-grey .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-grey .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-grey .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-blue-grey .navbar .navbar-brand, .col-blue-grey .navbar .navbar-brand:hover, .col-blue-grey .navbar .navbar-brand:active, .col-blue-grey .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-blue-grey .navbar .nav > li > a:hover, .col-blue-grey .navbar .nav > li > a:focus, .col-blue-grey .navbar .nav .open > a, .col-blue-grey .navbar .nav .open > a:hover, .col-blue-grey .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-blue-grey .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-blue-grey .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-blue-grey .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-black .navbar .navbar-brand, .col-black .navbar .navbar-brand:hover, .col-black .navbar .navbar-brand:active, .col-black .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-black .navbar .nav > li > a:hover, .col-black .navbar .nav > li > a:focus, .col-black .navbar .nav .open > a, .col-black .navbar .nav .open > a:hover, .col-black .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-black .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-black .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-black .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } + background-color: rgba(0, 0, 0, 0.08); +} .col-white .navbar .navbar-brand, .col-white .navbar .navbar-brand:hover, .col-white .navbar .navbar-brand:active, .col-white .navbar .navbar-brand:focus { - color: #fff; } + color: #fff; +} .col-white .navbar .nav > li > a:hover, .col-white .navbar .nav > li > a:focus, .col-white .navbar .nav .open > a, .col-white .navbar .nav .open > a:hover, .col-white .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } + background-color: rgba(0, 0, 0, 0.05); +} .col-white .navbar .nav > li > a { - color: #fff; } + color: #fff; +} .col-white .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } + float: left; + padding: 10px 20px; + font-size: 22px; + color: #fff; + margin-right: 10px; + margin-left: -10px; + margin-top: 4px; +} .col-white .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -/* Material Icons ============================== */ -.material-icons.md-18 { - font-size: 18px; } - -.material-icons.md-24 { - font-size: 24px; } - -.material-icons.md-26 { - font-size: 26px; } - -.material-icons.md-28 { - font-size: 28px; } - -.material-icons.md-30 { - font-size: 30px; } - -.material-icons.md-32 { - font-size: 32px; } - -.material-icons.md-36 { - font-size: 36px; } - -.material-icons.md-48 { - font-size: 48px; } - -/* Helpers ===================================== */ -.m-l--125 { - margin-left: -125px; } - -.m-t--125 { - margin-top: -125px; } - -.m-r--125 { - margin-right: -125px; } - -.m-b--125 { - margin-bottom: -125px; } - -.m-l--120 { - margin-left: -120px; } - -.m-t--120 { - margin-top: -120px; } - -.m-r--120 { - margin-right: -120px; } - -.m-b--120 { - margin-bottom: -120px; } - -.m-l--115 { - margin-left: -115px; } - -.m-t--115 { - margin-top: -115px; } - -.m-r--115 { - margin-right: -115px; } - -.m-b--115 { - margin-bottom: -115px; } - -.m-l--110 { - margin-left: -110px; } - -.m-t--110 { - margin-top: -110px; } - -.m-r--110 { - margin-right: -110px; } - -.m-b--110 { - margin-bottom: -110px; } - -.m-l--105 { - margin-left: -105px; } - -.m-t--105 { - margin-top: -105px; } - -.m-r--105 { - margin-right: -105px; } - -.m-b--105 { - margin-bottom: -105px; } - -.m-l--100 { - margin-left: -100px; } - -.m-t--100 { - margin-top: -100px; } - -.m-r--100 { - margin-right: -100px; } - -.m-b--100 { - margin-bottom: -100px; } - -.m-l--95 { - margin-left: -95px; } - -.m-t--95 { - margin-top: -95px; } - -.m-r--95 { - margin-right: -95px; } - -.m-b--95 { - margin-bottom: -95px; } - -.m-l--90 { - margin-left: -90px; } - -.m-t--90 { - margin-top: -90px; } - -.m-r--90 { - margin-right: -90px; } - -.m-b--90 { - margin-bottom: -90px; } - -.m-l--85 { - margin-left: -85px; } - -.m-t--85 { - margin-top: -85px; } - -.m-r--85 { - margin-right: -85px; } - -.m-b--85 { - margin-bottom: -85px; } - -.m-l--80 { - margin-left: -80px; } - -.m-t--80 { - margin-top: -80px; } - -.m-r--80 { - margin-right: -80px; } - -.m-b--80 { - margin-bottom: -80px; } - -.m-l--75 { - margin-left: -75px; } - -.m-t--75 { - margin-top: -75px; } - -.m-r--75 { - margin-right: -75px; } - -.m-b--75 { - margin-bottom: -75px; } - -.m-l--70 { - margin-left: -70px; } - -.m-t--70 { - margin-top: -70px; } - -.m-r--70 { - margin-right: -70px; } - -.m-b--70 { - margin-bottom: -70px; } - -.m-l--65 { - margin-left: -65px; } - -.m-t--65 { - margin-top: -65px; } - -.m-r--65 { - margin-right: -65px; } - -.m-b--65 { - margin-bottom: -65px; } - -.m-l--60 { - margin-left: -60px; } - -.m-t--60 { - margin-top: -60px; } - -.m-r--60 { - margin-right: -60px; } - -.m-b--60 { - margin-bottom: -60px; } - -.m-l--55 { - margin-left: -55px; } - -.m-t--55 { - margin-top: -55px; } - -.m-r--55 { - margin-right: -55px; } - -.m-b--55 { - margin-bottom: -55px; } - -.m-l--50 { - margin-left: -50px; } - -.m-t--50 { - margin-top: -50px; } - -.m-r--50 { - margin-right: -50px; } - -.m-b--50 { - margin-bottom: -50px; } - -.m-l--45 { - margin-left: -45px; } - -.m-t--45 { - margin-top: -45px; } - -.m-r--45 { - margin-right: -45px; } - -.m-b--45 { - margin-bottom: -45px; } - -.m-l--40 { - margin-left: -40px; } - -.m-t--40 { - margin-top: -40px; } - -.m-r--40 { - margin-right: -40px; } - -.m-b--40 { - margin-bottom: -40px; } - -.m-l--35 { - margin-left: -35px; } - -.m-t--35 { - margin-top: -35px; } - -.m-r--35 { - margin-right: -35px; } - -.m-b--35 { - margin-bottom: -35px; } - -.m-l--30 { - margin-left: -30px; } - -.m-t--30 { - margin-top: -30px; } - -.m-r--30 { - margin-right: -30px; } - -.m-b--30 { - margin-bottom: -30px; } - -.m-l--25 { - margin-left: -25px; } - -.m-t--25 { - margin-top: -25px; } - -.m-r--25 { - margin-right: -25px; } - -.m-b--25 { - margin-bottom: -25px; } - -.m-l--20 { - margin-left: -20px; } - -.m-t--20 { - margin-top: -20px; } - -.m-r--20 { - margin-right: -20px; } - -.m-b--20 { - margin-bottom: -20px; } - -.m-l--15 { - margin-left: -15px; } - -.m-t--15 { - margin-top: -15px; } - -.m-r--15 { - margin-right: -15px; } - -.m-b--15 { - margin-bottom: -15px; } - -.m-l--10 { - margin-left: -10px; } - -.m-t--10 { - margin-top: -10px; } - -.m-r--10 { - margin-right: -10px; } - -.m-b--10 { - margin-bottom: -10px; } - -.m-l--5 { - margin-left: -5px; } - -.m-t--5 { - margin-top: -5px; } - -.m-r--5 { - margin-right: -5px; } - -.m-b--5 { - margin-bottom: -5px; } - -.m-l-0 { - margin-left: 0; } - -.m-t-0 { - margin-top: 0; } - -.m-r-0 { - margin-right: 0; } - -.m-b-0 { - margin-bottom: 0; } - -.m-l-5 { - margin-left: 5px; } - -.m-t-5 { - margin-top: 5px; } - -.m-r-5 { - margin-right: 5px; } - -.m-b-5 { - margin-bottom: 5px; } - -.m-l-10 { - margin-left: 10px; } - -.m-t-10 { - margin-top: 10px; } - -.m-r-10 { - margin-right: 10px; } - -.m-b-10 { - margin-bottom: 10px; } - -.m-l-15 { - margin-left: 15px; } - -.m-t-15 { - margin-top: 15px; } - -.m-r-15 { - margin-right: 15px; } - -.m-b-15 { - margin-bottom: 15px; } - -.m-l-20 { - margin-left: 20px; } - -.m-t-20 { - margin-top: 20px; } - -.m-r-20 { - margin-right: 20px; } - -.m-b-20 { - margin-bottom: 20px; } - -.m-l-25 { - margin-left: 25px; } - -.m-t-25 { - margin-top: 25px; } - -.m-r-25 { - margin-right: 25px; } - -.m-b-25 { - margin-bottom: 25px; } - -.m-l-30 { - margin-left: 30px; } - -.m-t-30 { - margin-top: 30px; } - -.m-r-30 { - margin-right: 30px; } - -.m-b-30 { - margin-bottom: 30px; } - -.m-l-35 { - margin-left: 35px; } - -.m-t-35 { - margin-top: 35px; } - -.m-r-35 { - margin-right: 35px; } - -.m-b-35 { - margin-bottom: 35px; } - -.m-l-40 { - margin-left: 40px; } - -.m-t-40 { - margin-top: 40px; } - -.m-r-40 { - margin-right: 40px; } - -.m-b-40 { - margin-bottom: 40px; } - -.m-l-45 { - margin-left: 45px; } - -.m-t-45 { - margin-top: 45px; } - -.m-r-45 { - margin-right: 45px; } - -.m-b-45 { - margin-bottom: 45px; } - -.m-l-50 { - margin-left: 50px; } - -.m-t-50 { - margin-top: 50px; } - -.m-r-50 { - margin-right: 50px; } - -.m-b-50 { - margin-bottom: 50px; } - -.m-l-55 { - margin-left: 55px; } - -.m-t-55 { - margin-top: 55px; } - -.m-r-55 { - margin-right: 55px; } - -.m-b-55 { - margin-bottom: 55px; } - -.m-l-60 { - margin-left: 60px; } - -.m-t-60 { - margin-top: 60px; } - -.m-r-60 { - margin-right: 60px; } - -.m-b-60 { - margin-bottom: 60px; } - -.m-l-65 { - margin-left: 65px; } - -.m-t-65 { - margin-top: 65px; } - -.m-r-65 { - margin-right: 65px; } - -.m-b-65 { - margin-bottom: 65px; } - -.m-l-70 { - margin-left: 70px; } - -.m-t-70 { - margin-top: 70px; } - -.m-r-70 { - margin-right: 70px; } - -.m-b-70 { - margin-bottom: 70px; } - -.m-l-75 { - margin-left: 75px; } - -.m-t-75 { - margin-top: 75px; } - -.m-r-75 { - margin-right: 75px; } - -.m-b-75 { - margin-bottom: 75px; } - -.m-l-80 { - margin-left: 80px; } - -.m-t-80 { - margin-top: 80px; } - -.m-r-80 { - margin-right: 80px; } - -.m-b-80 { - margin-bottom: 80px; } - -.m-l-85 { - margin-left: 85px; } - -.m-t-85 { - margin-top: 85px; } - -.m-r-85 { - margin-right: 85px; } - -.m-b-85 { - margin-bottom: 85px; } - -.m-l-90 { - margin-left: 90px; } - -.m-t-90 { - margin-top: 90px; } - -.m-r-90 { - margin-right: 90px; } - -.m-b-90 { - margin-bottom: 90px; } - -.m-l-95 { - margin-left: 95px; } - -.m-t-95 { - margin-top: 95px; } - -.m-r-95 { - margin-right: 95px; } - -.m-b-95 { - margin-bottom: 95px; } - -.m-l-100 { - margin-left: 100px; } - -.m-t-100 { - margin-top: 100px; } - -.m-r-100 { - margin-right: 100px; } - -.m-b-100 { - margin-bottom: 100px; } - -.m-l-105 { - margin-left: 105px; } - -.m-t-105 { - margin-top: 105px; } - -.m-r-105 { - margin-right: 105px; } - -.m-b-105 { - margin-bottom: 105px; } - -.m-l-110 { - margin-left: 110px; } - -.m-t-110 { - margin-top: 110px; } - -.m-r-110 { - margin-right: 110px; } - -.m-b-110 { - margin-bottom: 110px; } - -.m-l-115 { - margin-left: 115px; } - -.m-t-115 { - margin-top: 115px; } - -.m-r-115 { - margin-right: 115px; } - -.m-b-115 { - margin-bottom: 115px; } - -.m-l-120 { - margin-left: 120px; } - -.m-t-120 { - margin-top: 120px; } - -.m-r-120 { - margin-right: 120px; } - -.m-b-120 { - margin-bottom: 120px; } - -.m-l-125 { - margin-left: 125px; } - -.m-t-125 { - margin-top: 125px; } - -.m-r-125 { - margin-right: 125px; } - -.m-b-125 { - margin-bottom: 125px; } - -.margin-0 { - margin: 0; } - -.p-l-0 { - padding-left: 0; } - -.p-t-0 { - padding-top: 0; } - -.p-r-0 { - padding-right: 0; } - -.p-b-0 { - padding-bottom: 0; } - -.p-l-5 { - padding-left: 5px; } - -.p-t-5 { - padding-top: 5px; } - -.p-r-5 { - padding-right: 5px; } - -.p-b-5 { - padding-bottom: 5px; } - -.p-l-10 { - padding-left: 10px; } - -.p-t-10 { - padding-top: 10px; } - -.p-r-10 { - padding-right: 10px; } - -.p-b-10 { - padding-bottom: 10px; } - -.p-l-15 { - padding-left: 15px; } - -.p-t-15 { - padding-top: 15px; } - -.p-r-15 { - padding-right: 15px; } - -.p-b-15 { - padding-bottom: 15px; } - -.p-l-20 { - padding-left: 20px; } - -.p-t-20 { - padding-top: 20px; } - -.p-r-20 { - padding-right: 20px; } - -.p-b-20 { - padding-bottom: 20px; } - -.p-l-25 { - padding-left: 25px; } - -.p-t-25 { - padding-top: 25px; } - -.p-r-25 { - padding-right: 25px; } - -.p-b-25 { - padding-bottom: 25px; } - -.p-l-30 { - padding-left: 30px; } - -.p-t-30 { - padding-top: 30px; } - -.p-r-30 { - padding-right: 30px; } - -.p-b-30 { - padding-bottom: 30px; } - -.p-l-35 { - padding-left: 35px; } - -.p-t-35 { - padding-top: 35px; } - -.p-r-35 { - padding-right: 35px; } - -.p-b-35 { - padding-bottom: 35px; } - -.p-l-40 { - padding-left: 40px; } - -.p-t-40 { - padding-top: 40px; } - -.p-r-40 { - padding-right: 40px; } - -.p-b-40 { - padding-bottom: 40px; } - -.p-l-45 { - padding-left: 45px; } - -.p-t-45 { - padding-top: 45px; } - -.p-r-45 { - padding-right: 45px; } - -.p-b-45 { - padding-bottom: 45px; } - -.p-l-50 { - padding-left: 50px; } - -.p-t-50 { - padding-top: 50px; } - -.p-r-50 { - padding-right: 50px; } - -.p-b-50 { - padding-bottom: 50px; } - -.p-l-55 { - padding-left: 55px; } - -.p-t-55 { - padding-top: 55px; } - -.p-r-55 { - padding-right: 55px; } - -.p-b-55 { - padding-bottom: 55px; } - -.p-l-60 { - padding-left: 60px; } - -.p-t-60 { - padding-top: 60px; } - -.p-r-60 { - padding-right: 60px; } - -.p-b-60 { - padding-bottom: 60px; } - -.p-l-65 { - padding-left: 65px; } - -.p-t-65 { - padding-top: 65px; } - -.p-r-65 { - padding-right: 65px; } - -.p-b-65 { - padding-bottom: 65px; } - -.p-l-70 { - padding-left: 70px; } - -.p-t-70 { - padding-top: 70px; } - -.p-r-70 { - padding-right: 70px; } - -.p-b-70 { - padding-bottom: 70px; } - -.p-l-75 { - padding-left: 75px; } - -.p-t-75 { - padding-top: 75px; } - -.p-r-75 { - padding-right: 75px; } - -.p-b-75 { - padding-bottom: 75px; } - -.p-l-80 { - padding-left: 80px; } - -.p-t-80 { - padding-top: 80px; } - -.p-r-80 { - padding-right: 80px; } - -.p-b-80 { - padding-bottom: 80px; } - -.p-l-85 { - padding-left: 85px; } - -.p-t-85 { - padding-top: 85px; } - -.p-r-85 { - padding-right: 85px; } - -.p-b-85 { - padding-bottom: 85px; } - -.p-l-90 { - padding-left: 90px; } - -.p-t-90 { - padding-top: 90px; } - -.p-r-90 { - padding-right: 90px; } - -.p-b-90 { - padding-bottom: 90px; } - -.p-l-95 { - padding-left: 95px; } - -.p-t-95 { - padding-top: 95px; } - -.p-r-95 { - padding-right: 95px; } - -.p-b-95 { - padding-bottom: 95px; } - -.p-l-100 { - padding-left: 100px; } - -.p-t-100 { - padding-top: 100px; } - -.p-r-100 { - padding-right: 100px; } - -.p-b-100 { - padding-bottom: 100px; } - -.p-l-105 { - padding-left: 105px; } - -.p-t-105 { - padding-top: 105px; } - -.p-r-105 { - padding-right: 105px; } - -.p-b-105 { - padding-bottom: 105px; } - -.p-l-110 { - padding-left: 110px; } - -.p-t-110 { - padding-top: 110px; } - -.p-r-110 { - padding-right: 110px; } - -.p-b-110 { - padding-bottom: 110px; } - -.p-l-115 { - padding-left: 115px; } - -.p-t-115 { - padding-top: 115px; } - -.p-r-115 { - padding-right: 115px; } - -.p-b-115 { - padding-bottom: 115px; } - -.p-l-120 { - padding-left: 120px; } - -.p-t-120 { - padding-top: 120px; } - -.p-r-120 { - padding-right: 120px; } - -.p-b-120 { - padding-bottom: 120px; } - -.p-l-125 { - padding-left: 125px; } - -.p-t-125 { - padding-top: 125px; } - -.p-r-125 { - padding-right: 125px; } - -.p-b-125 { - padding-bottom: 125px; } - -.padding-0 { - padding: 0; } - -.font-6 { - font-size: 6px; } - -.font-7 { - font-size: 7px; } - -.font-8 { - font-size: 8px; } - -.font-9 { - font-size: 9px; } - -.font-10 { - font-size: 10px; } - -.font-11 { - font-size: 11px; } - -.font-12 { - font-size: 12px; } - -.font-13 { - font-size: 13px; } - -.font-14 { - font-size: 14px; } - -.font-15 { - font-size: 15px; } - -.font-16 { - font-size: 16px; } - -.font-17 { - font-size: 17px; } - -.font-18 { - font-size: 18px; } - -.font-19 { - font-size: 19px; } - -.font-20 { - font-size: 20px; } - -.font-21 { - font-size: 21px; } - -.font-22 { - font-size: 22px; } - -.font-23 { - font-size: 23px; } - -.font-24 { - font-size: 24px; } - -.font-25 { - font-size: 25px; } - -.font-26 { - font-size: 26px; } - -.font-27 { - font-size: 27px; } - -.font-28 { - font-size: 28px; } - -.font-29 { - font-size: 29px; } - -.font-30 { - font-size: 30px; } - -.font-31 { - font-size: 31px; } - -.font-32 { - font-size: 32px; } - -.font-33 { - font-size: 33px; } - -.font-34 { - font-size: 34px; } - -.font-35 { - font-size: 35px; } - -.font-36 { - font-size: 36px; } - -.font-37 { - font-size: 37px; } - -.font-38 { - font-size: 38px; } - -.font-39 { - font-size: 39px; } - -.font-40 { - font-size: 40px; } - -.font-41 { - font-size: 41px; } - -.font-42 { - font-size: 42px; } - -.font-43 { - font-size: 43px; } - -.font-44 { - font-size: 44px; } - -.font-45 { - font-size: 45px; } - -.font-46 { - font-size: 46px; } - -.font-47 { - font-size: 47px; } - -.font-48 { - font-size: 48px; } - -.font-49 { - font-size: 49px; } - -.font-50 { - font-size: 50px; } - -.align-left { - text-align: left; } - -.align-center { - text-align: center; } - -.align-right { - text-align: right; } - -.align-justify { - text-align: justify; } - -.no-resize { - resize: none; } - -.font-bold { - font-weight: bold; } - -.font-italic { - font-style: italic; } - -.font-underline { - text-decoration: underline; } - -.font-line-through { - text-decoration: line-through; } - -.font-overline { - text-decoration: overline; } - -.block-header { - margin-bottom: 15px; } - .block-header h2 { - margin: 0 !important; - color: #666 !important; - font-weight: normal; - font-size: 16px; } - .block-header h2 small { - display: block; - font-size: 12px; - margin-top: 8px; - color: #888; } - .block-header h2 small a { - font-weight: bold; - color: #777; } + background-color: rgba(0, 0, 0, 0.08); +} .bg-red { - background-color: #F44336 !important; - color: #fff; } - .bg-red .content .text, - .bg-red .content .number { - color: #fff !important; } - -.bg-pink { - background-color: #E91E63 !important; - color: #fff; } - .bg-pink .content .text, - .bg-pink .content .number { - color: #fff !important; } - -.bg-purple { - background-color: #9C27B0 !important; - color: #fff; } - .bg-purple .content .text, - .bg-purple .content .number { - color: #fff !important; } - -.bg-deep-purple { - background-color: #673AB7 !important; - color: #fff; } - .bg-deep-purple .content .text, - .bg-deep-purple .content .number { - color: #fff !important; } - -.bg-indigo { - background-color: #3F51B5 !important; - color: #fff; } - .bg-indigo .content .text, - .bg-indigo .content .number { - color: #fff !important; } - -.bg-blue { - background-color: #2196F3 !important; - color: #fff; } - .bg-blue .content .text, - .bg-blue .content .number { - color: #fff !important; } - -.bg-light-blue { - background-color: #03A9F4 !important; - color: #fff; } - .bg-light-blue .content .text, - .bg-light-blue .content .number { - color: #fff !important; } - -.bg-cyan { - background-color: #00BCD4 !important; - color: #fff; } - .bg-cyan .content .text, - .bg-cyan .content .number { - color: #fff !important; } - -.bg-teal { - background-color: #009688 !important; - color: #fff; } - .bg-teal .content .text, - .bg-teal .content .number { - color: #fff !important; } - -.bg-green { - background-color: #4CAF50 !important; - color: #fff; } - .bg-green .content .text, - .bg-green .content .number { - color: #fff !important; } - -.bg-light-green { - background-color: #8BC34A !important; - color: #fff; } - .bg-light-green .content .text, - .bg-light-green .content .number { - color: #fff !important; } - -.bg-lime { - background-color: #CDDC39 !important; - color: #fff; } - .bg-lime .content .text, - .bg-lime .content .number { - color: #fff !important; } - -.bg-yellow { - background-color: #ffe821 !important; - color: #fff; } - .bg-yellow .content .text, - .bg-yellow .content .number { - color: #fff !important; } - -.bg-amber { - background-color: #FFC107 !important; - color: #fff; } - .bg-amber .content .text, - .bg-amber .content .number { - color: #fff !important; } - -.bg-orange { - background-color: #FF9800 !important; - color: #fff; } - .bg-orange .content .text, - .bg-orange .content .number { - color: #fff !important; } - -.bg-deep-orange { - background-color: #FF5722 !important; - color: #fff; } - .bg-deep-orange .content .text, - .bg-deep-orange .content .number { - color: #fff !important; } - -.bg-brown { - background-color: #795548 !important; - color: #fff; } - .bg-brown .content .text, - .bg-brown .content .number { - color: #fff !important; } - -.bg-grey { - background-color: #9E9E9E !important; - color: #fff; } - .bg-grey .content .text, - .bg-grey .content .number { - color: #fff !important; } - -.bg-blue-grey { - background-color: #607D8B !important; - color: #fff; } - .bg-blue-grey .content .text, - .bg-blue-grey .content .number { - color: #fff !important; } - -.bg-black { - background-color: #000000 !important; - color: #fff; } - .bg-black .content .text, - .bg-black .content .number { - color: #fff !important; } - -.bg-white { - background-color: #ffffff !important; - color: #fff; } - .bg-white .content .text, - .bg-white .content .number { - color: #fff !important; } - -.col-red { - color: #F44336 !important; } - -.col-pink { - color: #E91E63 !important; } - -.col-purple { - color: #9C27B0 !important; } - -.col-deep-purple { - color: #673AB7 !important; } - -.col-indigo { - color: #3F51B5 !important; } - -.col-blue { - color: #2196F3 !important; } - -.col-light-blue { - color: #03A9F4 !important; } - -.col-cyan { - color: #00BCD4 !important; } - -.col-teal { - color: #009688 !important; } - -.col-green { - color: #4CAF50 !important; } - -.col-light-green { - color: #8BC34A !important; } - -.col-lime { - color: #CDDC39 !important; } - -.col-yellow { - color: #ffe821 !important; } - -.col-amber { - color: #FFC107 !important; } - -.col-orange { - color: #FF9800 !important; } - -.col-deep-orange { - color: #FF5722 !important; } - -.col-brown { - color: #795548 !important; } - -.col-grey { - color: #9E9E9E !important; } - -.col-blue-grey { - color: #607D8B !important; } - -.col-black { - color: #000000 !important; } - -.col-white { - color: #ffffff !important; } - -/* Custom Animate ============================== */ -@-ms-keyframes spin { - from { - -ms-transform: rotate(0deg); - -moz-transform: rotate(0deg); - -o-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - to { - -ms-transform: rotate(360deg); - -moz-transform: rotate(360deg); - -o-transform: rotate(360deg); - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -@-moz-keyframes spin { - from { - -moz-transform: rotate(0deg); - -ms-transform: rotate(0deg); - -o-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - to { - -moz-transform: rotate(360deg); - -ms-transform: rotate(360deg); - -o-transform: rotate(360deg); - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -@-webkit-keyframes spin { - from { - -webkit-transform: rotate(0deg); - -moz-transform: rotate(0deg); - -ms-transform: rotate(0deg); - -o-transform: rotate(0deg); - transform: rotate(0deg); } - to { - -webkit-transform: rotate(360deg); - -moz-transform: rotate(360deg); - -ms-transform: rotate(360deg); - -o-transform: rotate(360deg); - transform: rotate(360deg); } } - -@keyframes spin { - from { - -moz-transform: rotate(0deg); - -ms-transform: rotate(0deg); - -o-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); } - to { - -moz-transform: rotate(360deg); - -ms-transform: rotate(360deg); - -o-transform: rotate(360deg); - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -/* Demo ======================================== */ -.demo-button-sizes .btn { - margin-bottom: 5px; } - -.icon-button-demo button { - margin-right: 5px; - margin-bottom: 12px; } - -.icon-and-text-button-demo button { - margin-right: 5px; - margin-bottom: 12px; - width: 16.66666666666667%; } - -.button-demo ul { - padding-left: 0; } - .button-demo ul li { - list-style: none; - padding-left: 0; - display: inline-block; - margin-right: 7px; } - .button-demo ul li .btn { - display: block; - min-width: 175px; } - -.button-demo .btn { - margin-right: 8px; - margin-bottom: 13px; - min-width: 120px; } - -.demo-button-groups .btn-group { - margin-right: 10px; } - -.demo-button-toolbar .btn-toolbar { - float: left; - margin-right: 25px; } - -.demo-button-nesting > .btn-group { - margin-right: 15px; } - -.demo-single-button-dropdowns > .btn-group { - margin-right: 10px; } - -.demo-splite-button-dropdowns > .btn-group { - margin-right: 10px; } - -.demo-dropup .dropup { - margin-right: 10px; } - -.demo-checkbox label, -.demo-radio-button label { - min-width: 150px; } - -.demo-knob-chart div { - margin-right: 15px; } - -.demo-switch .switch { - display: inline-block; - min-width: 170px; } - -.demo-switch .demo-switch-title { - min-width: 95px; - display: inline-block; } - -.demo-color-box { - padding: 15px 0; - text-align: center; - margin-bottom: 20px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - border-radius: 3px; } - .demo-color-box .color-name { - font-size: 16px; - margin-bottom: 5px; } - .demo-color-box .color-code, - .demo-color-box .color-class-name { - font-size: 13px; } - -.demo-image-copyright { - text-align: right; - font-style: italic; - font-size: 12px; - color: #777; - margin: 5px 0 10px 0; } - .demo-image-copyright a { - font-weight: bold; - color: #555 !important; } - -.demo-tagsinput-area { - margin-bottom: 50px !important; } - -.demo-icon-container .demo-google-material-icon { - margin-bottom: 5px; - text-align: left; } - .demo-icon-container .demo-google-material-icon .icon-name { - position: relative; - top: -8px; - left: 7px; } - .demo-icon-container .demo-google-material-icon .material-icons { - width: 24px; } - -.demo-preloader .preloader { - margin-right: 10px; } - -.irs-demo { - margin-bottom: 40px; } - .irs-demo .irs { - margin-top: 15px; } - -.right-sidebar .nav-tabs + .tab-content { - padding: 0; } - -.right-sidebar p { - margin: 20px 15px 15px 15px; - font-weight: bold; - text-align: center; } - -.right-sidebar #settings .setting-list { - list-style: none; - padding-left: 0; - margin-bottom: 20px; } - .right-sidebar #settings .setting-list li { - padding: 15px; - position: relative; - border-top: 1px solid #eee; } - .right-sidebar #settings .setting-list li .switch { - position: absolute; - top: 15px; - right: 5px; } - -.demo-choose-skin { - list-style: none; - padding-left: 0; - overflow-y: hidden; } - .demo-choose-skin li { - border-bottom: 1px solid #eee; - padding: 10px 10px 4px 10px; - position: relative; - cursor: pointer; } - .demo-choose-skin li.active { - background-color: #eee; } - .demo-choose-skin li.active:after { - font-family: 'Material Icons'; - position: absolute; - top: 10px; - right: 10px; - content: '\E876'; - font-size: 18px; - font-weight: bold; } - .demo-choose-skin li:hover { - background-color: #eee; } - .demo-choose-skin li div { - width: 24px; - height: 24px; - display: inline-block; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - border-radius: 3px; } - .demo-choose-skin li span { - position: relative; - bottom: 7px; - left: 5px; } - .demo-choose-skin .red { - background-color: #F44336; } - .demo-choose-skin .pink { - background-color: #E91E63; } - .demo-choose-skin .purple { - background-color: #9C27B0; } - .demo-choose-skin .deep-purple { - background-color: #673AB7; } - .demo-choose-skin .indigo { - background-color: #3F51B5; } - .demo-choose-skin .blue { - background-color: #2196F3; } - .demo-choose-skin .light-blue { - background-color: #03A9F4; } - .demo-choose-skin .cyan { - background-color: #00BCD4; } - .demo-choose-skin .teal { - background-color: #009688; } - .demo-choose-skin .green { - background-color: #4CAF50; } - .demo-choose-skin .light-green { - background-color: #8BC34A; } - .demo-choose-skin .lime { - background-color: #CDDC39; } - .demo-choose-skin .yellow { - background-color: #ffe821; } - .demo-choose-skin .amber { - background-color: #FFC107; } - .demo-choose-skin .orange { - background-color: #FF9800; } - .demo-choose-skin .deep-orange { - background-color: #FF5722; } - .demo-choose-skin .brown { - background-color: #795548; } - .demo-choose-skin .grey { - background-color: #9E9E9E; } - .demo-choose-skin .blue-grey { - background-color: #607D8B; } - .demo-choose-skin .black { - background-color: #000000; } - .demo-choose-skin .white { - background-color: #ffffff; } - -/* Materialize Css | Taken from www.materializecss.com */ -/* Media ======================================= */ -@media (max-width: 767px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: 35px; - width: 73%; } - .navbar .navbar-header { - display: inline-block; - margin-bottom: -6px; - width: calc(100% + 30px); } - .navbar .nav > li { - display: inline-block; } - .navbar .navbar-nav { - margin-top: -10px; - margin-bottom: 1px; - margin-left: -7px; } - .navbar .navbar-nav .open .dropdown-menu { - background-color: #fff; - position: absolute; } - .navbar .dropdown-menu { - margin-left: -50px; } - .navbar .js-right-sidebar { - margin-top: 15px; } - .dt-buttons { - float: none !important; - text-align: center; - margin-bottom: 15px; } - .panel-switch-btn { - top: 12px; - right: 0 !important; } } - -@media (min-width: 768px) and (max-width: 991px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: 20px; } } - -@media (min-width: 992px) and (max-width: 1169px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: 20px; } } - -/* General ===================================== */ -body { - background-color: #e9e9e9; - -moz-transition: all 0.5s; - -o-transition: all 0.5s; - -webkit-transition: all 0.5s; - transition: all 0.5s; - font-family: 'Roboto', Arial, Tahoma, sans-serif; } - -h1, -h2, -h3, -h4, -h5, -h6 { - font-weight: bold; } - -button, -input, -select, -a { - outline: none !important; } - -.no-animate { - -o-transition-property: none !important; - -moz-transition-property: none !important; - -ms-transition-property: none !important; - -webkit-transition-property: none !important; - transition-property: none !important; - -o-transform: none !important; - -moz-transform: none !important; - -ms-transform: none !important; - -webkit-transform: none !important; - transform: none !important; - -webkit-animation: none !important; - -moz-animation: none !important; - -o-animation: none !important; - -ms-animation: none !important; - animation: none !important; } - -section.content { - margin: 100px 15px 0 235px; - -moz-transition: 0.5s; - -o-transition: 0.5s; - -webkit-transition: 0.5s; - transition: 0.5s; } - -/* Dashboard =================================== */ -.dashboard-flot-chart { - height: 400px; } - -.dashboard-donut-chart { - height: 265px; } - -.dashboard-line-chart { - height: 250px; } - -.dashboard-stat-list { - list-style: none; - padding-left: 0; - margin-top: 40px; } - .dashboard-stat-list li { - padding: 16px 0 0 0; } - .dashboard-stat-list li small { - font-size: 8px; } - -.dashboard-task-infos .progress { - height: 10px; - margin-bottom: 0; - position: relative; - top: 6px; } - -/* Buttons ===================================== */ -.btn:focus { - outline: none !important; } - -.btn-circle { - border: none; - outline: none !important; - overflow: hidden; - width: 40px; - height: 40px; - -webkit-border-radius: 50%; - -moz-border-radius: 50%; - -ms-border-radius: 50%; - border-radius: 50%; } - .btn-circle i { - font-size: 18px; - position: relative; - left: -1px; } - -.btn-link { - font-weight: bold; - color: #333; - -moz-transition: 0.5s; - -o-transition: 0.5s; - -webkit-transition: 0.5s; - transition: 0.5s; } - .btn-link:active, .btn-link:focus { - text-decoration: none; - color: #333; } - .btn-link:hover { - text-decoration: none; - color: #333; - background-color: #ddd; } - -.btn-circle-lg { - border: none; - outline: none !important; - overflow: hidden; - width: 50px; - height: 50px; - -webkit-border-radius: 50% !important; - -moz-border-radius: 50% !important; - -ms-border-radius: 50% !important; - border-radius: 50% !important; } - .btn-circle-lg i { - font-size: 26px !important; - position: relative !important; - left: 0 !important; - top: 6px !important; } - -.btn:not(.btn-link):not(.btn-circle) { - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -ms-border-radius: 2px; - border-radius: 2px; - border: none; - font-size: 13px; - outline: none; } - .btn:not(.btn-link):not(.btn-circle):hover { - outline: none; } - .btn:not(.btn-link):not(.btn-circle) i { - font-size: 20px; - position: relative; - top: 3px; } - .btn:not(.btn-link):not(.btn-circle) span { - position: relative; - top: -2px; - margin-left: 3px; } - -.btn-warning, -.btn-warning:hover, -.btn-warning:active, -.btn-warning:focus { - background-color: #ff9600 !important; } - -.btn-danger, -.btn-danger:hover, -.btn-danger:active, -.btn-danger:focus { - background-color: #fb483a !important; } - -.btn-info, -.btn-info:hover, -.btn-info:active, -.btn-info:focus { - background-color: #00b0e4 !important; } - -.btn-success, -.btn-success:hover, -.btn-success:active, -.btn-success:focus { - background-color: #2b982b !important; } - -.btn-primary, -.btn-primary:hover, -.btn-primary:active, -.btn-primary:focus { - background-color: #1f91f3 !important; } - -.btn-default, -.btn-default:hover, -.btn-default:active, -.btn-default:focus { - background-color: #fff !important; } - -.btn-group, -.btn-group-vertical { - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); } - .btn-group .btn, - .btn-group-vertical .btn { - box-shadow: none !important; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .btn-group .btn .caret, - .btn-group-vertical .btn .caret { - position: relative; - bottom: 1px; } - .btn-group .btn-group, - .btn-group-vertical .btn-group { - box-shadow: none !important; } - .btn-group .btn + .dropdown-toggle, - .btn-group-vertical .btn + .dropdown-toggle { - border-left: 1px solid rgba(0, 0, 0, 0.08) !important; } - -/* Bootstrap Tags Input ======================== */ -.bootstrap-tagsinput { - -webkit-box-shadow: none !important; - -moz-box-shadow: none !important; - -ms-box-shadow: none !important; - box-shadow: none !important; - border: none !important; } - -/* noUISlider ================================== */ -.noUi-target { - -webkit-touch-callout: none; - -webkit-user-select: none; - -ms-touch-action: none; - touch-action: none; - -ms-user-select: none; - -moz-user-select: none; - user-select: none; - -moz-box-sizing: border-box; - box-sizing: border-box; - position: relative; - direction: ltr; } - .noUi-target * { - -webkit-touch-callout: none; - -webkit-user-select: none; - -ms-touch-action: none; - touch-action: none; - -ms-user-select: none; - -moz-user-select: none; - user-select: none; - -moz-box-sizing: border-box; - box-sizing: border-box; } - -.noUi-base { - width: 100%; - height: 100%; - position: relative; - z-index: 1; } - -.noUi-origin { - position: absolute; - right: 0; - top: 6px; - left: 0; - bottom: 0; } - -.noUi-handle { - position: relative; - z-index: 1; } - -.noUi-stacking .noUi-handle { - z-index: 10; } - -.noUi-state-tap .noUi-origin { - -webkit-transition: left 0.25s, top 0.25s; - transition: left 0.25s, top 0.25s; } - -.noUi-state-drag * { - cursor: inherit !important; } - -.noUi-base { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); } - -.noUi-horizontal { - height: 18px; } - .noUi-horizontal .noUi-handle { - width: 34px; - height: 28px; - left: -17px; - top: -6px; } - -.noUi-vertical { - width: 18px; } - .noUi-vertical .noUi-handle { - width: 28px; - height: 34px; - left: -6px; - top: -17px; } - -.noUi-background { - background: #FAFAFA; - box-shadow: inset 0 1px 1px #f0f0f0; } - -.noUi-connect { - background: #3FB8AF; - box-shadow: inset 0 0 3px rgba(51, 51, 51, 0.45); - -webkit-transition: background 450ms; - transition: background 450ms; } - -.noUi-origin { - border-radius: 2px; } - -.noUi-target { - border-radius: 4px; - border: 1px solid #D3D3D3; - box-shadow: inset 0 1px 1px #F0F0F0, 0 3px 6px -5px #BBB; } - .noUi-target.noUi-connect { - box-shadow: inset 0 0 3px rgba(51, 51, 51, 0.45), 0 3px 6px -5px #BBB; } - -.noUi-dragable { - cursor: w-resize; } - -.noUi-vertical .noUi-dragable { - cursor: n-resize; } - -.noUi-handle { - border: 1px solid #D9D9D9; - border-radius: 3px; - background: #FFF; - cursor: default; - box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #EBEBEB, 0 3px 6px -3px #BBB; } - -.noUi-active { - box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #DDD, 0 3px 6px -3px #BBB; } - -.noUi-handle:before { - content: ""; - display: block; - position: absolute; - height: 14px; - width: 1px; - background: #E8E7E6; - left: 14px; - top: 6px; } - -.noUi-handle:after { - content: ""; - display: block; - position: absolute; - height: 14px; - width: 1px; - background: #E8E7E6; - left: 14px; - top: 6px; - left: 17px; } - -.noUi-vertical .noUi-handle:before { - width: 14px; - height: 1px; - left: 6px; - top: 14px; } - -.noUi-vertical .noUi-handle:after { - width: 14px; - height: 1px; - left: 6px; - top: 14px; - top: 17px; } - -[disabled].noUi-connect, [disabled] .noUi-connect { - background: #B8B8B8; } - -[disabled].noUi-origin, [disabled] .noUi-handle { - cursor: not-allowed; } - -.noUi-target { - box-shadow: none; - border: none; } - -.noUi-base { - height: 15px; - top: -6px; } - -.noUi-background { - height: 3px; - top: 6px; - background-color: #bfbfbf; - box-shadow: none; } - -.noUi-horizontal { - height: 3px; } - -.noUi-connect { - height: 3px; - top: 6px; - background-color: #26A69A; - box-shadow: none; } - -.noUi-horizontal .noUi-handle { - width: 15px; - height: 15px; - border-radius: 50%; - box-shadow: none; - background-color: #26A69A; - border: none; - left: -5px; - top: -6px; - transition: width 0.2s cubic-bezier(0.215, 0.61, 0.355, 1), height 0.2s cubic-bezier(0.215, 0.61, 0.355, 1), left 0.2s cubic-bezier(0.215, 0.61, 0.355, 1), top 0.2s cubic-bezier(0.215, 0.61, 0.355, 1); } - -.noUi-handle:before, .noUi-handle:after { - content: none; } - -.noUi-target .noUi-active.noUi-handle { - -webkit-box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); - -moz-box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); - -ms-box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); - box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); } - -.noUi-target .range-label { - position: absolute; - height: 30px; - width: 30px; - top: -17px; - left: -2px; - background-color: #26A69A; - border-radius: 50%; - transition: border-radius 0.25s cubic-bezier(0.215, 0.61, 0.355, 1), transform 0.25s cubic-bezier(0.215, 0.61, 0.355, 1); - transform: scale(0.5) rotate(-45deg); - transform-origin: 50% 100%; } - -.noUi-target .noUi-active .range-label { - border-radius: 15px 15px 15px 0; - transform: rotate(-45deg) translate(23px, -25px); } - -.range-label span { - width: 100%; - text-align: center; - color: #fff; - font-size: 12px; - transform: rotate(45deg); - opacity: 0; - position: absolute; - top: 7px; - left: -1px; - transition: opacity 0.25s cubic-bezier(0.215, 0.61, 0.355, 1); } - -.noUi-active .range-label span { - opacity: 1; } - -/* Multi Select ================================ */ -.ms-container { - width: auto !important; } - .ms-container .ms-list { - -webkit-box-shadow: none !important; - -moz-box-shadow: none !important; - -ms-box-shadow: none !important; - box-shadow: none !important; - -webkit-border-radius: 0 !important; - -moz-border-radius: 0 !important; - -ms-border-radius: 0 !important; - border-radius: 0 !important; } - .ms-container .ms-list.ms-focus { - -webkit-box-shadow: none !important; - -moz-box-shadow: none !important; - -ms-box-shadow: none !important; - box-shadow: none !important; } - .ms-container .ms-selectable, - .ms-container .ms-selection { - min-width: 250px !important; } - .ms-container .ms-selectable li.ms-hover, - .ms-container .ms-selection li.ms-hover { - color: #000000 !important; - background-color: #e6e6e6 !important; } - .ms-container .ms-selectable li.ms-elem-selectable, - .ms-container .ms-selectable li.ms-elem-selection, - .ms-container .ms-selection li.ms-elem-selectable, - .ms-container .ms-selection li.ms-elem-selection { - padding: 9px 15px 6px 15px !important; } - .ms-container .ms-optgroup-label { - padding: 5px 0 0 8px !important; } - -/* Card ======================================== */ -.card { - background: #fff; - min-height: 50px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); - position: relative; - margin-bottom: 30px; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -ms-border-radius: 2px; - border-radius: 2px; } - .card .card-inside-title { - margin-top: 25px; - margin-bottom: 15px; - display: block; - font-size: 15px; - color: #000; } - .card .card-inside-title small { - color: #999; - display: block; - font-size: 11px; - margin-top: 5px; } - .card .card-inside-title small a { - color: #777; - font-weight: bold; } - .card .card-inside-title:first-child { - margin-top: 0; } - .card .bg-red, - .card .bg-pink, - .card .bg-purple, - .card .bg-deep-purple, - .card .bg-indigo, - .card .bg-blue, - .card .bg-light-blue, - .card .bg-cyan, - .card .bg-teal, - .card .bg-green, - .card .bg-light-green, - .card .bg-lime, - .card .bg-yellow, - .card .bg-amber, - .card .bg-orange, - .card .bg-deep-orange, - .card .bg-brown, - .card .bg-grey, - .card .bg-blue-grey, - .card .bg-black { - border-bottom: none !important; - color: #fff !important; } - .card .bg-red h2, .card .bg-red small, .card .bg-red .material-icons, - .card .bg-pink h2, - .card .bg-pink small, - .card .bg-pink .material-icons, - .card .bg-purple h2, - .card .bg-purple small, - .card .bg-purple .material-icons, - .card .bg-deep-purple h2, - .card .bg-deep-purple small, - .card .bg-deep-purple .material-icons, - .card .bg-indigo h2, - .card .bg-indigo small, - .card .bg-indigo .material-icons, - .card .bg-blue h2, - .card .bg-blue small, - .card .bg-blue .material-icons, - .card .bg-light-blue h2, - .card .bg-light-blue small, - .card .bg-light-blue .material-icons, - .card .bg-cyan h2, - .card .bg-cyan small, - .card .bg-cyan .material-icons, - .card .bg-teal h2, - .card .bg-teal small, - .card .bg-teal .material-icons, - .card .bg-green h2, - .card .bg-green small, - .card .bg-green .material-icons, - .card .bg-light-green h2, - .card .bg-light-green small, - .card .bg-light-green .material-icons, - .card .bg-lime h2, - .card .bg-lime small, - .card .bg-lime .material-icons, - .card .bg-yellow h2, - .card .bg-yellow small, - .card .bg-yellow .material-icons, - .card .bg-amber h2, - .card .bg-amber small, - .card .bg-amber .material-icons, - .card .bg-orange h2, - .card .bg-orange small, - .card .bg-orange .material-icons, - .card .bg-deep-orange h2, - .card .bg-deep-orange small, - .card .bg-deep-orange .material-icons, - .card .bg-brown h2, - .card .bg-brown small, - .card .bg-brown .material-icons, - .card .bg-grey h2, - .card .bg-grey small, - .card .bg-grey .material-icons, - .card .bg-blue-grey h2, - .card .bg-blue-grey small, - .card .bg-blue-grey .material-icons, - .card .bg-black h2, - .card .bg-black small, - .card .bg-black .material-icons { - color: #fff !important; } - .card .bg-red .badge, - .card .bg-pink .badge, - .card .bg-purple .badge, - .card .bg-deep-purple .badge, - .card .bg-indigo .badge, - .card .bg-blue .badge, - .card .bg-light-blue .badge, - .card .bg-cyan .badge, - .card .bg-teal .badge, - .card .bg-green .badge, - .card .bg-light-green .badge, - .card .bg-lime .badge, - .card .bg-yellow .badge, - .card .bg-amber .badge, - .card .bg-orange .badge, - .card .bg-deep-orange .badge, - .card .bg-brown .badge, - .card .bg-grey .badge, - .card .bg-blue-grey .badge, - .card .bg-black .badge { - background-color: #fff; - color: #555; } - .card .header { - color: #555; - padding: 20px; - position: relative; - border-bottom: 1px solid rgba(204, 204, 204, 0.35); } - .card .header .header-dropdown { - position: absolute; - top: 20px; - right: 15px; - list-style: none; } - .card .header .header-dropdown .dropdown-menu li { - display: block !important; } - .card .header .header-dropdown li { - display: inline-block; } - .card .header .header-dropdown i { - font-size: 20px; - color: #999; - -moz-transition: all 0.5s; - -o-transition: all 0.5s; - -webkit-transition: all 0.5s; - transition: all 0.5s; } - .card .header .header-dropdown i:hover { - color: #000; } - .card .header h2 { - margin: 0; - font-size: 18px; - font-weight: normal; - color: #111; } - .card .header h2 small { - display: block; - font-size: 12px; - margin-top: 5px; - color: #999; - line-height: 15px; } - .card .header h2 small a { - font-weight: bold; - color: #777; } - .card .header .col-xs-12 h2 { - margin-top: 5px; } - .card .body { - font-size: 14px; - color: #555; - padding: 20px; } - .card .body .col-xs-1, - .card .body .col-sm-1, - .card .body .col-md-1, - .card .body .col-lg-1 { - margin-bottom: 20px; } - .card .body .col-xs-2, - .card .body .col-sm-2, - .card .body .col-md-2, - .card .body .col-lg-2 { - margin-bottom: 20px; } - .card .body .col-xs-3, - .card .body .col-sm-3, - .card .body .col-md-3, - .card .body .col-lg-3 { - margin-bottom: 20px; } - .card .body .col-xs-4, - .card .body .col-sm-4, - .card .body .col-md-4, - .card .body .col-lg-4 { - margin-bottom: 20px; } - .card .body .col-xs-5, - .card .body .col-sm-5, - .card .body .col-md-5, - .card .body .col-lg-5 { - margin-bottom: 20px; } - .card .body .col-xs-6, - .card .body .col-sm-6, - .card .body .col-md-6, - .card .body .col-lg-6 { - margin-bottom: 20px; } - .card .body .col-xs-7, - .card .body .col-sm-7, - .card .body .col-md-7, - .card .body .col-lg-7 { - margin-bottom: 20px; } - .card .body .col-xs-8, - .card .body .col-sm-8, - .card .body .col-md-8, - .card .body .col-lg-8 { - margin-bottom: 20px; } - .card .body .col-xs-9, - .card .body .col-sm-9, - .card .body .col-md-9, - .card .body .col-lg-9 { - margin-bottom: 20px; } - .card .body .col-xs-10, - .card .body .col-sm-10, - .card .body .col-md-10, - .card .body .col-lg-10 { - margin-bottom: 20px; } - .card .body .col-xs-11, - .card .body .col-sm-11, - .card .body .col-md-11, - .card .body .col-lg-11 { - margin-bottom: 20px; } - .card .body .col-xs-12, - .card .body .col-sm-12, - .card .body .col-md-12, - .card .body .col-lg-12 { - margin-bottom: 20px; } - -/* Infobox ===================================== */ -.info-box { - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); - height: 80px; - display: flex; - cursor: default; - background-color: #fff; - position: relative; - overflow: hidden; - margin-bottom: 30px; } - .info-box .icon { - display: inline-block; - text-align: center; - background-color: rgba(0, 0, 0, 0.12); - width: 80px; } - .info-box .icon i { - color: #fff; - font-size: 50px; - line-height: 80px; } - .info-box .icon .chart.chart-bar { - height: 100%; - line-height: 100px; } - .info-box .icon .chart.chart-bar canvas { - vertical-align: baseline !important; } - .info-box .icon .chart.chart-pie { - height: 100%; - line-height: 123px; } - .info-box .icon .chart.chart-pie canvas { - vertical-align: baseline !important; } - .info-box .icon .chart.chart-line { - height: 100%; - line-height: 115px; } - .info-box .icon .chart.chart-line canvas { - vertical-align: baseline !important; } - .info-box .content { - display: inline-block; - padding: 7px 10px; } - .info-box .content .text { - font-size: 13px; - margin-top: 11px; - color: #555; } - .info-box .content .number { - font-weight: normal; - font-size: 26px; - margin-top: -4px; - color: #555; } - -.info-box.hover-zoom-effect .icon { - overflow: hidden; } - .info-box.hover-zoom-effect .icon i { - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } - -.info-box.hover-zoom-effect:hover .icon i { - opacity: 0.4; - -moz-transform: rotate(-32deg) scale(1.4); - -ms-transform: rotate(-32deg) scale(1.4); - -o-transform: rotate(-32deg) scale(1.4); - -webkit-transform: rotate(-32deg) scale(1.4); - transform: rotate(-32deg) scale(1.4); } - -.info-box.hover-expand-effect:after { - background-color: rgba(0, 0, 0, 0.05); - content: "."; - position: absolute; - left: 80px; - top: 0; - width: 0; - height: 100%; - color: transparent; - -moz-transition: all 0.95s; - -o-transition: all 0.95s; - -webkit-transition: all 0.95s; - transition: all 0.95s; } - -.info-box.hover-expand-effect:hover:after { - width: 100%; } - -.info-box-2 { - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); - height: 80px; - display: flex; - cursor: default; - background-color: #fff; - position: relative; - overflow: hidden; - margin-bottom: 30px; } - .info-box-2 .icon { - display: inline-block; - text-align: center; - width: 80px; } - .info-box-2 .icon i { - color: #fff; - font-size: 50px; - line-height: 80px; } - .info-box-2 .chart.chart-bar { - height: 100%; - line-height: 105px; } - .info-box-2 .chart.chart-bar canvas { - vertical-align: baseline !important; } - .info-box-2 .chart.chart-pie { - height: 100%; - line-height: 123px; } - .info-box-2 .chart.chart-pie canvas { - vertical-align: baseline !important; } - .info-box-2 .chart.chart-line { - height: 100%; - line-height: 115px; } - .info-box-2 .chart.chart-line canvas { - vertical-align: baseline !important; } - .info-box-2 .content { - display: inline-block; - padding: 7px 10px; } - .info-box-2 .content .text { - font-size: 13px; - margin-top: 11px; - color: #555; } - .info-box-2 .content .number { - font-weight: normal; - font-size: 26px; - margin-top: -4px; - color: #555; } - -.info-box-2.hover-zoom-effect .icon { - overflow: hidden; } - .info-box-2.hover-zoom-effect .icon i { - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } - -.info-box-2.hover-zoom-effect:hover .icon i { - opacity: 0.4; - -moz-transform: rotate(-32deg) scale(1.4); - -ms-transform: rotate(-32deg) scale(1.4); - -o-transform: rotate(-32deg) scale(1.4); - -webkit-transform: rotate(-32deg) scale(1.4); - transform: rotate(-32deg) scale(1.4); } - -.info-box-2.hover-expand-effect:after { - background-color: rgba(0, 0, 0, 0.05); - content: "."; - position: absolute; - left: 0; - top: 0; - width: 0; - height: 100%; - color: transparent; - -moz-transition: all 0.95s; - -o-transition: all 0.95s; - -webkit-transition: all 0.95s; - transition: all 0.95s; } - -.info-box-2.hover-expand-effect:hover:after { - width: 100%; } - -.info-box-3 { - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); - height: 80px; - display: flex; - cursor: default; - background-color: #fff; - position: relative; - overflow: hidden; - margin-bottom: 30px; } - .info-box-3 .icon { - position: absolute; - right: 10px; - bottom: 2px; - text-align: center; } - .info-box-3 .icon i { - color: rgba(0, 0, 0, 0.15); - font-size: 60px; } - .info-box-3 .chart { - margin-right: 5px; } - .info-box-3 .chart.chart-bar { - height: 100%; - line-height: 50px; } - .info-box-3 .chart.chart-bar canvas { - vertical-align: baseline !important; } - .info-box-3 .chart.chart-pie { - height: 100%; - line-height: 34px; } - .info-box-3 .chart.chart-pie canvas { - vertical-align: baseline !important; } - .info-box-3 .chart.chart-line { - height: 100%; - line-height: 40px; } - .info-box-3 .chart.chart-line canvas { - vertical-align: baseline !important; } - .info-box-3 .content { - display: inline-block; - padding: 7px 16px; } - .info-box-3 .content .text { - font-size: 13px; - margin-top: 11px; - color: #555; } - .info-box-3 .content .number { - font-weight: normal; - font-size: 26px; - margin-top: -4px; - color: #555; } - -.info-box-3.hover-zoom-effect .icon i { - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } - -.info-box-3.hover-zoom-effect:hover .icon i { - opacity: 0.4; - -moz-transform: rotate(-32deg) scale(1.4); - -ms-transform: rotate(-32deg) scale(1.4); - -o-transform: rotate(-32deg) scale(1.4); - -webkit-transform: rotate(-32deg) scale(1.4); - transform: rotate(-32deg) scale(1.4); } - -.info-box-3.hover-expand-effect:after { - background-color: rgba(0, 0, 0, 0.05); - content: "."; - position: absolute; - left: 0; - top: 0; - width: 0; - height: 100%; - color: transparent; - -moz-transition: all 0.95s; - -o-transition: all 0.95s; - -webkit-transition: all 0.95s; - transition: all 0.95s; } - -.info-box-3.hover-expand-effect:hover:after { - width: 100%; } - -.info-box-4 { - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); - height: 80px; - display: flex; - cursor: default; - background-color: #fff; - position: relative; - overflow: hidden; - margin-bottom: 30px; } - .info-box-4 .icon { - position: absolute; - right: 10px; - bottom: 2px; - text-align: center; } - .info-box-4 .icon i { - color: rgba(0, 0, 0, 0.15); - font-size: 60px; } - .info-box-4 .chart { - margin-right: 5px; } - .info-box-4 .chart.chart-bar { - height: 100%; - line-height: 50px; } - .info-box-4 .chart.chart-bar canvas { - vertical-align: baseline !important; } - .info-box-4 .chart.chart-pie { - height: 100%; - line-height: 34px; } - .info-box-4 .chart.chart-pie canvas { - vertical-align: baseline !important; } - .info-box-4 .chart.chart-line { - height: 100%; - line-height: 40px; } - .info-box-4 .chart.chart-line canvas { - vertical-align: baseline !important; } - .info-box-4 .content { - display: inline-block; - padding: 7px 16px; } - .info-box-4 .content .text { - font-size: 13px; - margin-top: 11px; - color: #555; } - .info-box-4 .content .number { - font-weight: normal; - font-size: 26px; - margin-top: -4px; - color: #555; } - -.info-box-4.hover-zoom-effect .icon i { - -moz-transition: all 0.3s ease; - -o-transition: all 0.3s ease; - -webkit-transition: all 0.3s ease; - transition: all 0.3s ease; } - -.info-box-4.hover-zoom-effect:hover .icon i { - opacity: 0.4; - -moz-transform: rotate(-32deg) scale(1.4); - -ms-transform: rotate(-32deg) scale(1.4); - -o-transform: rotate(-32deg) scale(1.4); - -webkit-transform: rotate(-32deg) scale(1.4); - transform: rotate(-32deg) scale(1.4); } - -.info-box-4.hover-expand-effect:after { - background-color: rgba(0, 0, 0, 0.05); - content: "."; - position: absolute; - left: 0; - top: 0; - width: 0; - height: 100%; - color: transparent; - -moz-transition: all 0.95s; - -o-transition: all 0.95s; - -webkit-transition: all 0.95s; - transition: all 0.95s; } - -.info-box-4.hover-expand-effect:hover:after { - width: 100%; } - -/* Alerts ====================================== */ -.alert { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - -ms-box-shadow: none; - box-shadow: none; - border: none; - color: #fff !important; } - .alert .alert-link { - color: #fff; - text-decoration: underline; - font-weight: bold; } - -.alert-success { - background-color: #2b982b; } - -.alert-info { - background-color: #00b0e4; } - -.alert-warning { - background-color: #ff9600 !important; } - -.alert-danger { - background-color: #fb483a !important; } - -.alert-dismissible .close { - color: #fff; - opacity: 1; - border: none; - text-shadow: none; } - -/* Dialogs (SweetAlert) ======================== */ -.sweet-alert { - -webkit-border-radius: 0 !important; - -moz-border-radius: 0 !important; - -ms-border-radius: 0 !important; - border-radius: 0 !important; } - .sweet-alert p { - font-size: 14px !important; } - .sweet-alert .sa-input-error { - top: 23px !important; - right: 13px !important; } - .sweet-alert h2 { - font-size: 18px !important; - margin: 0 0 5px 0 !important; } - .sweet-alert button { - font-size: 15px !important; - -webkit-border-radius: 0 !important; - -moz-border-radius: 0 !important; - -ms-border-radius: 0 !important; - border-radius: 0 !important; - padding: 5px 20px !important; } - -/* Checkbox & Radio ============================ */ -[type="checkbox"] + label { - padding-left: 26px; - height: 25px; - line-height: 21px; - font-size: 13px; - font-weight: normal; } - -[type="checkbox"]:checked + label:before { - top: -4px; - left: -2px; - width: 11px; - height: 19px; } - -[type="checkbox"]:checked.chk-col-red + label:before { - border-right: 2px solid #F44336; - border-bottom: 2px solid #F44336; } - -[type="checkbox"]:checked.chk-col-pink + label:before { - border-right: 2px solid #E91E63; - border-bottom: 2px solid #E91E63; } - -[type="checkbox"]:checked.chk-col-purple + label:before { - border-right: 2px solid #9C27B0; - border-bottom: 2px solid #9C27B0; } - -[type="checkbox"]:checked.chk-col-deep-purple + label:before { - border-right: 2px solid #673AB7; - border-bottom: 2px solid #673AB7; } - -[type="checkbox"]:checked.chk-col-indigo + label:before { - border-right: 2px solid #3F51B5; - border-bottom: 2px solid #3F51B5; } - -[type="checkbox"]:checked.chk-col-blue + label:before { - border-right: 2px solid #2196F3; - border-bottom: 2px solid #2196F3; } - -[type="checkbox"]:checked.chk-col-light-blue + label:before { - border-right: 2px solid #03A9F4; - border-bottom: 2px solid #03A9F4; } - -[type="checkbox"]:checked.chk-col-cyan + label:before { - border-right: 2px solid #00BCD4; - border-bottom: 2px solid #00BCD4; } - -[type="checkbox"]:checked.chk-col-teal + label:before { - border-right: 2px solid #009688; - border-bottom: 2px solid #009688; } - -[type="checkbox"]:checked.chk-col-green + label:before { - border-right: 2px solid #4CAF50; - border-bottom: 2px solid #4CAF50; } - -[type="checkbox"]:checked.chk-col-light-green + label:before { - border-right: 2px solid #8BC34A; - border-bottom: 2px solid #8BC34A; } - -[type="checkbox"]:checked.chk-col-lime + label:before { - border-right: 2px solid #CDDC39; - border-bottom: 2px solid #CDDC39; } - -[type="checkbox"]:checked.chk-col-yellow + label:before { - border-right: 2px solid #ffe821; - border-bottom: 2px solid #ffe821; } - -[type="checkbox"]:checked.chk-col-amber + label:before { - border-right: 2px solid #FFC107; - border-bottom: 2px solid #FFC107; } - -[type="checkbox"]:checked.chk-col-orange + label:before { - border-right: 2px solid #FF9800; - border-bottom: 2px solid #FF9800; } - -[type="checkbox"]:checked.chk-col-deep-orange + label:before { - border-right: 2px solid #FF5722; - border-bottom: 2px solid #FF5722; } - -[type="checkbox"]:checked.chk-col-brown + label:before { - border-right: 2px solid #795548; - border-bottom: 2px solid #795548; } - -[type="checkbox"]:checked.chk-col-grey + label:before { - border-right: 2px solid #9E9E9E; - border-bottom: 2px solid #9E9E9E; } - -[type="checkbox"]:checked.chk-col-blue-grey + label:before { - border-right: 2px solid #607D8B; - border-bottom: 2px solid #607D8B; } - -[type="checkbox"]:checked.chk-col-black + label:before { - border-right: 2px solid #000000; - border-bottom: 2px solid #000000; } - -[type="checkbox"]:checked.chk-col-white + label:before { - border-right: 2px solid #ffffff; - border-bottom: 2px solid #ffffff; } - -[type="checkbox"].filled-in:checked + label:after { - top: 0; - width: 20px; - height: 20px; - border: 2px solid #26a69a; - background-color: #26a69a; - z-index: 0; } - -[type="checkbox"].filled-in:checked + label:before { - border-right: 2px solid #fff !important; - border-bottom: 2px solid #fff !important; } - -[type="checkbox"].filled-in:checked.chk-col-red + label:after { - border: 2px solid #F44336; - background-color: #F44336; } - -[type="checkbox"].filled-in:checked.chk-col-pink + label:after { - border: 2px solid #E91E63; - background-color: #E91E63; } - -[type="checkbox"].filled-in:checked.chk-col-purple + label:after { - border: 2px solid #9C27B0; - background-color: #9C27B0; } - -[type="checkbox"].filled-in:checked.chk-col-deep-purple + label:after { - border: 2px solid #673AB7; - background-color: #673AB7; } - -[type="checkbox"].filled-in:checked.chk-col-indigo + label:after { - border: 2px solid #3F51B5; - background-color: #3F51B5; } - -[type="checkbox"].filled-in:checked.chk-col-blue + label:after { - border: 2px solid #2196F3; - background-color: #2196F3; } - -[type="checkbox"].filled-in:checked.chk-col-light-blue + label:after { - border: 2px solid #03A9F4; - background-color: #03A9F4; } - -[type="checkbox"].filled-in:checked.chk-col-cyan + label:after { - border: 2px solid #00BCD4; - background-color: #00BCD4; } - -[type="checkbox"].filled-in:checked.chk-col-teal + label:after { - border: 2px solid #009688; - background-color: #009688; } - -[type="checkbox"].filled-in:checked.chk-col-green + label:after { - border: 2px solid #4CAF50; - background-color: #4CAF50; } - -[type="checkbox"].filled-in:checked.chk-col-light-green + label:after { - border: 2px solid #8BC34A; - background-color: #8BC34A; } - -[type="checkbox"].filled-in:checked.chk-col-lime + label:after { - border: 2px solid #CDDC39; - background-color: #CDDC39; } - -[type="checkbox"].filled-in:checked.chk-col-yellow + label:after { - border: 2px solid #ffe821; - background-color: #ffe821; } - -[type="checkbox"].filled-in:checked.chk-col-amber + label:after { - border: 2px solid #FFC107; - background-color: #FFC107; } - -[type="checkbox"].filled-in:checked.chk-col-orange + label:after { - border: 2px solid #FF9800; - background-color: #FF9800; } - -[type="checkbox"].filled-in:checked.chk-col-deep-orange + label:after { - border: 2px solid #FF5722; - background-color: #FF5722; } - -[type="checkbox"].filled-in:checked.chk-col-brown + label:after { - border: 2px solid #795548; - background-color: #795548; } - -[type="checkbox"].filled-in:checked.chk-col-grey + label:after { - border: 2px solid #9E9E9E; - background-color: #9E9E9E; } - -[type="checkbox"].filled-in:checked.chk-col-blue-grey + label:after { - border: 2px solid #607D8B; - background-color: #607D8B; } - -[type="checkbox"].filled-in:checked.chk-col-black + label:after { - border: 2px solid #000000; - background-color: #000000; } - -[type="checkbox"].filled-in:checked.chk-col-white + label:after { - border: 2px solid #ffffff; - background-color: #ffffff; } - -[type="radio"]:not(:checked) + label { - padding-left: 26px; - height: 25px; - line-height: 25px; - font-size: 13px; - font-weight: normal; } - -[type="radio"]:checked + label { - padding-left: 26px; - height: 25px; - line-height: 25px; - font-size: 13px; - font-weight: normal; } - -[type="radio"].radio-col-red:checked + label:after { - background-color: #F44336; - border-color: #F44336; } - -[type="radio"].radio-col-pink:checked + label:after { - background-color: #E91E63; - border-color: #E91E63; } - -[type="radio"].radio-col-purple:checked + label:after { - background-color: #9C27B0; - border-color: #9C27B0; } - -[type="radio"].radio-col-deep-purple:checked + label:after { - background-color: #673AB7; - border-color: #673AB7; } - -[type="radio"].radio-col-indigo:checked + label:after { - background-color: #3F51B5; - border-color: #3F51B5; } - -[type="radio"].radio-col-blue:checked + label:after { - background-color: #2196F3; - border-color: #2196F3; } - -[type="radio"].radio-col-light-blue:checked + label:after { - background-color: #03A9F4; - border-color: #03A9F4; } - -[type="radio"].radio-col-cyan:checked + label:after { - background-color: #00BCD4; - border-color: #00BCD4; } - -[type="radio"].radio-col-teal:checked + label:after { - background-color: #009688; - border-color: #009688; } - -[type="radio"].radio-col-green:checked + label:after { - background-color: #4CAF50; - border-color: #4CAF50; } - -[type="radio"].radio-col-light-green:checked + label:after { - background-color: #8BC34A; - border-color: #8BC34A; } - -[type="radio"].radio-col-lime:checked + label:after { - background-color: #CDDC39; - border-color: #CDDC39; } - -[type="radio"].radio-col-yellow:checked + label:after { - background-color: #ffe821; - border-color: #ffe821; } - -[type="radio"].radio-col-amber:checked + label:after { - background-color: #FFC107; - border-color: #FFC107; } - -[type="radio"].radio-col-orange:checked + label:after { - background-color: #FF9800; - border-color: #FF9800; } - -[type="radio"].radio-col-deep-orange:checked + label:after { - background-color: #FF5722; - border-color: #FF5722; } - -[type="radio"].radio-col-brown:checked + label:after { - background-color: #795548; - border-color: #795548; } - -[type="radio"].radio-col-grey:checked + label:after { - background-color: #9E9E9E; - border-color: #9E9E9E; } - -[type="radio"].radio-col-blue-grey:checked + label:after { - background-color: #607D8B; - border-color: #607D8B; } - -[type="radio"].radio-col-black:checked + label:after { - background-color: #000000; - border-color: #000000; } - -[type="radio"].radio-col-white:checked + label:after { - background-color: #ffffff; - border-color: #ffffff; } - -[type="radio"].with-gap.radio-col-red:checked + label:before { - border: 2px solid #F44336; } - -[type="radio"].with-gap.radio-col-red:checked + label:after { - background-color: #F44336; - border: 2px solid #F44336; } - -[type="radio"].with-gap.radio-col-pink:checked + label:before { - border: 2px solid #E91E63; } - -[type="radio"].with-gap.radio-col-pink:checked + label:after { - background-color: #E91E63; - border: 2px solid #E91E63; } - -[type="radio"].with-gap.radio-col-purple:checked + label:before { - border: 2px solid #9C27B0; } - -[type="radio"].with-gap.radio-col-purple:checked + label:after { - background-color: #9C27B0; - border: 2px solid #9C27B0; } - -[type="radio"].with-gap.radio-col-deep-purple:checked + label:before { - border: 2px solid #673AB7; } - -[type="radio"].with-gap.radio-col-deep-purple:checked + label:after { - background-color: #673AB7; - border: 2px solid #673AB7; } - -[type="radio"].with-gap.radio-col-indigo:checked + label:before { - border: 2px solid #3F51B5; } - -[type="radio"].with-gap.radio-col-indigo:checked + label:after { - background-color: #3F51B5; - border: 2px solid #3F51B5; } - -[type="radio"].with-gap.radio-col-blue:checked + label:before { - border: 2px solid #2196F3; } - -[type="radio"].with-gap.radio-col-blue:checked + label:after { - background-color: #2196F3; - border: 2px solid #2196F3; } - -[type="radio"].with-gap.radio-col-light-blue:checked + label:before { - border: 2px solid #03A9F4; } - -[type="radio"].with-gap.radio-col-light-blue:checked + label:after { - background-color: #03A9F4; - border: 2px solid #03A9F4; } - -[type="radio"].with-gap.radio-col-cyan:checked + label:before { - border: 2px solid #00BCD4; } - -[type="radio"].with-gap.radio-col-cyan:checked + label:after { - background-color: #00BCD4; - border: 2px solid #00BCD4; } - -[type="radio"].with-gap.radio-col-teal:checked + label:before { - border: 2px solid #009688; } - -[type="radio"].with-gap.radio-col-teal:checked + label:after { - background-color: #009688; - border: 2px solid #009688; } - -[type="radio"].with-gap.radio-col-green:checked + label:before { - border: 2px solid #4CAF50; } - -[type="radio"].with-gap.radio-col-green:checked + label:after { - background-color: #4CAF50; - border: 2px solid #4CAF50; } - -[type="radio"].with-gap.radio-col-light-green:checked + label:before { - border: 2px solid #8BC34A; } - -[type="radio"].with-gap.radio-col-light-green:checked + label:after { - background-color: #8BC34A; - border: 2px solid #8BC34A; } - -[type="radio"].with-gap.radio-col-lime:checked + label:before { - border: 2px solid #CDDC39; } - -[type="radio"].with-gap.radio-col-lime:checked + label:after { - background-color: #CDDC39; - border: 2px solid #CDDC39; } - -[type="radio"].with-gap.radio-col-yellow:checked + label:before { - border: 2px solid #ffe821; } - -[type="radio"].with-gap.radio-col-yellow:checked + label:after { - background-color: #ffe821; - border: 2px solid #ffe821; } - -[type="radio"].with-gap.radio-col-amber:checked + label:before { - border: 2px solid #FFC107; } - -[type="radio"].with-gap.radio-col-amber:checked + label:after { - background-color: #FFC107; - border: 2px solid #FFC107; } - -[type="radio"].with-gap.radio-col-orange:checked + label:before { - border: 2px solid #FF9800; } - -[type="radio"].with-gap.radio-col-orange:checked + label:after { - background-color: #FF9800; - border: 2px solid #FF9800; } - -[type="radio"].with-gap.radio-col-deep-orange:checked + label:before { - border: 2px solid #FF5722; } - -[type="radio"].with-gap.radio-col-deep-orange:checked + label:after { - background-color: #FF5722; - border: 2px solid #FF5722; } - -[type="radio"].with-gap.radio-col-brown:checked + label:before { - border: 2px solid #795548; } - -[type="radio"].with-gap.radio-col-brown:checked + label:after { - background-color: #795548; - border: 2px solid #795548; } - -[type="radio"].with-gap.radio-col-grey:checked + label:before { - border: 2px solid #9E9E9E; } - -[type="radio"].with-gap.radio-col-grey:checked + label:after { - background-color: #9E9E9E; - border: 2px solid #9E9E9E; } - -[type="radio"].with-gap.radio-col-blue-grey:checked + label:before { - border: 2px solid #607D8B; } - -[type="radio"].with-gap.radio-col-blue-grey:checked + label:after { - background-color: #607D8B; - border: 2px solid #607D8B; } - -[type="radio"].with-gap.radio-col-black:checked + label:before { - border: 2px solid #000000; } - -[type="radio"].with-gap.radio-col-black:checked + label:after { - background-color: #000000; - border: 2px solid #000000; } - -[type="radio"].with-gap.radio-col-white:checked + label:before { - border: 2px solid #ffffff; } - -[type="radio"].with-gap.radio-col-white:checked + label:after { - background-color: #ffffff; - border: 2px solid #ffffff; } - -/* Switch ====================================== */ -.switch label { - font-weight: normal; - font-size: 13px; } - .switch label .lever { - margin: 0 14px; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-red:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(244, 67, 54, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-red { - background-color: rgba(244, 67, 54, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-red:after { - background-color: #F44336; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-pink:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(233, 30, 99, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-pink { - background-color: rgba(233, 30, 99, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-pink:after { - background-color: #E91E63; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-purple:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(156, 39, 176, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-purple { - background-color: rgba(156, 39, 176, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-purple:after { - background-color: #9C27B0; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-deep-purple:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(103, 58, 183, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-deep-purple { - background-color: rgba(103, 58, 183, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-deep-purple:after { - background-color: #673AB7; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-indigo:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(63, 81, 181, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-indigo { - background-color: rgba(63, 81, 181, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-indigo:after { - background-color: #3F51B5; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-blue:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(33, 150, 243, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-blue { - background-color: rgba(33, 150, 243, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-blue:after { - background-color: #2196F3; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-light-blue:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(3, 169, 244, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-light-blue { - background-color: rgba(3, 169, 244, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-light-blue:after { - background-color: #03A9F4; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-cyan:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(0, 188, 212, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-cyan { - background-color: rgba(0, 188, 212, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-cyan:after { - background-color: #00BCD4; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-teal:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(0, 150, 136, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-teal { - background-color: rgba(0, 150, 136, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-teal:after { - background-color: #009688; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-green:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(76, 175, 80, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-green { - background-color: rgba(76, 175, 80, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-green:after { - background-color: #4CAF50; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-light-green:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(139, 195, 74, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-light-green { - background-color: rgba(139, 195, 74, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-light-green:after { - background-color: #8BC34A; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-lime:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(205, 220, 57, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-lime { - background-color: rgba(205, 220, 57, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-lime:after { - background-color: #CDDC39; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-yellow:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(255, 232, 33, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-yellow { - background-color: rgba(255, 232, 33, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-yellow:after { - background-color: #ffe821; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-amber:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(255, 193, 7, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-amber { - background-color: rgba(255, 193, 7, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-amber:after { - background-color: #FFC107; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-orange:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(255, 152, 0, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-orange { - background-color: rgba(255, 152, 0, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-orange:after { - background-color: #FF9800; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-deep-orange:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(255, 87, 34, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-deep-orange { - background-color: rgba(255, 87, 34, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-deep-orange:after { - background-color: #FF5722; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-brown:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(121, 85, 72, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-brown { - background-color: rgba(121, 85, 72, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-brown:after { - background-color: #795548; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-grey:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(158, 158, 158, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-grey { - background-color: rgba(158, 158, 158, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-grey:after { - background-color: #9E9E9E; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-blue-grey:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(96, 125, 139, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-blue-grey { - background-color: rgba(96, 125, 139, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-blue-grey:after { - background-color: #607D8B; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-black:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(0, 0, 0, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-black { - background-color: rgba(0, 0, 0, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-black:after { - background-color: #000000; } - .switch label input[type=checkbox]:checked:not(:disabled) ~ .lever.switch-col-white:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba(255, 255, 255, 0.1); } - .switch label input[type=checkbox]:checked + .lever.switch-col-white { - background-color: rgba(255, 255, 255, 0.5); } - .switch label input[type=checkbox]:checked + .lever.switch-col-white:after { - background-color: #ffffff; } - -/* DateTime Picker ============================= */ -.dtp div.dtp-date, -.dtp div.dtp-time { - background: #007d72; } - -.dtp > .dtp-content > .dtp-date-view > header.dtp-header { - background: #009688; } - -.dtp .dtp-buttons .dtp-btn-ok { - margin-left: 10px; } - -.dtp .dtp-buttons .dtp-btn-clear { - margin-right: 10px !important; } - -.dtp .p10 > a { - color: #fff; } - -.dtp div.dtp-actual-year { - font-size: 1.5em; - color: #ffffff; } - -.dtp table.dtp-picker-days tr td a.selected { - background: #007d72; - color: #fff; } - -/* Bootstrap Select ============================ */ -.bootstrap-select { - box-shadow: none !important; - border-bottom: 1px solid #ddd !important; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .bootstrap-select .dropdown-toggle:focus, .bootstrap-select .dropdown-toggle:active { - outline: none !important; } - .bootstrap-select .bs-searchbox, - .bootstrap-select .bs-actionsbox, - .bootstrap-select .bs-donebutton { - padding: 0 0 5px 0; - border-bottom: 1px solid #e9e9e9; } - .bootstrap-select .bs-searchbox .form-control, - .bootstrap-select .bs-actionsbox .form-control, - .bootstrap-select .bs-donebutton .form-control { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none !important; - -moz-box-shadow: none !important; - -ms-box-shadow: none !important; - box-shadow: none !important; - border: none; - margin-left: 30px; } - .bootstrap-select .bs-searchbox { - position: relative; } - .bootstrap-select .bs-searchbox:after { - content: '\E8B6'; - font-family: 'Material Icons'; - position: absolute; - top: 0; - left: 10px; - font-size: 25px; } - .bootstrap-select ul.dropdown-menu { - margin-top: 0 !important; } - .bootstrap-select .dropdown-menu li.selected a { - background-color: #eee !important; - color: #555 !important; } - .bootstrap-select .dropdown-menu .active a { - background-color: transparent; - color: #333 !important; } - .bootstrap-select .dropdown-menu .notify { - background-color: #F44336 !important; - color: #fff !important; - border: none !important; } - -.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark { - margin-top: 9px; } - -/* Tooltip & Popovers ========================== */ -.tooltip { - font-size: 13px; } - .tooltip .tooltip-inner { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - -.popover { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - border: 1px solid rgba(0, 0, 0, 0.08); } - .popover .popover-title { - font-weight: bold; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - background-color: #e9e9e9; - border-bottom: 1px solid #ddd; } - .popover .popover-content { - font-size: 13px; - color: #777; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - -/* Nav Tabs ==================================== */ -.nav-tabs { - border-bottom: 2px solid #eee; } - .nav-tabs > li { - position: relative; - top: 3px; - left: -2px; } - .nav-tabs > li > a { - border: none !important; - color: #999 !important; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .nav-tabs > li > a:hover, .nav-tabs > li > a:active, .nav-tabs > li > a:focus { - background-color: transparent !important; } - .nav-tabs > li > a:before { - content: ''; - position: absolute; - left: 0; - width: 100%; - height: 0; - border-bottom: 2px solid #2196F3; - bottom: 2px; - -moz-transform: scaleX(0); - -ms-transform: scaleX(0); - -o-transform: scaleX(0); - -webkit-transform: scaleX(0); - transform: scaleX(0); - -moz-transition: 0.1s ease-in; - -o-transition: 0.1s ease-in; - -webkit-transition: 0.1s ease-in; - transition: 0.1s ease-in; } - .nav-tabs > li > a .material-icons { - position: relative; - top: 7px; - margin-bottom: 8px; } - .nav-tabs li.active a { - color: #222 !important; } - .nav-tabs li.active a:hover, .nav-tabs li.active a:active, .nav-tabs li.active a:focus { - background-color: transparent !important; } - .nav-tabs li.active a:before { - -moz-transform: scaleX(1); - -ms-transform: scaleX(1); - -o-transform: scaleX(1); - -webkit-transform: scaleX(1); - transform: scaleX(1); } - .nav-tabs + .tab-content { - padding: 15px 0; } - -.nav-tabs.tab-col-red > li > a:before { - border-bottom: 2px solid #F44336; } - -.nav-tabs.tab-col-pink > li > a:before { - border-bottom: 2px solid #E91E63; } - -.nav-tabs.tab-col-purple > li > a:before { - border-bottom: 2px solid #9C27B0; } - -.nav-tabs.tab-col-deep-purple > li > a:before { - border-bottom: 2px solid #673AB7; } - -.nav-tabs.tab-col-indigo > li > a:before { - border-bottom: 2px solid #3F51B5; } - -.nav-tabs.tab-col-blue > li > a:before { - border-bottom: 2px solid #2196F3; } - -.nav-tabs.tab-col-light-blue > li > a:before { - border-bottom: 2px solid #03A9F4; } - -.nav-tabs.tab-col-cyan > li > a:before { - border-bottom: 2px solid #00BCD4; } - -.nav-tabs.tab-col-teal > li > a:before { - border-bottom: 2px solid #009688; } - -.nav-tabs.tab-col-green > li > a:before { - border-bottom: 2px solid #4CAF50; } - -.nav-tabs.tab-col-light-green > li > a:before { - border-bottom: 2px solid #8BC34A; } - -.nav-tabs.tab-col-lime > li > a:before { - border-bottom: 2px solid #CDDC39; } - -.nav-tabs.tab-col-yellow > li > a:before { - border-bottom: 2px solid #ffe821; } - -.nav-tabs.tab-col-amber > li > a:before { - border-bottom: 2px solid #FFC107; } - -.nav-tabs.tab-col-orange > li > a:before { - border-bottom: 2px solid #FF9800; } - -.nav-tabs.tab-col-deep-orange > li > a:before { - border-bottom: 2px solid #FF5722; } - -.nav-tabs.tab-col-brown > li > a:before { - border-bottom: 2px solid #795548; } - -.nav-tabs.tab-col-grey > li > a:before { - border-bottom: 2px solid #9E9E9E; } - -.nav-tabs.tab-col-blue-grey > li > a:before { - border-bottom: 2px solid #607D8B; } - -.nav-tabs.tab-col-black > li > a:before { - border-bottom: 2px solid #000000; } - -.nav-tabs.tab-col-white > li > a:before { - border-bottom: 2px solid #ffffff; } - -/* Thumbnails ================================== */ -.thumbnail { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .thumbnail p:not(button) { - color: #999999; - font-size: 14px; } - .thumbnail h3 { - font-weight: bold; - font-size: 17px; } - -/* Modals ====================================== */ -.modal .modal-header { - border: none; - padding: 25px 25px 5px 25px; } - .modal .modal-header .modal-title { - font-weight: bold; - font-size: 16px; } - -.modal .modal-content { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - box-shadow: 0 5px 20px rgba(0, 0, 0, 0.31) !important; - border: none; } - .modal .modal-content .modal-body { - color: #777; - padding: 15px 25px; } - -.modal .modal-footer { - border: none; } - -.modal-col-red { - background-color: #F44336; } - .modal-col-red .modal-body, - .modal-col-red .modal-title { - color: #fff !important; } - .modal-col-red .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-red .modal-footer .btn-link { - color: #fff !important; } - .modal-col-red .modal-footer .btn-link:hover, .modal-col-red .modal-footer .btn-link:active, .modal-col-red .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-pink { - background-color: #E91E63; } - .modal-col-pink .modal-body, - .modal-col-pink .modal-title { - color: #fff !important; } - .modal-col-pink .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-pink .modal-footer .btn-link { - color: #fff !important; } - .modal-col-pink .modal-footer .btn-link:hover, .modal-col-pink .modal-footer .btn-link:active, .modal-col-pink .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-purple { - background-color: #9C27B0; } - .modal-col-purple .modal-body, - .modal-col-purple .modal-title { - color: #fff !important; } - .modal-col-purple .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-purple .modal-footer .btn-link { - color: #fff !important; } - .modal-col-purple .modal-footer .btn-link:hover, .modal-col-purple .modal-footer .btn-link:active, .modal-col-purple .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-deep-purple { - background-color: #673AB7; } - .modal-col-deep-purple .modal-body, - .modal-col-deep-purple .modal-title { - color: #fff !important; } - .modal-col-deep-purple .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-deep-purple .modal-footer .btn-link { - color: #fff !important; } - .modal-col-deep-purple .modal-footer .btn-link:hover, .modal-col-deep-purple .modal-footer .btn-link:active, .modal-col-deep-purple .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-indigo { - background-color: #3F51B5; } - .modal-col-indigo .modal-body, - .modal-col-indigo .modal-title { - color: #fff !important; } - .modal-col-indigo .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-indigo .modal-footer .btn-link { - color: #fff !important; } - .modal-col-indigo .modal-footer .btn-link:hover, .modal-col-indigo .modal-footer .btn-link:active, .modal-col-indigo .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-blue { - background-color: #2196F3; } - .modal-col-blue .modal-body, - .modal-col-blue .modal-title { - color: #fff !important; } - .modal-col-blue .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-blue .modal-footer .btn-link { - color: #fff !important; } - .modal-col-blue .modal-footer .btn-link:hover, .modal-col-blue .modal-footer .btn-link:active, .modal-col-blue .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-light-blue { - background-color: #03A9F4; } - .modal-col-light-blue .modal-body, - .modal-col-light-blue .modal-title { - color: #fff !important; } - .modal-col-light-blue .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-light-blue .modal-footer .btn-link { - color: #fff !important; } - .modal-col-light-blue .modal-footer .btn-link:hover, .modal-col-light-blue .modal-footer .btn-link:active, .modal-col-light-blue .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-cyan { - background-color: #00BCD4; } - .modal-col-cyan .modal-body, - .modal-col-cyan .modal-title { - color: #fff !important; } - .modal-col-cyan .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-cyan .modal-footer .btn-link { - color: #fff !important; } - .modal-col-cyan .modal-footer .btn-link:hover, .modal-col-cyan .modal-footer .btn-link:active, .modal-col-cyan .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-teal { - background-color: #009688; } - .modal-col-teal .modal-body, - .modal-col-teal .modal-title { - color: #fff !important; } - .modal-col-teal .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-teal .modal-footer .btn-link { - color: #fff !important; } - .modal-col-teal .modal-footer .btn-link:hover, .modal-col-teal .modal-footer .btn-link:active, .modal-col-teal .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-green { - background-color: #4CAF50; } - .modal-col-green .modal-body, - .modal-col-green .modal-title { - color: #fff !important; } - .modal-col-green .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-green .modal-footer .btn-link { - color: #fff !important; } - .modal-col-green .modal-footer .btn-link:hover, .modal-col-green .modal-footer .btn-link:active, .modal-col-green .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-light-green { - background-color: #8BC34A; } - .modal-col-light-green .modal-body, - .modal-col-light-green .modal-title { - color: #fff !important; } - .modal-col-light-green .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-light-green .modal-footer .btn-link { - color: #fff !important; } - .modal-col-light-green .modal-footer .btn-link:hover, .modal-col-light-green .modal-footer .btn-link:active, .modal-col-light-green .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-lime { - background-color: #CDDC39; } - .modal-col-lime .modal-body, - .modal-col-lime .modal-title { - color: #fff !important; } - .modal-col-lime .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-lime .modal-footer .btn-link { - color: #fff !important; } - .modal-col-lime .modal-footer .btn-link:hover, .modal-col-lime .modal-footer .btn-link:active, .modal-col-lime .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-yellow { - background-color: #ffe821; } - .modal-col-yellow .modal-body, - .modal-col-yellow .modal-title { - color: #fff !important; } - .modal-col-yellow .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-yellow .modal-footer .btn-link { - color: #fff !important; } - .modal-col-yellow .modal-footer .btn-link:hover, .modal-col-yellow .modal-footer .btn-link:active, .modal-col-yellow .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-amber { - background-color: #FFC107; } - .modal-col-amber .modal-body, - .modal-col-amber .modal-title { - color: #fff !important; } - .modal-col-amber .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-amber .modal-footer .btn-link { - color: #fff !important; } - .modal-col-amber .modal-footer .btn-link:hover, .modal-col-amber .modal-footer .btn-link:active, .modal-col-amber .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-orange { - background-color: #FF9800; } - .modal-col-orange .modal-body, - .modal-col-orange .modal-title { - color: #fff !important; } - .modal-col-orange .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-orange .modal-footer .btn-link { - color: #fff !important; } - .modal-col-orange .modal-footer .btn-link:hover, .modal-col-orange .modal-footer .btn-link:active, .modal-col-orange .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-deep-orange { - background-color: #FF5722; } - .modal-col-deep-orange .modal-body, - .modal-col-deep-orange .modal-title { - color: #fff !important; } - .modal-col-deep-orange .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-deep-orange .modal-footer .btn-link { - color: #fff !important; } - .modal-col-deep-orange .modal-footer .btn-link:hover, .modal-col-deep-orange .modal-footer .btn-link:active, .modal-col-deep-orange .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-brown { - background-color: #795548; } - .modal-col-brown .modal-body, - .modal-col-brown .modal-title { - color: #fff !important; } - .modal-col-brown .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-brown .modal-footer .btn-link { - color: #fff !important; } - .modal-col-brown .modal-footer .btn-link:hover, .modal-col-brown .modal-footer .btn-link:active, .modal-col-brown .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-grey { - background-color: #9E9E9E; } - .modal-col-grey .modal-body, - .modal-col-grey .modal-title { - color: #fff !important; } - .modal-col-grey .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-grey .modal-footer .btn-link { - color: #fff !important; } - .modal-col-grey .modal-footer .btn-link:hover, .modal-col-grey .modal-footer .btn-link:active, .modal-col-grey .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-blue-grey { - background-color: #607D8B; } - .modal-col-blue-grey .modal-body, - .modal-col-blue-grey .modal-title { - color: #fff !important; } - .modal-col-blue-grey .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-blue-grey .modal-footer .btn-link { - color: #fff !important; } - .modal-col-blue-grey .modal-footer .btn-link:hover, .modal-col-blue-grey .modal-footer .btn-link:active, .modal-col-blue-grey .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-black { - background-color: #000000; } - .modal-col-black .modal-body, - .modal-col-black .modal-title { - color: #fff !important; } - .modal-col-black .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-black .modal-footer .btn-link { - color: #fff !important; } - .modal-col-black .modal-footer .btn-link:hover, .modal-col-black .modal-footer .btn-link:active, .modal-col-black .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -.modal-col-white { - background-color: #ffffff; } - .modal-col-white .modal-body, - .modal-col-white .modal-title { - color: #fff !important; } - .modal-col-white .modal-footer { - background-color: rgba(0, 0, 0, 0.12); } - .modal-col-white .modal-footer .btn-link { - color: #fff !important; } - .modal-col-white .modal-footer .btn-link:hover, .modal-col-white .modal-footer .btn-link:active, .modal-col-white .modal-footer .btn-link:focus { - background-color: rgba(0, 0, 0, 0.12); } - -/* Labels ====================================== */ -.label { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - -.label-primary { - background-color: #1f91f3; } - -.label-success { - background-color: #2b982b; } - -.label-info { - background-color: #00b0e4; } - -.label-warning { - background-color: #ff9600; } - -.label-danger { - background-color: #fb483a; } - -/* Collapse ==================================== */ -.collapse .well, -.collapse.in .well, -.collapsing .well { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - margin-bottom: 0; } - -/* Tables ====================================== */ -.table tbody tr td, .table tbody tr th { - padding: 10px; - border-top: 1px solid #eee; - border-bottom: 1px solid #eee; } - -.table tbody tr.primary td, .table tbody tr.primary th { - background-color: #1f91f3; - color: #fff; } - -.table tbody tr.success td, .table tbody tr.success th { - background-color: #2b982b; - color: #fff; } - -.table tbody tr.info td, .table tbody tr.info th { - background-color: #00b0e4; - color: #fff; } - -.table tbody tr.warning td, .table tbody tr.warning th { - background-color: #ff9600; - color: #fff; } - -.table tbody tr.danger td, .table tbody tr.danger th { - background-color: #fb483a; - color: #fff; } - -.table thead tr th { - padding: 10px; - border-bottom: 1px solid #eee; } - -.table-bordered { - border-top: 1px solid #eee; } - .table-bordered tbody tr td, .table-bordered tbody tr th { - padding: 10px; - border: 1px solid #eee; } - .table-bordered thead tr th { - padding: 10px; - border: 1px solid #eee; } - -/* Panel ======================================= */ -.panel-group .panel-col-red { - border: 1px solid #F44336; } - .panel-group .panel-col-red .panel-title { - background-color: #F44336 !important; - color: #fff; } - .panel-group .panel-col-red .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-pink { - border: 1px solid #E91E63; } - .panel-group .panel-col-pink .panel-title { - background-color: #E91E63 !important; - color: #fff; } - .panel-group .panel-col-pink .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-purple { - border: 1px solid #9C27B0; } - .panel-group .panel-col-purple .panel-title { - background-color: #9C27B0 !important; - color: #fff; } - .panel-group .panel-col-purple .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-deep-purple { - border: 1px solid #673AB7; } - .panel-group .panel-col-deep-purple .panel-title { - background-color: #673AB7 !important; - color: #fff; } - .panel-group .panel-col-deep-purple .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-indigo { - border: 1px solid #3F51B5; } - .panel-group .panel-col-indigo .panel-title { - background-color: #3F51B5 !important; - color: #fff; } - .panel-group .panel-col-indigo .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-blue { - border: 1px solid #2196F3; } - .panel-group .panel-col-blue .panel-title { - background-color: #2196F3 !important; - color: #fff; } - .panel-group .panel-col-blue .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-light-blue { - border: 1px solid #03A9F4; } - .panel-group .panel-col-light-blue .panel-title { - background-color: #03A9F4 !important; - color: #fff; } - .panel-group .panel-col-light-blue .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-cyan { - border: 1px solid #00BCD4; } - .panel-group .panel-col-cyan .panel-title { - background-color: #00BCD4 !important; - color: #fff; } - .panel-group .panel-col-cyan .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-teal { - border: 1px solid #009688; } - .panel-group .panel-col-teal .panel-title { - background-color: #009688 !important; - color: #fff; } - .panel-group .panel-col-teal .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-green { - border: 1px solid #4CAF50; } - .panel-group .panel-col-green .panel-title { - background-color: #4CAF50 !important; - color: #fff; } - .panel-group .panel-col-green .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-light-green { - border: 1px solid #8BC34A; } - .panel-group .panel-col-light-green .panel-title { - background-color: #8BC34A !important; - color: #fff; } - .panel-group .panel-col-light-green .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-lime { - border: 1px solid #CDDC39; } - .panel-group .panel-col-lime .panel-title { - background-color: #CDDC39 !important; - color: #fff; } - .panel-group .panel-col-lime .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-yellow { - border: 1px solid #ffe821; } - .panel-group .panel-col-yellow .panel-title { - background-color: #ffe821 !important; - color: #fff; } - .panel-group .panel-col-yellow .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-amber { - border: 1px solid #FFC107; } - .panel-group .panel-col-amber .panel-title { - background-color: #FFC107 !important; - color: #fff; } - .panel-group .panel-col-amber .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-orange { - border: 1px solid #FF9800; } - .panel-group .panel-col-orange .panel-title { - background-color: #FF9800 !important; - color: #fff; } - .panel-group .panel-col-orange .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-deep-orange { - border: 1px solid #FF5722; } - .panel-group .panel-col-deep-orange .panel-title { - background-color: #FF5722 !important; - color: #fff; } - .panel-group .panel-col-deep-orange .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-brown { - border: 1px solid #795548; } - .panel-group .panel-col-brown .panel-title { - background-color: #795548 !important; - color: #fff; } - .panel-group .panel-col-brown .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-grey { - border: 1px solid #9E9E9E; } - .panel-group .panel-col-grey .panel-title { - background-color: #9E9E9E !important; - color: #fff; } - .panel-group .panel-col-grey .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-blue-grey { - border: 1px solid #607D8B; } - .panel-group .panel-col-blue-grey .panel-title { - background-color: #607D8B !important; - color: #fff; } - .panel-group .panel-col-blue-grey .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-black { - border: 1px solid #000000; } - .panel-group .panel-col-black .panel-title { - background-color: #000000 !important; - color: #fff; } - .panel-group .panel-col-black .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel-col-white { - border: 1px solid #ffffff; } - .panel-group .panel-col-white .panel-title { - background-color: #ffffff !important; - color: #fff; } - .panel-group .panel-col-white .panel-body { - border-top-color: transparent !important; } - -.panel-group .panel { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .panel-group .panel .panel-title .material-icons { - float: left; - line-height: 16px; - margin-right: 8px; } - .panel-group .panel .panel-heading { - padding: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .panel-group .panel .panel-heading a { - display: block; - padding: 10px 15px; } - .panel-group .panel .panel-heading a:hover, .panel-group .panel .panel-heading a:focus, .panel-group .panel .panel-heading a:active { - text-decoration: none; } - .panel-group .panel .panel-body { - color: #555; } - -.panel-group .panel-primary { - border: 1px solid #1f91f3; } - .panel-group .panel-primary .panel-title { - background-color: #1f91f3; } - -.panel-group .panel-success { - border: 1px solid #2b982b; } - .panel-group .panel-success .panel-title { - background-color: #2b982b; - color: #fff; } - -.panel-group .panel-warning { - border: 1px solid #ff9600; } - .panel-group .panel-warning .panel-title { - background-color: #ff9600; - color: #fff; } - -.panel-group .panel-danger { - border: 1px solid #fb483a; } - .panel-group .panel-danger .panel-title { - background-color: #fb483a; - color: #fff; } - -.full-body .panel-col-red .panel-body { - border-top-color: #fff !important; - background-color: #F44336; - color: #fff; } - -.full-body .panel-col-pink .panel-body { - border-top-color: #fff !important; - background-color: #E91E63; - color: #fff; } - -.full-body .panel-col-purple .panel-body { - border-top-color: #fff !important; - background-color: #9C27B0; - color: #fff; } - -.full-body .panel-col-deep-purple .panel-body { - border-top-color: #fff !important; - background-color: #673AB7; - color: #fff; } - -.full-body .panel-col-indigo .panel-body { - border-top-color: #fff !important; - background-color: #3F51B5; - color: #fff; } - -.full-body .panel-col-blue .panel-body { - border-top-color: #fff !important; - background-color: #2196F3; - color: #fff; } - -.full-body .panel-col-light-blue .panel-body { - border-top-color: #fff !important; - background-color: #03A9F4; - color: #fff; } - -.full-body .panel-col-cyan .panel-body { - border-top-color: #fff !important; - background-color: #00BCD4; - color: #fff; } - -.full-body .panel-col-teal .panel-body { - border-top-color: #fff !important; - background-color: #009688; - color: #fff; } - -.full-body .panel-col-green .panel-body { - border-top-color: #fff !important; - background-color: #4CAF50; - color: #fff; } - -.full-body .panel-col-light-green .panel-body { - border-top-color: #fff !important; - background-color: #8BC34A; - color: #fff; } - -.full-body .panel-col-lime .panel-body { - border-top-color: #fff !important; - background-color: #CDDC39; - color: #fff; } - -.full-body .panel-col-yellow .panel-body { - border-top-color: #fff !important; - background-color: #ffe821; - color: #fff; } - -.full-body .panel-col-amber .panel-body { - border-top-color: #fff !important; - background-color: #FFC107; - color: #fff; } - -.full-body .panel-col-orange .panel-body { - border-top-color: #fff !important; - background-color: #FF9800; - color: #fff; } - -.full-body .panel-col-deep-orange .panel-body { - border-top-color: #fff !important; - background-color: #FF5722; - color: #fff; } - -.full-body .panel-col-brown .panel-body { - border-top-color: #fff !important; - background-color: #795548; - color: #fff; } - -.full-body .panel-col-grey .panel-body { - border-top-color: #fff !important; - background-color: #9E9E9E; - color: #fff; } - -.full-body .panel-col-blue-grey .panel-body { - border-top-color: #fff !important; - background-color: #607D8B; - color: #fff; } - -.full-body .panel-col-black .panel-body { - border-top-color: #fff !important; - background-color: #000000; - color: #fff; } - -.full-body .panel-col-white .panel-body { - border-top-color: #fff !important; - background-color: #ffffff; - color: #fff; } - -.full-body .panel-primary .panel-body { - border-top-color: #fff !important; - background-color: #1f91f3; - color: #fff; } - -.full-body .panel-success .panel-body { - border-top-color: #fff !important; - background-color: #2b982b; - color: #fff; } - -.full-body .panel-warning .panel-body { - border-top-color: #fff !important; - background-color: #ff9600; - color: #fff; } - -.full-body .panel-danger .panel-body { - border-top-color: #fff !important; - background-color: #fb483a; - color: #fff; } - -/* Progress Bars =============================== */ -.progress { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - height: 22px; } - .progress .progress-bar { - line-height: 23px; - background-color: #1f91f3; } - .progress .progress-bar-success { - background-color: #2b982b; } - .progress .progress-bar-info { - background-color: #00b0e4; } - .progress .progress-bar-warning { - background-color: #ff9600; } - .progress .progress-bar-danger { - background-color: #fb483a; } - -/* Ion Range Slider ============================ */ -.irs .irs-min, -.irs .irs-max, -.irs .irs-from, -.irs .irs-to, -.irs .irs-single { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - -/* Input Group ================================= */ -.input-group { - width: 100%; - margin-bottom: 20px; } - .input-group .form-line { - display: inline-block; - width: 100%; - border-bottom: 1px solid #ddd; - position: relative; } - .input-group .form-line:after { - content: ''; - position: absolute; - left: 0; - width: 100%; - bottom: -2px; - -moz-transform: scaleX(0); - -ms-transform: scaleX(0); - -o-transform: scaleX(0); - -webkit-transform: scaleX(0); - transform: scaleX(0); - -moz-transition: 0.25s ease-in; - -o-transition: 0.25s ease-in; - -webkit-transition: 0.25s ease-in; - transition: 0.25s ease-in; - border-bottom: 2px solid #1f91f3; } - .input-group .form-line + .input-group-addon { - padding-right: 0; - padding-left: 10px; } - .input-group .help-info { - float: right; - font-size: 12px; - margin-top: 5px; - color: #999; } - .input-group label.error { - font-size: 12px; - display: block; - margin-top: 5px; - font-weight: normal; - color: #F44336; } - .input-group .form-line.error:after { - border-bottom: 2px solid #F44336; } - .input-group .form-line.success:after { - border-bottom: 2px solid #4CAF50; } - .input-group .form-line.warning:after { - border-bottom: 2px solid #FFC107; } - .input-group .form-line.focused:after { - -moz-transform: scaleX(1); - -ms-transform: scaleX(1); - -o-transform: scaleX(1); - -webkit-transform: scaleX(1); - transform: scaleX(1); } - .input-group .form-line.focused .form-label { - bottom: 25px; - left: 0; - font-size: 12px; } - .input-group .input-group-addon { - border: none; - background-color: transparent; - padding-left: 0; - font-weight: bold; } - .input-group .input-group-addon .material-icons { - font-size: 18px; - color: #555; } - .input-group input[type="text"], - .input-group .form-control { - border: none; - box-shadow: none; - padding-left: 0; } - .input-group .form-control:focus { - -webkit-box-shadow: none !important; - -moz-box-shadow: none !important; - -ms-box-shadow: none !important; - box-shadow: none !important; } - -.input-group.input-group-sm .input-group-addon i { - font-size: 14px; } - -.input-group.input-group-sm .form-control { - font-size: 12px; } - -.input-group.input-group-lg .input-group-addon i { - font-size: 26px; } - -.input-group.input-group-lg .form-control { - font-size: 18px; } - -.form-control-label { - text-align: right; } - .form-control-label label { - margin-top: 8px; } - -.form-horizontal .form-group { - margin-bottom: 0; } - -.form-group { - width: 100%; - margin-bottom: 25px; } - .form-group .form-control { - width: 100%; - border: none; - box-shadow: none; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - padding-left: 0; } - .form-group .help-info { - float: right; - font-size: 12px; - margin-top: 5px; - color: #999; } - .form-group label.error { - font-size: 12px; - display: block; - margin-top: 5px; - font-weight: normal; - color: #F44336; } - .form-group .form-line { - width: 100%; - position: relative; - border-bottom: 1px solid #ddd; } - .form-group .form-line:after { - content: ''; - position: absolute; - left: 0; - width: 100%; - height: 0; - bottom: -1px; - -moz-transform: scaleX(0); - -ms-transform: scaleX(0); - -o-transform: scaleX(0); - -webkit-transform: scaleX(0); - transform: scaleX(0); - -moz-transition: 0.25s ease-in; - -o-transition: 0.25s ease-in; - -webkit-transition: 0.25s ease-in; - transition: 0.25s ease-in; - border-bottom: 2px solid #1f91f3; } - .form-group .form-line .form-label { - font-weight: normal; - color: #aaa; - position: absolute; - top: 10px; - left: 0; - cursor: text; - -moz-transition: 0.2s; - -o-transition: 0.2s; - -webkit-transition: 0.2s; - transition: 0.2s; } - .form-group .form-line.error:after { - border-bottom: 2px solid #F44336; } - .form-group .form-line.success:after { - border-bottom: 2px solid #4CAF50; } - .form-group .form-line.warning:after { - border-bottom: 2px solid #FFC107; } - .form-group .form-line.focused:after { - -moz-transform: scaleX(1); - -ms-transform: scaleX(1); - -o-transform: scaleX(1); - -webkit-transform: scaleX(1); - transform: scaleX(1); } - .form-group .form-line.focused .form-label { - top: -10px; - left: 0; - font-size: 12px; } - -.form-group-sm .form-label { - font-size: 12px; } - -.form-group-sm .form-line.focused .form-label { - bottom: 20px; - font-size: 10px; } - -.form-group-lg .form-label { - font-size: 18px; } - -.form-group-lg .form-line.focused .form-label { - bottom: 35px; - font-size: 12px; } - -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: transparent; } - -/* Color Picker ================================ */ -.colorpicker { - z-index: 1; } - .colorpicker:before, .colorpicker:after { - display: none !important; } - -/* Dropzone ==================================== */ -.dropzone { - border: 2px solid transparent !important; - background-color: #eee !important; } - .dropzone .dz-message .drag-icon-cph .material-icons { - font-size: 80px; - color: #777; } - -.dz-drag-hover { - border: 2px dashed #888 !important; } - -/* Breadcrumbs ================================= */ -.breadcrumb { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - background-color: transparent; - font-size: 13px; - margin-bottom: 10px; } - .breadcrumb li a { - color: #444; - text-decoration: none; } - .breadcrumb li a .material-icons { - font-size: 18px; - position: relative; - top: 4px; } - .breadcrumb li .material-icons { - font-size: 18px; - position: relative; - top: 4px; } - .breadcrumb > li + li:before { - content: '>\00a0'; } - -.breadcrumb-col-red li a { - color: #F44336 !important; - font-weight: bold; } - -.breadcrumb-bg-red { - background-color: #F44336 !important; } - .breadcrumb-bg-red li { - color: #fff !important; } - .breadcrumb-bg-red li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-red li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-red li + li:before { - color: #fff; } - -.breadcrumb-col-pink li a { - color: #E91E63 !important; - font-weight: bold; } - -.breadcrumb-bg-pink { - background-color: #E91E63 !important; } - .breadcrumb-bg-pink li { - color: #fff !important; } - .breadcrumb-bg-pink li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-pink li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-pink li + li:before { - color: #fff; } - -.breadcrumb-col-purple li a { - color: #9C27B0 !important; - font-weight: bold; } - -.breadcrumb-bg-purple { - background-color: #9C27B0 !important; } - .breadcrumb-bg-purple li { - color: #fff !important; } - .breadcrumb-bg-purple li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-purple li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-purple li + li:before { - color: #fff; } - -.breadcrumb-col-deep-purple li a { - color: #673AB7 !important; - font-weight: bold; } - -.breadcrumb-bg-deep-purple { - background-color: #673AB7 !important; } - .breadcrumb-bg-deep-purple li { - color: #fff !important; } - .breadcrumb-bg-deep-purple li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-deep-purple li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-deep-purple li + li:before { - color: #fff; } - -.breadcrumb-col-indigo li a { - color: #3F51B5 !important; - font-weight: bold; } - -.breadcrumb-bg-indigo { - background-color: #3F51B5 !important; } - .breadcrumb-bg-indigo li { - color: #fff !important; } - .breadcrumb-bg-indigo li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-indigo li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-indigo li + li:before { - color: #fff; } - -.breadcrumb-col-blue li a { - color: #2196F3 !important; - font-weight: bold; } - -.breadcrumb-bg-blue { - background-color: #2196F3 !important; } - .breadcrumb-bg-blue li { - color: #fff !important; } - .breadcrumb-bg-blue li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-blue li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-blue li + li:before { - color: #fff; } - -.breadcrumb-col-light-blue li a { - color: #03A9F4 !important; - font-weight: bold; } - -.breadcrumb-bg-light-blue { - background-color: #03A9F4 !important; } - .breadcrumb-bg-light-blue li { - color: #fff !important; } - .breadcrumb-bg-light-blue li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-light-blue li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-light-blue li + li:before { - color: #fff; } - -.breadcrumb-col-cyan li a { - color: #00BCD4 !important; - font-weight: bold; } - -.breadcrumb-bg-cyan { - background-color: #00BCD4 !important; } - .breadcrumb-bg-cyan li { - color: #fff !important; } - .breadcrumb-bg-cyan li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-cyan li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-cyan li + li:before { - color: #fff; } - -.breadcrumb-col-teal li a { - color: #009688 !important; - font-weight: bold; } - -.breadcrumb-bg-teal { - background-color: #009688 !important; } - .breadcrumb-bg-teal li { - color: #fff !important; } - .breadcrumb-bg-teal li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-teal li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-teal li + li:before { - color: #fff; } - -.breadcrumb-col-green li a { - color: #4CAF50 !important; - font-weight: bold; } - -.breadcrumb-bg-green { - background-color: #4CAF50 !important; } - .breadcrumb-bg-green li { - color: #fff !important; } - .breadcrumb-bg-green li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-green li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-green li + li:before { - color: #fff; } - -.breadcrumb-col-light-green li a { - color: #8BC34A !important; - font-weight: bold; } - -.breadcrumb-bg-light-green { - background-color: #8BC34A !important; } - .breadcrumb-bg-light-green li { - color: #fff !important; } - .breadcrumb-bg-light-green li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-light-green li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-light-green li + li:before { - color: #fff; } - -.breadcrumb-col-lime li a { - color: #CDDC39 !important; - font-weight: bold; } - -.breadcrumb-bg-lime { - background-color: #CDDC39 !important; } - .breadcrumb-bg-lime li { - color: #fff !important; } - .breadcrumb-bg-lime li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-lime li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-lime li + li:before { - color: #fff; } - -.breadcrumb-col-yellow li a { - color: #ffe821 !important; - font-weight: bold; } - -.breadcrumb-bg-yellow { - background-color: #ffe821 !important; } - .breadcrumb-bg-yellow li { - color: #fff !important; } - .breadcrumb-bg-yellow li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-yellow li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-yellow li + li:before { - color: #fff; } - -.breadcrumb-col-amber li a { - color: #FFC107 !important; - font-weight: bold; } - -.breadcrumb-bg-amber { - background-color: #FFC107 !important; } - .breadcrumb-bg-amber li { - color: #fff !important; } - .breadcrumb-bg-amber li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-amber li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-amber li + li:before { - color: #fff; } - -.breadcrumb-col-orange li a { - color: #FF9800 !important; - font-weight: bold; } - -.breadcrumb-bg-orange { - background-color: #FF9800 !important; } - .breadcrumb-bg-orange li { - color: #fff !important; } - .breadcrumb-bg-orange li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-orange li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-orange li + li:before { - color: #fff; } - -.breadcrumb-col-deep-orange li a { - color: #FF5722 !important; - font-weight: bold; } - -.breadcrumb-bg-deep-orange { - background-color: #FF5722 !important; } - .breadcrumb-bg-deep-orange li { - color: #fff !important; } - .breadcrumb-bg-deep-orange li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-deep-orange li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-deep-orange li + li:before { - color: #fff; } - -.breadcrumb-col-brown li a { - color: #795548 !important; - font-weight: bold; } - -.breadcrumb-bg-brown { - background-color: #795548 !important; } - .breadcrumb-bg-brown li { - color: #fff !important; } - .breadcrumb-bg-brown li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-brown li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-brown li + li:before { - color: #fff; } - -.breadcrumb-col-grey li a { - color: #9E9E9E !important; - font-weight: bold; } - -.breadcrumb-bg-grey { - background-color: #9E9E9E !important; } - .breadcrumb-bg-grey li { - color: #fff !important; } - .breadcrumb-bg-grey li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-grey li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-grey li + li:before { - color: #fff; } - -.breadcrumb-col-blue-grey li a { - color: #607D8B !important; - font-weight: bold; } - -.breadcrumb-bg-blue-grey { - background-color: #607D8B !important; } - .breadcrumb-bg-blue-grey li { - color: #fff !important; } - .breadcrumb-bg-blue-grey li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-blue-grey li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-blue-grey li + li:before { - color: #fff; } - -.breadcrumb-col-black li a { - color: #000000 !important; - font-weight: bold; } - -.breadcrumb-bg-black { - background-color: #000000 !important; } - .breadcrumb-bg-black li { - color: #fff !important; } - .breadcrumb-bg-black li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-black li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-black li + li:before { - color: #fff; } - -.breadcrumb-col-white li a { - color: #ffffff !important; - font-weight: bold; } - -.breadcrumb-bg-white { - background-color: #ffffff !important; } - .breadcrumb-bg-white li { - color: #fff !important; } - .breadcrumb-bg-white li a { - color: #fff; - font-weight: bold; } - .breadcrumb-bg-white li a .material-icons { - padding-bottom: 8px; } - .breadcrumb-bg-white li + li:before { - color: #fff; } - -/* Badge | List Group Item ===================== */ -.badge { - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -ms-border-radius: 2px; - border-radius: 2px; } - -.list-group-item { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - -moz-transition: 0.5s; - -o-transition: 0.5s; - -webkit-transition: 0.5s; - transition: 0.5s; } - -.list-group .active { - background-color: #2196F3; - border-color: #2196F3; } - .list-group .active:hover, .list-group .active:focus, .list-group .active:active { - background-color: #2196F3; - border-color: #2196F3; } - .list-group .active .list-group-item-text { - color: #dfe9f1; - font-size: 13px; } - .list-group .active .list-group-item-text:hover, .list-group .active .list-group-item-text:active, .list-group .active .list-group-item-text:focus { - color: #dfe9f1; } - -.list-group .list-group-item.active:hover .list-group-item-text, .list-group .list-group-item.active:focus .list-group-item-text, .list-group .list-group-item.active:active .list-group-item-text { - color: #dfe9f1; } - -.list-group .list-group-item:first-child, .list-group .list-group-item:last-child { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - -.list-group .list-group-item .list-group-item-heading { - font-weight: bold; - font-size: 17px; } - -.list-group .list-group-item-success { - background-color: #2b982b; - border: none; - color: #fff; } - .list-group .list-group-item-success:hover, .list-group .list-group-item-success:focus { - background-color: #2b982b; - color: #fff; - opacity: 0.8; } - -.list-group .list-group-item-info { - background-color: #00b0e4; - border: none; - color: #fff; } - .list-group .list-group-item-info:hover, .list-group .list-group-item-info:focus { - background-color: #00b0e4; - color: #fff; - opacity: 0.8; } - -.list-group .list-group-item-warning { - background-color: #ff9600; - border: none; - color: #fff; } - .list-group .list-group-item-warning:hover, .list-group .list-group-item-warning:focus { - background-color: #ff9600; - color: #fff; - opacity: 0.8; } - -.list-group .list-group-item-danger { - background-color: #fb483a; - border: none; - color: #fff; } - .list-group .list-group-item-danger:hover, .list-group .list-group-item-danger:focus { - background-color: #fb483a; - color: #fff; - opacity: 0.8; } - -.list-group .pl-red { - stroke: #F44336; } - -.list-group .list-group-bg-red { - background-color: #F44336; - border: none; - color: #fff; } - .list-group .list-group-bg-red:hover, .list-group .list-group-bg-red:focus { background-color: #F44336; color: #fff; - opacity: 0.8; } +} -.list-group .pl-pink { - stroke: #E91E63; } - -.list-group .list-group-bg-pink { - background-color: #E91E63; - border: none; - color: #fff; } - .list-group .list-group-bg-pink:hover, .list-group .list-group-bg-pink:focus { +.bg-pink { background-color: #E91E63; color: #fff; - opacity: 0.8; } +} -.list-group .pl-purple { - stroke: #9C27B0; } - -.list-group .list-group-bg-purple { - background-color: #9C27B0; - border: none; - color: #fff; } - .list-group .list-group-bg-purple:hover, .list-group .list-group-bg-purple:focus { +.bg-purple { background-color: #9C27B0; color: #fff; - opacity: 0.8; } +} -.list-group .pl-deep-purple { - stroke: #673AB7; } - -.list-group .list-group-bg-deep-purple { - background-color: #673AB7; - border: none; - color: #fff; } - .list-group .list-group-bg-deep-purple:hover, .list-group .list-group-bg-deep-purple:focus { +.bg-deep-purple { background-color: #673AB7; color: #fff; - opacity: 0.8; } +} -.list-group .pl-indigo { - stroke: #3F51B5; } - -.list-group .list-group-bg-indigo { - background-color: #3F51B5; - border: none; - color: #fff; } - .list-group .list-group-bg-indigo:hover, .list-group .list-group-bg-indigo:focus { +.bg-indigo { background-color: #3F51B5; color: #fff; - opacity: 0.8; } +} -.list-group .pl-blue { - stroke: #2196F3; } - -.list-group .list-group-bg-blue { - background-color: #2196F3; - border: none; - color: #fff; } - .list-group .list-group-bg-blue:hover, .list-group .list-group-bg-blue:focus { +.bg-blue { background-color: #2196F3; color: #fff; - opacity: 0.8; } +} -.list-group .pl-light-blue { - stroke: #03A9F4; } - -.list-group .list-group-bg-light-blue { - background-color: #03A9F4; - border: none; - color: #fff; } - .list-group .list-group-bg-light-blue:hover, .list-group .list-group-bg-light-blue:focus { +.bg-light-blue { background-color: #03A9F4; color: #fff; - opacity: 0.8; } +} -.list-group .pl-cyan { - stroke: #00BCD4; } - -.list-group .list-group-bg-cyan { - background-color: #00BCD4; - border: none; - color: #fff; } - .list-group .list-group-bg-cyan:hover, .list-group .list-group-bg-cyan:focus { +.bg-cyan { background-color: #00BCD4; color: #fff; - opacity: 0.8; } +} -.list-group .pl-teal { - stroke: #009688; } - -.list-group .list-group-bg-teal { - background-color: #009688; - border: none; - color: #fff; } - .list-group .list-group-bg-teal:hover, .list-group .list-group-bg-teal:focus { +.bg-teal { background-color: #009688; color: #fff; - opacity: 0.8; } +} -.list-group .pl-green { - stroke: #4CAF50; } - -.list-group .list-group-bg-green { - background-color: #4CAF50; - border: none; - color: #fff; } - .list-group .list-group-bg-green:hover, .list-group .list-group-bg-green:focus { +.bg-green { background-color: #4CAF50; color: #fff; - opacity: 0.8; } +} -.list-group .pl-light-green { - stroke: #8BC34A; } - -.list-group .list-group-bg-light-green { - background-color: #8BC34A; - border: none; - color: #fff; } - .list-group .list-group-bg-light-green:hover, .list-group .list-group-bg-light-green:focus { +.bg-light-green { background-color: #8BC34A; color: #fff; - opacity: 0.8; } +} -.list-group .pl-lime { - stroke: #CDDC39; } - -.list-group .list-group-bg-lime { - background-color: #CDDC39; - border: none; - color: #fff; } - .list-group .list-group-bg-lime:hover, .list-group .list-group-bg-lime:focus { +.bg-lime { background-color: #CDDC39; color: #fff; - opacity: 0.8; } +} -.list-group .pl-yellow { - stroke: #ffe821; } - -.list-group .list-group-bg-yellow { - background-color: #ffe821; - border: none; - color: #fff; } - .list-group .list-group-bg-yellow:hover, .list-group .list-group-bg-yellow:focus { +.bg-yellow { background-color: #ffe821; color: #fff; - opacity: 0.8; } +} -.list-group .pl-amber { - stroke: #FFC107; } - -.list-group .list-group-bg-amber { - background-color: #FFC107; - border: none; - color: #fff; } - .list-group .list-group-bg-amber:hover, .list-group .list-group-bg-amber:focus { +.bg-amber { background-color: #FFC107; color: #fff; - opacity: 0.8; } +} -.list-group .pl-orange { - stroke: #FF9800; } - -.list-group .list-group-bg-orange { - background-color: #FF9800; - border: none; - color: #fff; } - .list-group .list-group-bg-orange:hover, .list-group .list-group-bg-orange:focus { +.bg-orange { background-color: #FF9800; color: #fff; - opacity: 0.8; } +} -.list-group .pl-deep-orange { - stroke: #FF5722; } - -.list-group .list-group-bg-deep-orange { - background-color: #FF5722; - border: none; - color: #fff; } - .list-group .list-group-bg-deep-orange:hover, .list-group .list-group-bg-deep-orange:focus { +.bg-deep-orange { background-color: #FF5722; color: #fff; - opacity: 0.8; } +} -.list-group .pl-brown { - stroke: #795548; } - -.list-group .list-group-bg-brown { - background-color: #795548; - border: none; - color: #fff; } - .list-group .list-group-bg-brown:hover, .list-group .list-group-bg-brown:focus { +.bg-brown { background-color: #795548; color: #fff; - opacity: 0.8; } +} -.list-group .pl-grey { - stroke: #9E9E9E; } - -.list-group .list-group-bg-grey { - background-color: #9E9E9E; - border: none; - color: #fff; } - .list-group .list-group-bg-grey:hover, .list-group .list-group-bg-grey:focus { +.bg-grey { background-color: #9E9E9E; color: #fff; - opacity: 0.8; } +} -.list-group .pl-blue-grey { - stroke: #607D8B; } - -.list-group .list-group-bg-blue-grey { - background-color: #607D8B; - border: none; - color: #fff; } - .list-group .list-group-bg-blue-grey:hover, .list-group .list-group-bg-blue-grey:focus { +.bg-blue-grey { background-color: #607D8B; color: #fff; - opacity: 0.8; } +} -.list-group .pl-black { - stroke: #000000; } - -.list-group .list-group-bg-black { - background-color: #000000; - border: none; - color: #fff; } - .list-group .list-group-bg-black:hover, .list-group .list-group-bg-black:focus { - background-color: #000000; +.bg-black { + background-color: #555555; color: #fff; - opacity: 0.8; } +} -.list-group .pl-white { - stroke: #ffffff; } - -.list-group .list-group-bg-white { - background-color: #ffffff; - border: none; - color: #fff; } - .list-group .list-group-bg-white:hover, .list-group .list-group-bg-white:focus { +.bg-white { background-color: #ffffff; - color: #fff; - opacity: 0.8; } - -/* Pagination & Pager ========================== */ -.pager li > a { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - border: none; - background-color: transparent; - color: #222; - font-weight: bold; } - -.pager li a:focus, -.pager li a:active { - background-color: transparent; } - -.pagination .disabled a, -.pagination .disabled a:hover, -.pagination .disabled a:focus, -.pagination .disabled a:active { - color: #bbb; } - -.pagination li.active a { - background-color: #2196F3; } - -.pagination li { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .pagination li a:focus, - .pagination li a:active { - background-color: transparent; - color: #555; } - -.pagination > li > a { - border: none; - font-weight: bold; - color: #555; } - -.pagination > li:first-child > a, -.pagination > li:last-child > a { - width: auto; - height: 32px; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .pagination > li:first-child > a .material-icons, - .pagination > li:last-child > a .material-icons { - position: relative; - bottom: 2px; } - -.pagination-sm > li:first-child > a, -.pagination-sm > li:last-child > a { - width: 28px; - height: 28px; } - .pagination-sm > li:first-child > a .material-icons, - .pagination-sm > li:last-child > a .material-icons { - position: relative; - top: -1px; - left: -6px; - font-size: 20px; } - -.pagination-lg > li:first-child > a, -.pagination-lg > li:last-child > a { - width: 44px; - height: 44px; } - .pagination-lg > li:first-child > a .material-icons, - .pagination-lg > li:last-child > a .material-icons { - font-size: 30px; - position: relative; - top: -3px; - left: -10px; } - -/* Media Object ================================ */ -.media { - margin-bottom: 25px; } - .media .media-body { - color: #777; - font-size: 13px; } - .media .media-body .media-heading { - font-size: 16px; - font-weight: bold; - color: #333; } - -/* Form Wizard ================================= */ -.wizard, -.tabcontrol { - display: block; - width: 100%; - overflow: hidden; } - -.wizard a, -.tabcontrol a { - outline: 0; } - -.wizard ul, -.tabcontrol ul { - list-style: none !important; - padding: 0; - margin: 0; } - -.wizard ul > li, .tabcontrol ul > li { - display: block; - padding: 0; } - -/* Accessibility */ -.wizard > .steps .current-info, -.tabcontrol > .steps .current-info, -.wizard > .content > .title, -.tabcontrol > .content > .title { - position: absolute; - left: -999em; } - -.wizard > .steps { - position: relative; - display: block; - width: 100%; } - -.wizard.vertical > .steps { - float: left; - width: 30%; } - -.wizard.vertical > .steps > ul > li { - float: none; - width: 100%; } - -.wizard.vertical > .content { - float: left; - margin: 0 0 0.5em 0; - width: 70%; } - -.wizard.vertical > .actions { - float: right; - width: 100%; } - -.wizard.vertical > .actions > ul > li { - margin: 0 0 0 1em; } - -.wizard > .steps .number { - font-size: 1.429em; } - -.wizard > .steps > ul > li { - width: 25%; - float: left; } - -.wizard > .actions > ul > li { - float: left; } - -.wizard > .steps a { - display: block; - width: auto; - margin: 0 0.5em 0.5em; - padding: 1em 1em; - text-decoration: none; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; } - .wizard > .steps a:hover, .wizard > .steps a:active { - display: block; - width: auto; - margin: 0 0.5em 0.5em; - padding: 1em 1em; - text-decoration: none; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; } - -.wizard > .steps .disabled a { - background: #eee; - color: #aaa; - cursor: default; } - .wizard > .steps .disabled a:hover, .wizard > .steps .disabled a:active { - background: #eee; - color: #aaa; - cursor: default; } - -.wizard > .steps .current a { - background: #2184be; - color: #fff; - cursor: default; } - .wizard > .steps .current a:hover, .wizard > .steps .current a:active { - background: #2184be; - color: #fff; - cursor: default; } - -.wizard > .steps .done a { - background: #9dc8e2; - color: #fff; } - .wizard > .steps .done a:hover, .wizard > .steps .done a:active { - background: #9dc8e2; - color: #fff; } - -.wizard > .steps .error a { - background: #ff3111; - color: #fff; } - .wizard > .steps .error a:hover, .wizard > .steps .error a:active { - background: #ff3111; - color: #fff; } - -.wizard > .content { - border: 1px solid #ddd; - display: block; - margin: 0.5em; - min-height: 35em; - overflow: hidden; - position: relative; - width: auto; } - -.wizard > .actions { - position: relative; - display: block; - text-align: right; - width: 100%; } - -.wizard > .actions > ul { - display: inline-block; - text-align: right; } - .wizard > .actions > ul > li { - margin: 0 0.5em; } - -.wizard > .actions a { - background: #009688; - color: #fff; - display: block; - padding: 0.5em 1em; - text-decoration: none; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - .wizard > .actions a:hover, .wizard > .actions a:active { - background: #009688; - color: #fff; - display: block; - padding: 0.5em 1em; - text-decoration: none; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - -.wizard > .actions .disabled a { - background: #eee; - color: #aaa; } - .wizard > .actions .disabled a:hover, .wizard > .actions .disabled a:active { - background: #eee; - color: #aaa; } - -.tabcontrol > .steps { - position: relative; - display: block; - width: 100%; } - .tabcontrol > .steps > ul { - position: relative; - margin: 6px 0 0 0; - top: 1px; - z-index: 1; } - .tabcontrol > .steps > ul > li { - float: left; - margin: 5px 2px 0 0; - padding: 1px; - -webkit-border-top-left-radius: 5px; - -webkit-border-top-right-radius: 5px; - -moz-border-radius-topleft: 5px; - -moz-border-radius-topright: 5px; - border-top-left-radius: 5px; - border-top-right-radius: 5px; } - .tabcontrol > .steps > ul > li:hover { - background: #edecec; - border: 1px solid #bbb; - padding: 0; } - .tabcontrol > .steps > ul > li.current { - background: #fff; - border: 1px solid #bbb; - border-bottom: 0 none; - padding: 0 0 1px 0; - margin-top: 0; } - .tabcontrol > .steps > ul > li.current > a { - padding: 15px 30px 10px 30px; } - .tabcontrol > .steps > ul > li > a { - color: #5f5f5f; - display: inline-block; - border: 0 none; - margin: 0; - padding: 10px 30px; - text-decoration: none; } - .tabcontrol > .steps > ul > li > a:hover { - text-decoration: none; } - -.tabcontrol > .content { - position: relative; - display: inline-block; - width: 100%; - height: 35em; - overflow: hidden; - border-top: 1px solid #bbb; - padding-top: 20px; } - .tabcontrol > .content > .body { - float: left; - position: absolute; - width: 95%; - height: 95%; - padding: 2.5%; } - .tabcontrol > .content > .body ul { - list-style: disc !important; } - .tabcontrol > .content > .body ul > li { - display: list-item; } - -.wizard .content { - min-height: 245px; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - overflow-y: auto; } - .wizard .content .body { - padding: 15px; } - -.wizard .steps a { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - -moz-transition: 0.5s; - -o-transition: 0.5s; - -webkit-transition: 0.5s; - transition: 0.5s; } - .wizard .steps a:active, .wizard .steps a:focus, .wizard .steps a:hover { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } - -.wizard .steps .done a { - background-color: rgba(0, 150, 136, 0.6); } - .wizard .steps .done a:hover, .wizard .steps .done a:active, .wizard .steps .done a:focus { - background-color: rgba(0, 150, 136, 0.5); } - -.wizard .steps .error a { - background-color: #F44336 !important; } - -.wizard .steps .current a { - background-color: #009688; } - .wizard .steps .current a:active, .wizard .steps .current a:focus, .wizard .steps .current a:hover { - background-color: #009688; } - -/* Waves ======================================= */ -.waves-effect.waves-red .waves-ripple { - background: rgba(244, 67, 54, 0.5); } - -.waves-effect.waves-pink .waves-ripple { - background: rgba(233, 30, 99, 0.5); } - -.waves-effect.waves-purple .waves-ripple { - background: rgba(156, 39, 176, 0.5); } - -.waves-effect.waves-deep-purple .waves-ripple { - background: rgba(103, 58, 183, 0.5); } - -.waves-effect.waves-indigo .waves-ripple { - background: rgba(63, 81, 181, 0.5); } - -.waves-effect.waves-blue .waves-ripple { - background: rgba(33, 150, 243, 0.5); } - -.waves-effect.waves-light-blue .waves-ripple { - background: rgba(3, 169, 244, 0.5); } - -.waves-effect.waves-cyan .waves-ripple { - background: rgba(0, 188, 212, 0.5); } - -.waves-effect.waves-teal .waves-ripple { - background: rgba(0, 150, 136, 0.5); } - -.waves-effect.waves-green .waves-ripple { - background: rgba(76, 175, 80, 0.5); } - -.waves-effect.waves-light-green .waves-ripple { - background: rgba(139, 195, 74, 0.5); } - -.waves-effect.waves-lime .waves-ripple { - background: rgba(205, 220, 57, 0.5); } - -.waves-effect.waves-yellow .waves-ripple { - background: rgba(255, 232, 33, 0.5); } - -.waves-effect.waves-amber .waves-ripple { - background: rgba(255, 193, 7, 0.5); } - -.waves-effect.waves-orange .waves-ripple { - background: rgba(255, 152, 0, 0.5); } - -.waves-effect.waves-deep-orange .waves-ripple { - background: rgba(255, 87, 34, 0.5); } - -.waves-effect.waves-brown .waves-ripple { - background: rgba(121, 85, 72, 0.5); } - -.waves-effect.waves-grey .waves-ripple { - background: rgba(158, 158, 158, 0.5); } - -.waves-effect.waves-blue-grey .waves-ripple { - background: rgba(96, 125, 139, 0.5); } - -.waves-effect.waves-black .waves-ripple { - background: rgba(0, 0, 0, 0.5); } - -.waves-effect.waves-white .waves-ripple { - background: rgba(255, 255, 255, 0.5); } - -/* Page Loader ================================= */ -.page-loader-wrapper { - z-index: 99999999; - position: fixed; - top: 0; - left: 0; - bottom: 0; - right: 0; - width: 100%; - height: 100%; - background: #eee; - overflow: hidden; - text-align: center; } - .page-loader-wrapper p { - font-size: 13px; - margin-top: 10px; - font-weight: bold; - color: #444; } - .page-loader-wrapper .loader { - position: relative; - top: calc(50% - 30px); } - -/* Preloaders ================================== */ -.md-preloader .pl-red { - stroke: #F44336; } - -.md-preloader .pl-pink { - stroke: #E91E63; } - -.md-preloader .pl-purple { - stroke: #9C27B0; } - -.md-preloader .pl-deep-purple { - stroke: #673AB7; } - -.md-preloader .pl-indigo { - stroke: #3F51B5; } - -.md-preloader .pl-blue { - stroke: #2196F3; } - -.md-preloader .pl-light-blue { - stroke: #03A9F4; } - -.md-preloader .pl-cyan { - stroke: #00BCD4; } - -.md-preloader .pl-teal { - stroke: #009688; } - -.md-preloader .pl-green { - stroke: #4CAF50; } - -.md-preloader .pl-light-green { - stroke: #8BC34A; } - -.md-preloader .pl-lime { - stroke: #CDDC39; } - -.md-preloader .pl-yellow { - stroke: #ffe821; } - -.md-preloader .pl-amber { - stroke: #FFC107; } - -.md-preloader .pl-orange { - stroke: #FF9800; } - -.md-preloader .pl-deep-orange { - stroke: #FF5722; } - -.md-preloader .pl-brown { - stroke: #795548; } - -.md-preloader .pl-grey { - stroke: #9E9E9E; } - -.md-preloader .pl-blue-grey { - stroke: #607D8B; } - -.md-preloader .pl-black { - stroke: #000000; } - -.md-preloader .pl-white { - stroke: #ffffff; } - -.preloader { - display: inline-block; - position: relative; - width: 50px; - height: 50px; - -webkit-animation: container-rotate 1568ms linear infinite; - -moz-animation: container-rotate 1568ms linear infinite; - -o-animation: container-rotate 1568ms linear infinite; - animation: container-rotate 1568ms linear infinite; } - .preloader.pl-size-xl { - width: 75px; - height: 75px; } - .preloader.pl-size-l { - width: 60px; - height: 60px; } - .preloader.pl-size-md { - width: 50px; - height: 50px; } - .preloader.pl-size-sm { - width: 40px; - height: 40px; } - .preloader.pl-size-xs { - width: 25px; - height: 25px; } - -.spinner-layer { - position: absolute; - width: 100%; - height: 100%; - border-color: #F44336; - -ms-opacity: 1; - opacity: 1; - -webkit-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - -moz-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - -o-animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: fill-unfill-rotate 5332ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - .spinner-layer.pl-red { - border-color: #F44336; } - .spinner-layer.pl-pink { - border-color: #E91E63; } - .spinner-layer.pl-purple { - border-color: #9C27B0; } - .spinner-layer.pl-deep-purple { - border-color: #673AB7; } - .spinner-layer.pl-indigo { - border-color: #3F51B5; } - .spinner-layer.pl-blue { - border-color: #2196F3; } - .spinner-layer.pl-light-blue { - border-color: #03A9F4; } - .spinner-layer.pl-cyan { - border-color: #00BCD4; } - .spinner-layer.pl-teal { - border-color: #009688; } - .spinner-layer.pl-green { - border-color: #4CAF50; } - .spinner-layer.pl-light-green { - border-color: #8BC34A; } - .spinner-layer.pl-lime { - border-color: #CDDC39; } - .spinner-layer.pl-yellow { - border-color: #ffe821; } - .spinner-layer.pl-amber { - border-color: #FFC107; } - .spinner-layer.pl-orange { - border-color: #FF9800; } - .spinner-layer.pl-deep-orange { - border-color: #FF5722; } - .spinner-layer.pl-brown { - border-color: #795548; } - .spinner-layer.pl-grey { - border-color: #9E9E9E; } - .spinner-layer.pl-blue-grey { - border-color: #607D8B; } - .spinner-layer.pl-black { - border-color: #000000; } - .spinner-layer.pl-white { - border-color: #ffffff; } - -.right { - float: right !important; } - -.gap-patch { - position: absolute; - top: 0; - left: 45%; - width: 10%; - height: 100%; - overflow: hidden; - border-color: inherit; } - .gap-patch.circle { - width: 1000%; - left: -450%; } - -.circle-clipper { - display: inline-block; - position: relative; - width: 50%; - height: 100%; - overflow: hidden; - border-color: inherit; } - .circle-clipper .circle { - width: 200%; - height: 100%; - border-width: 3px; - border-style: solid; - border-color: inherit; - border-bottom-color: transparent !important; - -ms-border-radius: 50%; - border-radius: 50%; - -webkit-animation: none; - animation: none; - position: absolute; - top: 0; - right: 0; - bottom: 0; } - .circle-clipper.left .circle { - left: 0; - border-right-color: transparent !important; - -webkit-transform: rotate(129deg); - -moz-transform: rotate(129deg); - -ms-transform: rotate(129deg); - -o-transform: rotate(129deg); - transform: rotate(129deg); - -webkit-animation: left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - -moz-animation: left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - -o-animation: left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: left-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - .circle-clipper.right .circle { - left: -100%; - border-left-color: transparent !important; - -webkit-transform: rotate(-129deg); - -moz-transform: rotate(-129deg); - -ms-transform: rotate(-129deg); - -o-transform: rotate(-129deg); - transform: rotate(-129deg); - -webkit-animation: right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - -moz-animation: right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - -o-animation: right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; - animation: right-spin 1333ms cubic-bezier(0.4, 0, 0.2, 1) infinite both; } - -@-webkit-keyframes container-rotate { - to { - -webkit-transform: rotate(360deg); - -moz-transform: rotate(360deg); - -ms-transform: rotate(360deg); - -o-transform: rotate(360deg); - transform: rotate(360deg); } } - -@keyframes container-rotate { - to { - -moz-transform: rotate(360deg); - -ms-transform: rotate(360deg); - -o-transform: rotate(360deg); - -webkit-transform: rotate(360deg); - transform: rotate(360deg); } } - -@-webkit-keyframes fill-unfill-rotate { - 12.5% { - -webkit-transform: rotate(135deg); - transform: rotate(135deg); } - 25% { - -webkit-transform: rotate(270deg); - transform: rotate(270deg); } - 37.5% { - -webkit-transform: rotate(405deg); - transform: rotate(405deg); } - 50% { - -webkit-transform: rotate(540deg); - transform: rotate(540deg); } - 62.5% { - -webkit-transform: rotate(675deg); - transform: rotate(675deg); } - 75% { - -webkit-transform: rotate(810deg); - transform: rotate(810deg); } - 87.5% { - -webkit-transform: rotate(945deg); - transform: rotate(945deg); } - to { - -webkit-transform: rotate(1080deg); - transform: rotate(1080deg); } } - -@keyframes fill-unfill-rotate { - 12.5% { - transform: rotate(135deg); } - 25% { - transform: rotate(270deg); } - 37.5% { - transform: rotate(405deg); } - 50% { - transform: rotate(540deg); } - 62.5% { - transform: rotate(675deg); } - 75% { - transform: rotate(810deg); } - 87.5% { - transform: rotate(945deg); } - to { - transform: rotate(1080deg); } } - -@-webkit-keyframes left-spin { - from { - -webkit-transform: rotate(130deg); - -moz-transform: rotate(130deg); - -ms-transform: rotate(130deg); - -o-transform: rotate(130deg); - transform: rotate(130deg); } - 50% { - -webkit-transform: rotate(-5deg); - -moz-transform: rotate(-5deg); - -ms-transform: rotate(-5deg); - -o-transform: rotate(-5deg); - transform: rotate(-5deg); } - to { - -webkit-transform: rotate(130deg); - -moz-transform: rotate(130deg); - -ms-transform: rotate(130deg); - -o-transform: rotate(130deg); - transform: rotate(130deg); } } - -@keyframes left-spin { - from { - -moz-transform: rotate(130deg); - -ms-transform: rotate(130deg); - -o-transform: rotate(130deg); - -webkit-transform: rotate(130deg); - transform: rotate(130deg); } - 50% { - -moz-transform: rotate(-5deg); - -ms-transform: rotate(-5deg); - -o-transform: rotate(-5deg); - -webkit-transform: rotate(-5deg); - transform: rotate(-5deg); } - to { - -moz-transform: rotate(130deg); - -ms-transform: rotate(130deg); - -o-transform: rotate(130deg); - -webkit-transform: rotate(130deg); - transform: rotate(130deg); } } - -@-webkit-keyframes right-spin { - from { - -webkit-transform: rotate(-130deg); - -moz-transform: rotate(-130deg); - -ms-transform: rotate(-130deg); - -o-transform: rotate(-130deg); - transform: rotate(-130deg); } - 50% { - -webkit-transform: rotate(5deg); - -moz-transform: rotate(5deg); - -ms-transform: rotate(5deg); - -o-transform: rotate(5deg); - transform: rotate(5deg); } - to { - -webkit-transform: rotate(-130deg); - -moz-transform: rotate(-130deg); - -ms-transform: rotate(-130deg); - -o-transform: rotate(-130deg); - transform: rotate(-130deg); } } - -@-moz-keyframes right-spin { - from { - -moz-transform: rotate(-130deg); - -ms-transform: rotate(-130deg); - -o-transform: rotate(-130deg); - -webkit-transform: rotate(-130deg); - transform: rotate(-130deg); } - 50% { - -moz-transform: rotate(5deg); - -ms-transform: rotate(5deg); - -o-transform: rotate(5deg); - -webkit-transform: rotate(5deg); - transform: rotate(5deg); } - to { - -moz-transform: rotate(-130deg); - -ms-transform: rotate(-130deg); - -o-transform: rotate(-130deg); - -webkit-transform: rotate(-130deg); - transform: rotate(-130deg); } } - -@keyframes right-spin { - from { - -moz-transform: rotate(-130deg); - -ms-transform: rotate(-130deg); - -o-transform: rotate(-130deg); - -webkit-transform: rotate(-130deg); - transform: rotate(-130deg); } - 50% { - -moz-transform: rotate(5deg); - -ms-transform: rotate(5deg); - -o-transform: rotate(5deg); - -webkit-transform: rotate(5deg); - transform: rotate(5deg); } - to { - -moz-transform: rotate(-130deg); - -ms-transform: rotate(-130deg); - -o-transform: rotate(-130deg); - -webkit-transform: rotate(-130deg); - transform: rotate(-130deg); } } - -/* Navbars ===================================== */ -.navbar { - font-family: "Roboto", sans-serif; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3); - -moz-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3); - -ms-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3); - box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3); - border: none; - position: fixed; - top: 0; - left: 0; - z-index: 12; - width: 100%; } - .navbar .navbar-brand { - white-space: nowrap; - -ms-text-overflow: ellipsis; - -o-text-overflow: ellipsis; - text-overflow: ellipsis; - overflow: hidden; } - .navbar .navbar-custom-right-menu { - float: right; } - .navbar .navbar-toggle { - text-decoration: none; - color: #fff; - width: 20px; - height: 20px; - margin-top: -4px; - margin-right: 17px; } - .navbar .navbar-toggle:before { - content: '\E8D5'; - font-family: 'Material Icons'; - font-size: 26px; } - .navbar .navbar-collapse.in { - overflow: visible; } - -.ls-closed .sidebar { - margin-left: -300px; } - -.ls-closed section.content { - margin-left: 15px; } - -.ls-closed .bars:after, .ls-closed .bars:before { - font-family: 'Material Icons'; - font-size: 24px; - position: absolute; - top: 18px; - left: 20px; - margin-right: 10px; - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); - -moz-transition: all 0.3s; - -o-transition: all 0.3s; - -webkit-transition: all 0.3s; - transition: all 0.3s; } - -.ls-closed .bars:before { - content: '\E5D2'; - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - -webkit-transform: scale(1); - transform: scale(1); } - -.ls-closed .bars:after { - content: '\E5C4'; - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); } - -.ls-closed .navbar-brand { - margin-left: 30px; } - -.overlay-open .bars:before { - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); } - -.overlay-open .bars:after { - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - -webkit-transform: scale(1); - transform: scale(1); } - -.navbar-header { - padding: 10px 7px; } - .navbar-header .bars { - float: left; - text-decoration: none; } - -.navbar-nav > li > a { - padding: 7px 7px 2px 7px; - margin-top: 17px; - margin-left: 5px; } - -.navbar-nav .dropdown-menu { - margin-top: -40px !important; } - -.label-count { - position: absolute; - top: 2px; - right: 6px; - font-size: 10px; - line-height: 15px; - background-color: #000; - padding: 0 4px; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - border-radius: 3px; } - -.col-red .navbar .navbar-brand, -.col-red .navbar .navbar-brand:hover, -.col-red .navbar .navbar-brand:active, -.col-red .navbar .navbar-brand:focus { - color: #fff; } - -.col-red .navbar .nav > li > a:hover, -.col-red .navbar .nav > li > a:focus, -.col-red .navbar .nav .open > a, -.col-red .navbar .nav .open > a:hover, -.col-red .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-red .navbar .nav > li > a { - color: #fff; } - -.col-red .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-red .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-pink .navbar .navbar-brand, -.col-pink .navbar .navbar-brand:hover, -.col-pink .navbar .navbar-brand:active, -.col-pink .navbar .navbar-brand:focus { - color: #fff; } - -.col-pink .navbar .nav > li > a:hover, -.col-pink .navbar .nav > li > a:focus, -.col-pink .navbar .nav .open > a, -.col-pink .navbar .nav .open > a:hover, -.col-pink .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-pink .navbar .nav > li > a { - color: #fff; } - -.col-pink .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-pink .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-purple .navbar .navbar-brand, -.col-purple .navbar .navbar-brand:hover, -.col-purple .navbar .navbar-brand:active, -.col-purple .navbar .navbar-brand:focus { - color: #fff; } - -.col-purple .navbar .nav > li > a:hover, -.col-purple .navbar .nav > li > a:focus, -.col-purple .navbar .nav .open > a, -.col-purple .navbar .nav .open > a:hover, -.col-purple .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-purple .navbar .nav > li > a { - color: #fff; } - -.col-purple .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-purple .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-deep-purple .navbar .navbar-brand, -.col-deep-purple .navbar .navbar-brand:hover, -.col-deep-purple .navbar .navbar-brand:active, -.col-deep-purple .navbar .navbar-brand:focus { - color: #fff; } - -.col-deep-purple .navbar .nav > li > a:hover, -.col-deep-purple .navbar .nav > li > a:focus, -.col-deep-purple .navbar .nav .open > a, -.col-deep-purple .navbar .nav .open > a:hover, -.col-deep-purple .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-deep-purple .navbar .nav > li > a { - color: #fff; } - -.col-deep-purple .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-deep-purple .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-indigo .navbar .navbar-brand, -.col-indigo .navbar .navbar-brand:hover, -.col-indigo .navbar .navbar-brand:active, -.col-indigo .navbar .navbar-brand:focus { - color: #fff; } - -.col-indigo .navbar .nav > li > a:hover, -.col-indigo .navbar .nav > li > a:focus, -.col-indigo .navbar .nav .open > a, -.col-indigo .navbar .nav .open > a:hover, -.col-indigo .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-indigo .navbar .nav > li > a { - color: #fff; } - -.col-indigo .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-indigo .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-blue .navbar .navbar-brand, -.col-blue .navbar .navbar-brand:hover, -.col-blue .navbar .navbar-brand:active, -.col-blue .navbar .navbar-brand:focus { - color: #fff; } - -.col-blue .navbar .nav > li > a:hover, -.col-blue .navbar .nav > li > a:focus, -.col-blue .navbar .nav .open > a, -.col-blue .navbar .nav .open > a:hover, -.col-blue .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-blue .navbar .nav > li > a { - color: #fff; } - -.col-blue .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-blue .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-light-blue .navbar .navbar-brand, -.col-light-blue .navbar .navbar-brand:hover, -.col-light-blue .navbar .navbar-brand:active, -.col-light-blue .navbar .navbar-brand:focus { - color: #fff; } - -.col-light-blue .navbar .nav > li > a:hover, -.col-light-blue .navbar .nav > li > a:focus, -.col-light-blue .navbar .nav .open > a, -.col-light-blue .navbar .nav .open > a:hover, -.col-light-blue .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-light-blue .navbar .nav > li > a { - color: #fff; } - -.col-light-blue .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-light-blue .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-cyan .navbar .navbar-brand, -.col-cyan .navbar .navbar-brand:hover, -.col-cyan .navbar .navbar-brand:active, -.col-cyan .navbar .navbar-brand:focus { - color: #fff; } - -.col-cyan .navbar .nav > li > a:hover, -.col-cyan .navbar .nav > li > a:focus, -.col-cyan .navbar .nav .open > a, -.col-cyan .navbar .nav .open > a:hover, -.col-cyan .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-cyan .navbar .nav > li > a { - color: #fff; } - -.col-cyan .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-cyan .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-teal .navbar .navbar-brand, -.col-teal .navbar .navbar-brand:hover, -.col-teal .navbar .navbar-brand:active, -.col-teal .navbar .navbar-brand:focus { - color: #fff; } - -.col-teal .navbar .nav > li > a:hover, -.col-teal .navbar .nav > li > a:focus, -.col-teal .navbar .nav .open > a, -.col-teal .navbar .nav .open > a:hover, -.col-teal .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-teal .navbar .nav > li > a { - color: #fff; } - -.col-teal .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-teal .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-green .navbar .navbar-brand, -.col-green .navbar .navbar-brand:hover, -.col-green .navbar .navbar-brand:active, -.col-green .navbar .navbar-brand:focus { - color: #fff; } - -.col-green .navbar .nav > li > a:hover, -.col-green .navbar .nav > li > a:focus, -.col-green .navbar .nav .open > a, -.col-green .navbar .nav .open > a:hover, -.col-green .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-green .navbar .nav > li > a { - color: #fff; } - -.col-green .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-green .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-light-green .navbar .navbar-brand, -.col-light-green .navbar .navbar-brand:hover, -.col-light-green .navbar .navbar-brand:active, -.col-light-green .navbar .navbar-brand:focus { - color: #fff; } - -.col-light-green .navbar .nav > li > a:hover, -.col-light-green .navbar .nav > li > a:focus, -.col-light-green .navbar .nav .open > a, -.col-light-green .navbar .nav .open > a:hover, -.col-light-green .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-light-green .navbar .nav > li > a { - color: #fff; } - -.col-light-green .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-light-green .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-lime .navbar .navbar-brand, -.col-lime .navbar .navbar-brand:hover, -.col-lime .navbar .navbar-brand:active, -.col-lime .navbar .navbar-brand:focus { - color: #fff; } - -.col-lime .navbar .nav > li > a:hover, -.col-lime .navbar .nav > li > a:focus, -.col-lime .navbar .nav .open > a, -.col-lime .navbar .nav .open > a:hover, -.col-lime .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-lime .navbar .nav > li > a { - color: #fff; } - -.col-lime .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-lime .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-yellow .navbar .navbar-brand, -.col-yellow .navbar .navbar-brand:hover, -.col-yellow .navbar .navbar-brand:active, -.col-yellow .navbar .navbar-brand:focus { - color: #fff; } - -.col-yellow .navbar .nav > li > a:hover, -.col-yellow .navbar .nav > li > a:focus, -.col-yellow .navbar .nav .open > a, -.col-yellow .navbar .nav .open > a:hover, -.col-yellow .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-yellow .navbar .nav > li > a { - color: #fff; } - -.col-yellow .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-yellow .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-amber .navbar .navbar-brand, -.col-amber .navbar .navbar-brand:hover, -.col-amber .navbar .navbar-brand:active, -.col-amber .navbar .navbar-brand:focus { - color: #fff; } - -.col-amber .navbar .nav > li > a:hover, -.col-amber .navbar .nav > li > a:focus, -.col-amber .navbar .nav .open > a, -.col-amber .navbar .nav .open > a:hover, -.col-amber .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-amber .navbar .nav > li > a { - color: #fff; } - -.col-amber .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-amber .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-orange .navbar .navbar-brand, -.col-orange .navbar .navbar-brand:hover, -.col-orange .navbar .navbar-brand:active, -.col-orange .navbar .navbar-brand:focus { - color: #fff; } - -.col-orange .navbar .nav > li > a:hover, -.col-orange .navbar .nav > li > a:focus, -.col-orange .navbar .nav .open > a, -.col-orange .navbar .nav .open > a:hover, -.col-orange .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-orange .navbar .nav > li > a { - color: #fff; } - -.col-orange .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-orange .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-deep-orange .navbar .navbar-brand, -.col-deep-orange .navbar .navbar-brand:hover, -.col-deep-orange .navbar .navbar-brand:active, -.col-deep-orange .navbar .navbar-brand:focus { - color: #fff; } - -.col-deep-orange .navbar .nav > li > a:hover, -.col-deep-orange .navbar .nav > li > a:focus, -.col-deep-orange .navbar .nav .open > a, -.col-deep-orange .navbar .nav .open > a:hover, -.col-deep-orange .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-deep-orange .navbar .nav > li > a { - color: #fff; } - -.col-deep-orange .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-deep-orange .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-brown .navbar .navbar-brand, -.col-brown .navbar .navbar-brand:hover, -.col-brown .navbar .navbar-brand:active, -.col-brown .navbar .navbar-brand:focus { - color: #fff; } - -.col-brown .navbar .nav > li > a:hover, -.col-brown .navbar .nav > li > a:focus, -.col-brown .navbar .nav .open > a, -.col-brown .navbar .nav .open > a:hover, -.col-brown .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-brown .navbar .nav > li > a { - color: #fff; } - -.col-brown .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-brown .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-grey .navbar .navbar-brand, -.col-grey .navbar .navbar-brand:hover, -.col-grey .navbar .navbar-brand:active, -.col-grey .navbar .navbar-brand:focus { - color: #fff; } - -.col-grey .navbar .nav > li > a:hover, -.col-grey .navbar .nav > li > a:focus, -.col-grey .navbar .nav .open > a, -.col-grey .navbar .nav .open > a:hover, -.col-grey .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-grey .navbar .nav > li > a { - color: #fff; } - -.col-grey .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-grey .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-blue-grey .navbar .navbar-brand, -.col-blue-grey .navbar .navbar-brand:hover, -.col-blue-grey .navbar .navbar-brand:active, -.col-blue-grey .navbar .navbar-brand:focus { - color: #fff; } - -.col-blue-grey .navbar .nav > li > a:hover, -.col-blue-grey .navbar .nav > li > a:focus, -.col-blue-grey .navbar .nav .open > a, -.col-blue-grey .navbar .nav .open > a:hover, -.col-blue-grey .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-blue-grey .navbar .nav > li > a { - color: #fff; } - -.col-blue-grey .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-blue-grey .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-black .navbar .navbar-brand, -.col-black .navbar .navbar-brand:hover, -.col-black .navbar .navbar-brand:active, -.col-black .navbar .navbar-brand:focus { - color: #fff; } - -.col-black .navbar .nav > li > a:hover, -.col-black .navbar .nav > li > a:focus, -.col-black .navbar .nav .open > a, -.col-black .navbar .nav .open > a:hover, -.col-black .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-black .navbar .nav > li > a { - color: #fff; } - -.col-black .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-black .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -.col-white .navbar .navbar-brand, -.col-white .navbar .navbar-brand:hover, -.col-white .navbar .navbar-brand:active, -.col-white .navbar .navbar-brand:focus { - color: #fff; } - -.col-white .navbar .nav > li > a:hover, -.col-white .navbar .nav > li > a:focus, -.col-white .navbar .nav .open > a, -.col-white .navbar .nav .open > a:hover, -.col-white .navbar .nav .open > a:focus { - background-color: rgba(0, 0, 0, 0.05); } - -.col-white .navbar .nav > li > a { - color: #fff; } - -.col-white .navbar .bars { - float: left; - padding: 10px 20px; - font-size: 22px; - color: #fff; - margin-right: 10px; - margin-left: -10px; - margin-top: 4px; } - -.col-white .navbar .bars:hover { - background-color: rgba(0, 0, 0, 0.08); } - -/* Dropdown Menu =============================== */ -.dropdown-menu { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - margin-top: -35px !important; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); - border: none; } - .dropdown-menu .divider { - margin: 5px 0; } - .dropdown-menu .header { - font-size: 13px; - font-weight: bold; - min-width: 270px; - border-bottom: 1px solid #eee; - text-align: center; - padding: 4px 0 6px 0; } - .dropdown-menu ul.menu { - padding-left: 0; } - .dropdown-menu ul.menu.tasks h4 { - color: #333; - font-size: 13px; - margin: 0 0 8px 0; } - .dropdown-menu ul.menu.tasks h4 small { - float: right; - margin-top: 6px; } - .dropdown-menu ul.menu.tasks .progress { - height: 7px; - margin-bottom: 7px; } - .dropdown-menu ul.menu .icon-circle { - width: 36px; - height: 36px; - -webkit-border-radius: 50%; - -moz-border-radius: 50%; - -ms-border-radius: 50%; - border-radius: 50%; - color: #fff; - text-align: center; - display: inline-block; } - .dropdown-menu ul.menu .icon-circle i { - font-size: 18px; - line-height: 36px; } - .dropdown-menu ul.menu li { - border-bottom: 1px solid #eee; } - .dropdown-menu ul.menu li:last-child { - border-bottom: none; } - .dropdown-menu ul.menu li a { - padding: 7px 11px; - text-decoration: none; - -moz-transition: 0.5s; - -o-transition: 0.5s; - -webkit-transition: 0.5s; - transition: 0.5s; } - .dropdown-menu ul.menu li a:hover { - background-color: #e9e9e9; } - .dropdown-menu ul.menu .menu-info { - display: inline-block; - position: relative; - top: 3px; - left: 5px; } - .dropdown-menu ul.menu .menu-info h4 { - margin: 0; - font-size: 13px; - color: #333; } - .dropdown-menu ul.menu .menu-info p { - margin: 0; - font-size: 11px; - color: #aaa; } - .dropdown-menu ul.menu .menu-info p .material-icons { - font-size: 13px; - color: #aaa; - position: relative; - top: 2px; } - .dropdown-menu .footer a { - text-align: center; - border-top: 1px solid #eee; - padding: 5px 0 5px 0; - font-size: 12px; - margin-bottom: -5px; } - .dropdown-menu .footer a:hover { - background-color: transparent; } - .dropdown-menu > li > a { - padding: 7px 18px; - color: #666; - -moz-transition: all 0.5s; - -o-transition: all 0.5s; - -webkit-transition: all 0.5s; - transition: all 0.5s; - font-size: 14px; - line-height: 25px; } - .dropdown-menu > li > a:hover { - background-color: rgba(0, 0, 0, 0.075); } - .dropdown-menu > li > a i.material-icons { - float: left; - margin-right: 7px; - margin-top: 2px; - font-size: 20px; } - -.dropdown-animated { - -webkit-animation-duration: .3s !important; - -moz-animation-duration: .3s !important; - -o-animation-duration: .3s !important; - animation-duration: .3s !important; } - -/* Left Sidebar & Overlay ====================== */ -.overlay { - position: fixed; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: none; - z-index: 10; } - -.overlay-open .sidebar { - margin-left: 0; - z-index: 99999999; } - -.sidebar { - -moz-transition: all 0.5s; - -o-transition: all 0.5s; - -webkit-transition: all 0.5s; - transition: all 0.5s; - font-family: "Roboto", sans-serif; - background: #fdfdfd; - width: 225px; - overflow: hidden; - display: inline-block; - height: calc(100vh - 70px); - position: fixed; - top: 70px; - left: 0; - -webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); - -moz-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); - -ms-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); - box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1); - z-index: 11 !important; } - .sidebar .legal { - position: absolute; - bottom: 0; - width: 100%; - border-top: 1px solid #eee; - padding: 15px; - overflow: hidden; } - .sidebar .legal .copyright { - font-size: 13px; - white-space: nowrap; - -ms-text-overflow: ellipsis; - -o-text-overflow: ellipsis; - text-overflow: ellipsis; - overflow: hidden; } - .sidebar .legal .copyright a { - font-weight: bold; - text-decoration: none; } - .sidebar .legal .version { - white-space: nowrap; - -ms-text-overflow: ellipsis; - -o-text-overflow: ellipsis; - text-overflow: ellipsis; - overflow: hidden; - margin-top: 5px; - font-size: 13px; } - .sidebar .user-info { - padding: 13px 15px 12px 15px; - white-space: nowrap; - position: relative; - border-bottom: 1px solid #e9e9e9; - background: url("../images/user-img-background.jpg") no-repeat no-repeat; - height: 135px; } - .sidebar .user-info .image { - margin-right: 12px; - display: inline-block; } - .sidebar .user-info .image img { - -webkit-border-radius: 50%; - -moz-border-radius: 50%; - -ms-border-radius: 50%; - border-radius: 50%; - vertical-align: bottom !important; } - .sidebar .user-info .info-container { - cursor: default; - display: block; - position: relative; - top: 25px; } - .sidebar .user-info .info-container .name { - white-space: nowrap; - -ms-text-overflow: ellipsis; - -o-text-overflow: ellipsis; - text-overflow: ellipsis; - overflow: hidden; - font-size: 14px; - max-width: 200px; - color: #fff; } - .sidebar .user-info .info-container .email { - white-space: nowrap; - -ms-text-overflow: ellipsis; - -o-text-overflow: ellipsis; - text-overflow: ellipsis; - overflow: hidden; - font-size: 12px; - max-width: 200px; - color: #fff; } - .sidebar .user-info .info-container .user-helper-dropdown { - position: absolute; - right: -3px; - bottom: -12px; - -webkit-box-shadow: none; - -moz-box-shadow: none; - -ms-box-shadow: none; - box-shadow: none; - cursor: pointer; - color: #fff; } - .sidebar .menu { - position: relative; - overflow-y: auto; - height: 90vh; } - .sidebar .menu .list { - list-style: none; - padding-left: 0; } - .sidebar .menu .list li.active > :first-child span { - font-weight: bold; } - .sidebar .menu .list .header { - background: #eee; - font-size: 12px; - font-weight: 600; - padding: 8px 16px; } - .sidebar .menu .list i.material-icons { - margin-top: 4px; } - .sidebar .menu .list .menu-toggle:after, .sidebar .menu .list .menu-toggle:before { - position: absolute; - top: calc(50% - 14px); - right: 17px; - font-size: 19px; - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); - -moz-transition: all 0.3s; - -o-transition: all 0.3s; - -webkit-transition: all 0.3s; - transition: all 0.3s; } - .sidebar .menu .list .menu-toggle:before { - content: '+'; - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - -webkit-transform: scale(1); - transform: scale(1); } - .sidebar .menu .list .menu-toggle:after { - content: '\2013'; - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); } - .sidebar .menu .list .menu-toggle.toggled:before { - -moz-transform: scale(0); - -ms-transform: scale(0); - -o-transform: scale(0); - -webkit-transform: scale(0); - transform: scale(0); } - .sidebar .menu .list .menu-toggle.toggled:after { - -moz-transform: scale(1); - -ms-transform: scale(1); - -o-transform: scale(1); - -webkit-transform: scale(1); - transform: scale(1); } - .sidebar .menu .list a { - color: #747474; - position: relative; - display: inline-flex; - vertical-align: middle; - width: 100%; - padding: 10px 13px; } - .sidebar .menu .list a:hover, .sidebar .menu .list a:active, .sidebar .menu .list a:focus { - text-decoration: none !important; } - .sidebar .menu .list a small { - position: absolute; - top: calc(50% - 7.5px); - right: 15px; } - .sidebar .menu .list a span { - margin: 7px 0 7px 12px; - color: #333; - font-weight: bold; - font-size: 14px; - overflow: hidden; } - .sidebar .menu .list .ml-menu { - list-style: none; - display: none; - padding-left: 0; } - .sidebar .menu .list .ml-menu span { - font-weight: normal; - font-size: 14px; - margin: 3px 0 1px 6px; } - .sidebar .menu .list .ml-menu li a { - padding-left: 55px; - padding-top: 7px; - padding-bottom: 7px; } - .sidebar .menu .list .ml-menu li.active a.toggled:not(.menu-toggle) { - font-weight: 600; - margin-left: 5px; } - .sidebar .menu .list .ml-menu li.active a.toggled:not(.menu-toggle):before { - content: '\E315'; - font-family: 'Material Icons'; - position: relative; - font-size: 21px; - height: 20px; - top: -5px; - right: 0; } - .sidebar .menu .list .ml-menu li .ml-menu li a { - padding-left: 80px; } - .sidebar .menu .list .ml-menu li .ml-menu .ml-menu li a { - padding-left: 95px; } - -.right-sidebar { - width: 280px; - height: calc(100vh - 70px); - position: fixed; - right: -300px; - top: 70px; - background: #fdfdfd; - z-index: 11 !important; - -webkit-box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.1); - -moz-box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.1); - -ms-box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.1); - box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.1); - overflow: hidden; - -moz-transition: 0.5s; - -o-transition: 0.5s; - -webkit-transition: 0.5s; - transition: 0.5s; } - .right-sidebar.open { - right: 0; } - .right-sidebar .nav-tabs { - font-weight: 600; - font-size: 13px; - width: 100%; - margin-left: 2px; } - .right-sidebar .nav-tabs li { - text-align: center; } - .right-sidebar .nav-tabs li > a { - margin-right: 0; } - .right-sidebar .nav-tabs li:first-child { - width: 45%; } - .right-sidebar .nav-tabs li:last-child { - width: 55%; } - -/* Bootstrap Notify ============================ */ -.bootstrap-notify-container { - max-width: 320px; - text-align: center; } - -/* Jquery Nestable ============================= */ -.dd-handle { - background-color: #f9f9f9 !important; } - .dd-handle:hover { - color: #2196F3; } - -.nestable-dark-theme .dd-handle { - background: #ccc !important; - border: 1px solid #999 !important; } - -.dd3-handle { - background: #999 !important; } - -.dd3-content:hover { - color: #2196F3; } - -/* Login Page ================================== */ -.login-page { - background-color: #00BCD4; - padding-left: 0; - max-width: 360px; - margin: 5% auto; - overflow-x: hidden; } - .login-page .login-box .msg { - color: #555; - margin-bottom: 30px; - text-align: center; } - .login-page .login-box a { - font-size: 14px; - text-decoration: none; - color: #00BCD4; } - .login-page .login-box .logo { - margin-bottom: 20px; } - .login-page .login-box .logo a { - font-size: 36px; - display: block; - width: 100%; - text-align: center; - color: #fff; } - .login-page .login-box .logo small { - display: block; - width: 100%; - text-align: center; - color: #fff; - margin-top: -5px; } - -/* Sign Up Page ================================ */ -.signup-page { - background-color: #00BCD4; - padding-left: 0; - max-width: 360px; - margin: 5% auto; - overflow-x: hidden; } - .signup-page .signup-box .msg { - color: #555; - margin-bottom: 30px; - text-align: center; } - .signup-page .signup-box a { - font-size: 14px; - text-decoration: none; - color: #00BCD4; } - .signup-page .signup-box .logo { - margin-bottom: 20px; } - .signup-page .signup-box .logo a { - font-size: 36px; - display: block; - width: 100%; - text-align: center; - color: #fff; } - .signup-page .signup-box .logo small { - display: block; - width: 100%; - text-align: center; - color: #fff; - margin-top: -5px; } - -/* Forgot Password Page ======================== */ -.fp-page { - background-color: #00BCD4; - padding-left: 0; - max-width: 360px; - margin: 5% auto; - overflow-x: hidden; } - .fp-page .fp-box .msg { - color: #555; - margin-bottom: 30px; - text-align: center; } - .fp-page .fp-box a { - font-size: 14px; - text-decoration: none; - color: #00BCD4; } - .fp-page .fp-box .logo { - margin-bottom: 20px; } - .fp-page .fp-box .logo a { - font-size: 36px; - display: block; - width: 100%; - text-align: center; - color: #fff; } - .fp-page .fp-box .logo small { - display: block; - width: 100%; - text-align: center; - color: #fff; - margin-top: -5px; } - -/* 404 Not Found Page ========================== */ -.four-zero-four { - width: 100%; - text-align: center; - margin: 5% auto; } - .four-zero-four .four-zero-four-container .error-code { - font-size: 160px; } - .four-zero-four .four-zero-four-container .error-message { - font-size: 26px; color: #333; - font-weight: bold; - margin-top: -40px; } - .four-zero-four .four-zero-four-container .button-place { - margin-top: 32px; } +} -/* 500 Server Error Page ======================= */ -.five-zero-zero { - width: 100%; - text-align: center; - margin: 5% auto; } - .five-zero-zero .five-zero-zero-container .error-code { - font-size: 160px; } - .five-zero-zero .five-zero-zero-container .error-message { - font-size: 27px; - color: #333; - font-weight: bold; - margin-top: -40px; } - .five-zero-zero .five-zero-zero-container .button-place { - margin-top: 32px; } +.bg-plan { + background-color: #368F17; + color: #fff; +} -/* Maps ======================================== */ -/* Google Maps */ -.gmap { - width: 100%; - height: 400px; } +.bg-night { + background-color: #44475a; + color: #eee8d5; +} -/* jVector Map */ -.jvector-map { - width: 100%; - height: 600px; } +.col-red { + color: #F44336; +} -/* Charts ====================================== */ -/* Morris */ -.morris-hover.morris-default-style { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; } +.col-pink { + color: #E91E63; +} -/* Flot */ -.flot-chart { - width: 100%; - height: 320px; } +.col-purple { + color: #9C27B0; +} -.panel-switch-btn { - position: relative; - right: 20px; - z-index: 9; } - .panel-switch-btn label { - font-weight: bold !important; } +.col-deep-purple { + color: #673AB7; +} -.legendLabel { - width: 85px !important; - position: relative; - left: 3px; } +.col-indigo { + color: #3F51B5; +} -#multiple_axis_chart .legendLabel { - width: 160px !important; } +.col-blue { + color: #2196F3; +} -/* Sparkline */ -.sparkline { - text-align: center; } +.col-light-blue { + color: #03A9F4; +} -/* Searchbar =================================== */ -.search-bar { - position: fixed; - top: -100px; - left: 0; - z-index: 9999999; - width: 100%; - -moz-transition: 0.25s; - -o-transition: 0.25s; - -webkit-transition: 0.25s; - transition: 0.25s; } - .search-bar.open { - top: 0; } - .search-bar .search-icon { - position: absolute; - top: 20px; - left: 14px; } - .search-bar .search-icon .material-icons { - font-size: 32px; - color: #999; } - .search-bar .close-search { - position: absolute; - cursor: pointer; - font-size: 30px; - top: 16px; - right: 18px; } - .search-bar .close-search .material-icons { - color: #999; - opacity: 1; - -moz-transition: 0.5s; - -o-transition: 0.5s; - -webkit-transition: 0.5s; - transition: 0.5s; } - .search-bar .close-search .material-icons:hover { - opacity: .5; } - .search-bar input[type="text"] { - width: 100%; - font-size: 16px; - padding: 25px 60px 23px 56px; - border: none; } +.col-cyan { + color: #00BCD4; +} -/* Jquery DataTable ============================ */ -.dataTables_wrapper { - position: relative; } - .dataTables_wrapper select { - border: none; - border-bottom: 1px solid #ddd; - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - -ms-box-shadow: none; - box-shadow: none; } - .dataTables_wrapper select:active, .dataTables_wrapper select:focus { - -webkit-box-shadow: none; - -moz-box-shadow: none; - -ms-box-shadow: none; - box-shadow: none; } - .dataTables_wrapper input[type="search"] { - -webkit-border-radius: 0; - -moz-border-radius: 0; - -ms-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - -ms-box-shadow: none; - box-shadow: none; - border: none; - font-size: 12px; - border-bottom: 1px solid #ddd; } - .dataTables_wrapper input[type="search"]:focus, .dataTables_wrapper input[type="search"]:active { - border-bottom: 2px solid #1f91f3; } - .dataTables_wrapper .dt-buttons { - float: left; } - .dataTables_wrapper .dt-buttons a.dt-button { - background-color: #607D8B; - color: #fff; - padding: 7px 12px; - margin-right: 5px; - text-decoration: none; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.16), 0 2px 10px rgba(0, 0, 0, 0.12); - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -ms-border-radius: 2px; - border-radius: 2px; - border: none; - font-size: 13px; - outline: none; } - .dataTables_wrapper .dt-buttons a.dt-button:active { - opacity: 0.8; } +.col-teal { + color: #009688; +} -.dt-button-info { - position: fixed; - top: 50%; - left: 50%; - min-width: 400px; - text-align: center; - background-color: #fff; - border: 2px solid #999; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - -ms-border-radius: 3px; - border-radius: 3px; - margin-top: -100px; - margin-left: -200px; - z-index: 21; } - .dt-button-info h2 { - color: #777; } - .dt-button-info div { - color: #777; - margin-bottom: 20px; } +.col-green { + color: #4CAF50; +} -/* Light Gallery ================================ */ -.lg-outer .lg-thumb-item, -.lg-outer .lg-toogle-thumb { - -webkit-border-radius: 0 !important; - -moz-border-radius: 0 !important; - -ms-border-radius: 0 !important; - border-radius: 0 !important; } +.col-light-green { + color: #8BC34A; +} -/* For Internet Explorer 10 ===================== */ -html.ie10 .sidebar .menu .list li { - line-height: 30px; } +.col-lime { + color: #CDDC39; +} -html.ie10 .sidebar .menu .list .ml-menu li.active a:not(.menu-toggle).toggled:before { - top: 6px !important; - line-height: 20px !important; } +.col-yellow { + color: #ffe821; +} -html.ie10 .sidebar .user-info .info-container { - top: 15px; } +.col-amber { + color: #FFC107; +} -html.ie10 .search-bar input[type="text"] { - padding: 26px 60px 26px 56px; } +.col-orange { + color: #FF9800; +} -html.ie10 .dropdown-menu ul.menu li a { - margin-top: -22px; } +.col-deep-orange { + color: #FF5722; +} -html.ie10 .bs-searchbox .form-control { - width: 90%; } +.col-brown { + color: #795548; +} -/* For Internet Explorer 11 ===================== */ -html.ie11 .sidebar .menu .list .ml-menu li.active a:not(.menu-toggle).toggled:before { - top: 6px !important; - line-height: 20px !important; } +.col-grey { + color: #9E9E9E; +} -html.ie11 .sidebar .user-info .info-container { - top: 15px; } +.col-blue-grey { + color: #607D8B; +} -html.ie11 .search-bar input[type="text"] { - padding: 26px 60px 26px 56px; } +.col-black { + color: #555555; +} -html.ie11 .dropdown-menu ul.menu li a { - margin-top: -22px; } +.col-white { + color: #ffffff; +} -html.ie11 .bs-searchbox .form-control { - width: 90%; } +.col-plan { + color: #368F17; +} +/* Minecraft color codes */ +.black { + color: #000000; +} + +.darkblue { + color: #0000AA; +} + +.darkgreen { + color: #00AA00; +} + +.darkaqua { + color: #00AAAA; +} + +.darkred { + color: #AA0000; +} + +.darkpurple { + color: #AA00AA; +} + +.gold { + color: #FFAA00; +} + +.gray { + color: #AAAAAA; +} + +.darkgray { + color: #555555; +} + +.blue { + color: #5555FF; +} + +.green { + color: #55FF55; +} + +.aqua { + color: #55FFFF; +} + +.red { + color: #FF5555; +} + +.pink { + color: #FF55FF; +} + +.yellow { + color: #FFFF55; + text-shadow: 0 0 6px #000; +} + +.white { + color: #FFFFFF; + text-shadow: 0 0 8px #000; +} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/css/style.min.css b/Plan/common/src/main/resources/assets/plan/web/css/style.min.css deleted file mode 100644 index 0a2eec53f..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/style.min.css +++ /dev/null @@ -1 +0,0 @@ -@import url(materialize.css);.navbar{font-family:"Roboto",sans-serif;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;-webkit-box-shadow:0 1px 5px rgba(0,0,0,.3);-moz-box-shadow:0 1px 5px rgba(0,0,0,.3);-ms-box-shadow:0 1px 5px rgba(0,0,0,.3);box-shadow:0 1px 5px rgba(0,0,0,.3);border:none;position:fixed;top:0;left:0;z-index:12;width:100%;}.navbar .navbar-brand{white-space:nowrap;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;overflow:hidden;}.navbar .navbar-custom-right-menu{float:right;}.navbar .navbar-toggle{text-decoration:none;color:#fff;width:20px;height:20px;margin-top:-4px;margin-right:17px;}.navbar .navbar-toggle:before{content:'';font-family:'Material Icons';font-size:26px;}.navbar .navbar-collapse.in{overflow:visible;}.ls-closed .sidebar{margin-left:-300px;}.ls-closed section.content{margin-left:15px;}.ls-closed .bars:after,.ls-closed .bars:before{font-family:'Material Icons';font-size:24px;position:absolute;top:18px;left:20px;margin-right:10px;-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-moz-transition:all .3s;-o-transition:all .3s;-webkit-transition:all .3s;transition:all .3s;}.ls-closed .bars:before{content:'';-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);}.ls-closed .bars:after{content:'';-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);}.ls-closed .navbar-brand{margin-left:30px;}.overlay-open .bars:before{-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);}.overlay-open .bars:after{-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);}.navbar-header{padding:10px 7px;}.navbar-header .bars{float:left;text-decoration:none;}.navbar-nav>li>a{padding:7px 7px 2px 7px;margin-top:17px;margin-left:5px;}.navbar-nav .dropdown-menu{margin-top:-40px !important;}.label-count{position:absolute;top:2px;right:6px;font-size:10px;line-height:15px;background-color:#000;padding:0 4px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;border-radius:3px;}.col-red .navbar .navbar-brand,.col-red .navbar .navbar-brand:hover,.col-red .navbar .navbar-brand:active,.col-red .navbar .navbar-brand:focus{color:#fff;}.col-red .navbar .nav>li>a:hover,.col-red .navbar .nav>li>a:focus,.col-red .navbar .nav .open>a,.col-red .navbar .nav .open>a:hover,.col-red .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-red .navbar .nav>li>a{color:#fff;}.col-red .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-red .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-pink .navbar .navbar-brand,.col-pink .navbar .navbar-brand:hover,.col-pink .navbar .navbar-brand:active,.col-pink .navbar .navbar-brand:focus{color:#fff;}.col-pink .navbar .nav>li>a:hover,.col-pink .navbar .nav>li>a:focus,.col-pink .navbar .nav .open>a,.col-pink .navbar .nav .open>a:hover,.col-pink .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-pink .navbar .nav>li>a{color:#fff;}.col-pink .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-pink .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-purple .navbar .navbar-brand,.col-purple .navbar .navbar-brand:hover,.col-purple .navbar .navbar-brand:active,.col-purple .navbar .navbar-brand:focus{color:#fff;}.col-purple .navbar .nav>li>a:hover,.col-purple .navbar .nav>li>a:focus,.col-purple .navbar .nav .open>a,.col-purple .navbar .nav .open>a:hover,.col-purple .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-purple .navbar .nav>li>a{color:#fff;}.col-purple .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-purple .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-deep-purple .navbar .navbar-brand,.col-deep-purple .navbar .navbar-brand:hover,.col-deep-purple .navbar .navbar-brand:active,.col-deep-purple .navbar .navbar-brand:focus{color:#fff;}.col-deep-purple .navbar .nav>li>a:hover,.col-deep-purple .navbar .nav>li>a:focus,.col-deep-purple .navbar .nav .open>a,.col-deep-purple .navbar .nav .open>a:hover,.col-deep-purple .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-deep-purple .navbar .nav>li>a{color:#fff;}.col-deep-purple .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-deep-purple .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-indigo .navbar .navbar-brand,.col-indigo .navbar .navbar-brand:hover,.col-indigo .navbar .navbar-brand:active,.col-indigo .navbar .navbar-brand:focus{color:#fff;}.col-indigo .navbar .nav>li>a:hover,.col-indigo .navbar .nav>li>a:focus,.col-indigo .navbar .nav .open>a,.col-indigo .navbar .nav .open>a:hover,.col-indigo .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-indigo .navbar .nav>li>a{color:#fff;}.col-indigo .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-indigo .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-blue .navbar .navbar-brand,.col-blue .navbar .navbar-brand:hover,.col-blue .navbar .navbar-brand:active,.col-blue .navbar .navbar-brand:focus{color:#fff;}.col-blue .navbar .nav>li>a:hover,.col-blue .navbar .nav>li>a:focus,.col-blue .navbar .nav .open>a,.col-blue .navbar .nav .open>a:hover,.col-blue .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-blue .navbar .nav>li>a{color:#fff;}.col-blue .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-blue .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-light-blue .navbar .navbar-brand,.col-light-blue .navbar .navbar-brand:hover,.col-light-blue .navbar .navbar-brand:active,.col-light-blue .navbar .navbar-brand:focus{color:#fff;}.col-light-blue .navbar .nav>li>a:hover,.col-light-blue .navbar .nav>li>a:focus,.col-light-blue .navbar .nav .open>a,.col-light-blue .navbar .nav .open>a:hover,.col-light-blue .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-light-blue .navbar .nav>li>a{color:#fff;}.col-light-blue .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-light-blue .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-cyan .navbar .navbar-brand,.col-cyan .navbar .navbar-brand:hover,.col-cyan .navbar .navbar-brand:active,.col-cyan .navbar .navbar-brand:focus{color:#fff;}.col-cyan .navbar .nav>li>a:hover,.col-cyan .navbar .nav>li>a:focus,.col-cyan .navbar .nav .open>a,.col-cyan .navbar .nav .open>a:hover,.col-cyan .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-cyan .navbar .nav>li>a{color:#fff;}.col-cyan .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-cyan .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-teal .navbar .navbar-brand,.col-teal .navbar .navbar-brand:hover,.col-teal .navbar .navbar-brand:active,.col-teal .navbar .navbar-brand:focus{color:#fff;}.col-teal .navbar .nav>li>a:hover,.col-teal .navbar .nav>li>a:focus,.col-teal .navbar .nav .open>a,.col-teal .navbar .nav .open>a:hover,.col-teal .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-teal .navbar .nav>li>a{color:#fff;}.col-teal .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-teal .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-green .navbar .navbar-brand,.col-green .navbar .navbar-brand:hover,.col-green .navbar .navbar-brand:active,.col-green .navbar .navbar-brand:focus{color:#fff;}.col-green .navbar .nav>li>a:hover,.col-green .navbar .nav>li>a:focus,.col-green .navbar .nav .open>a,.col-green .navbar .nav .open>a:hover,.col-green .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-green .navbar .nav>li>a{color:#fff;}.col-green .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-green .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-light-green .navbar .navbar-brand,.col-light-green .navbar .navbar-brand:hover,.col-light-green .navbar .navbar-brand:active,.col-light-green .navbar .navbar-brand:focus{color:#fff;}.col-light-green .navbar .nav>li>a:hover,.col-light-green .navbar .nav>li>a:focus,.col-light-green .navbar .nav .open>a,.col-light-green .navbar .nav .open>a:hover,.col-light-green .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-light-green .navbar .nav>li>a{color:#fff;}.col-light-green .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-light-green .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-lime .navbar .navbar-brand,.col-lime .navbar .navbar-brand:hover,.col-lime .navbar .navbar-brand:active,.col-lime .navbar .navbar-brand:focus{color:#fff;}.col-lime .navbar .nav>li>a:hover,.col-lime .navbar .nav>li>a:focus,.col-lime .navbar .nav .open>a,.col-lime .navbar .nav .open>a:hover,.col-lime .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-lime .navbar .nav>li>a{color:#fff;}.col-lime .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-lime .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-yellow .navbar .navbar-brand,.col-yellow .navbar .navbar-brand:hover,.col-yellow .navbar .navbar-brand:active,.col-yellow .navbar .navbar-brand:focus{color:#fff;}.col-yellow .navbar .nav>li>a:hover,.col-yellow .navbar .nav>li>a:focus,.col-yellow .navbar .nav .open>a,.col-yellow .navbar .nav .open>a:hover,.col-yellow .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-yellow .navbar .nav>li>a{color:#fff;}.col-yellow .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-yellow .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-amber .navbar .navbar-brand,.col-amber .navbar .navbar-brand:hover,.col-amber .navbar .navbar-brand:active,.col-amber .navbar .navbar-brand:focus{color:#fff;}.col-amber .navbar .nav>li>a:hover,.col-amber .navbar .nav>li>a:focus,.col-amber .navbar .nav .open>a,.col-amber .navbar .nav .open>a:hover,.col-amber .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-amber .navbar .nav>li>a{color:#fff;}.col-amber .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-amber .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-orange .navbar .navbar-brand,.col-orange .navbar .navbar-brand:hover,.col-orange .navbar .navbar-brand:active,.col-orange .navbar .navbar-brand:focus{color:#fff;}.col-orange .navbar .nav>li>a:hover,.col-orange .navbar .nav>li>a:focus,.col-orange .navbar .nav .open>a,.col-orange .navbar .nav .open>a:hover,.col-orange .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-orange .navbar .nav>li>a{color:#fff;}.col-orange .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-orange .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-deep-orange .navbar .navbar-brand,.col-deep-orange .navbar .navbar-brand:hover,.col-deep-orange .navbar .navbar-brand:active,.col-deep-orange .navbar .navbar-brand:focus{color:#fff;}.col-deep-orange .navbar .nav>li>a:hover,.col-deep-orange .navbar .nav>li>a:focus,.col-deep-orange .navbar .nav .open>a,.col-deep-orange .navbar .nav .open>a:hover,.col-deep-orange .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-deep-orange .navbar .nav>li>a{color:#fff;}.col-deep-orange .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-deep-orange .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-brown .navbar .navbar-brand,.col-brown .navbar .navbar-brand:hover,.col-brown .navbar .navbar-brand:active,.col-brown .navbar .navbar-brand:focus{color:#fff;}.col-brown .navbar .nav>li>a:hover,.col-brown .navbar .nav>li>a:focus,.col-brown .navbar .nav .open>a,.col-brown .navbar .nav .open>a:hover,.col-brown .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-brown .navbar .nav>li>a{color:#fff;}.col-brown .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-brown .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-grey .navbar .navbar-brand,.col-grey .navbar .navbar-brand:hover,.col-grey .navbar .navbar-brand:active,.col-grey .navbar .navbar-brand:focus{color:#fff;}.col-grey .navbar .nav>li>a:hover,.col-grey .navbar .nav>li>a:focus,.col-grey .navbar .nav .open>a,.col-grey .navbar .nav .open>a:hover,.col-grey .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-grey .navbar .nav>li>a{color:#fff;}.col-grey .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-grey .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-blue-grey .navbar .navbar-brand,.col-blue-grey .navbar .navbar-brand:hover,.col-blue-grey .navbar .navbar-brand:active,.col-blue-grey .navbar .navbar-brand:focus{color:#fff;}.col-blue-grey .navbar .nav>li>a:hover,.col-blue-grey .navbar .nav>li>a:focus,.col-blue-grey .navbar .nav .open>a,.col-blue-grey .navbar .nav .open>a:hover,.col-blue-grey .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-blue-grey .navbar .nav>li>a{color:#fff;}.col-blue-grey .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-blue-grey .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-black .navbar .navbar-brand,.col-black .navbar .navbar-brand:hover,.col-black .navbar .navbar-brand:active,.col-black .navbar .navbar-brand:focus{color:#fff;}.col-black .navbar .nav>li>a:hover,.col-black .navbar .nav>li>a:focus,.col-black .navbar .nav .open>a,.col-black .navbar .nav .open>a:hover,.col-black .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-black .navbar .nav>li>a{color:#fff;}.col-black .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-black .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-white .navbar .navbar-brand,.col-white .navbar .navbar-brand:hover,.col-white .navbar .navbar-brand:active,.col-white .navbar .navbar-brand:focus{color:#fff;}.col-white .navbar .nav>li>a:hover,.col-white .navbar .nav>li>a:focus,.col-white .navbar .nav .open>a,.col-white .navbar .nav .open>a:hover,.col-white .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-white .navbar .nav>li>a{color:#fff;}.col-white .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-white .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.material-icons.md-18{font-size:18px;}.material-icons.md-24{font-size:24px;}.material-icons.md-26{font-size:26px;}.material-icons.md-28{font-size:28px;}.material-icons.md-30{font-size:30px;}.material-icons.md-32{font-size:32px;}.material-icons.md-36{font-size:36px;}.material-icons.md-48{font-size:48px;}.m-l--125{margin-left:-125px;}.m-t--125{margin-top:-125px;}.m-r--125{margin-right:-125px;}.m-b--125{margin-bottom:-125px;}.m-l--120{margin-left:-120px;}.m-t--120{margin-top:-120px;}.m-r--120{margin-right:-120px;}.m-b--120{margin-bottom:-120px;}.m-l--115{margin-left:-115px;}.m-t--115{margin-top:-115px;}.m-r--115{margin-right:-115px;}.m-b--115{margin-bottom:-115px;}.m-l--110{margin-left:-110px;}.m-t--110{margin-top:-110px;}.m-r--110{margin-right:-110px;}.m-b--110{margin-bottom:-110px;}.m-l--105{margin-left:-105px;}.m-t--105{margin-top:-105px;}.m-r--105{margin-right:-105px;}.m-b--105{margin-bottom:-105px;}.m-l--100{margin-left:-100px;}.m-t--100{margin-top:-100px;}.m-r--100{margin-right:-100px;}.m-b--100{margin-bottom:-100px;}.m-l--95{margin-left:-95px;}.m-t--95{margin-top:-95px;}.m-r--95{margin-right:-95px;}.m-b--95{margin-bottom:-95px;}.m-l--90{margin-left:-90px;}.m-t--90{margin-top:-90px;}.m-r--90{margin-right:-90px;}.m-b--90{margin-bottom:-90px;}.m-l--85{margin-left:-85px;}.m-t--85{margin-top:-85px;}.m-r--85{margin-right:-85px;}.m-b--85{margin-bottom:-85px;}.m-l--80{margin-left:-80px;}.m-t--80{margin-top:-80px;}.m-r--80{margin-right:-80px;}.m-b--80{margin-bottom:-80px;}.m-l--75{margin-left:-75px;}.m-t--75{margin-top:-75px;}.m-r--75{margin-right:-75px;}.m-b--75{margin-bottom:-75px;}.m-l--70{margin-left:-70px;}.m-t--70{margin-top:-70px;}.m-r--70{margin-right:-70px;}.m-b--70{margin-bottom:-70px;}.m-l--65{margin-left:-65px;}.m-t--65{margin-top:-65px;}.m-r--65{margin-right:-65px;}.m-b--65{margin-bottom:-65px;}.m-l--60{margin-left:-60px;}.m-t--60{margin-top:-60px;}.m-r--60{margin-right:-60px;}.m-b--60{margin-bottom:-60px;}.m-l--55{margin-left:-55px;}.m-t--55{margin-top:-55px;}.m-r--55{margin-right:-55px;}.m-b--55{margin-bottom:-55px;}.m-l--50{margin-left:-50px;}.m-t--50{margin-top:-50px;}.m-r--50{margin-right:-50px;}.m-b--50{margin-bottom:-50px;}.m-l--45{margin-left:-45px;}.m-t--45{margin-top:-45px;}.m-r--45{margin-right:-45px;}.m-b--45{margin-bottom:-45px;}.m-l--40{margin-left:-40px;}.m-t--40{margin-top:-40px;}.m-r--40{margin-right:-40px;}.m-b--40{margin-bottom:-40px;}.m-l--35{margin-left:-35px;}.m-t--35{margin-top:-35px;}.m-r--35{margin-right:-35px;}.m-b--35{margin-bottom:-35px;}.m-l--30{margin-left:-30px;}.m-t--30{margin-top:-30px;}.m-r--30{margin-right:-30px;}.m-b--30{margin-bottom:-30px;}.m-l--25{margin-left:-25px;}.m-t--25{margin-top:-25px;}.m-r--25{margin-right:-25px;}.m-b--25{margin-bottom:-25px;}.m-l--20{margin-left:-20px;}.m-t--20{margin-top:-20px;}.m-r--20{margin-right:-20px;}.m-b--20{margin-bottom:-20px;}.m-l--15{margin-left:-15px;}.m-t--15{margin-top:-15px;}.m-r--15{margin-right:-15px;}.m-b--15{margin-bottom:-15px;}.m-l--10{margin-left:-10px;}.m-t--10{margin-top:-10px;}.m-r--10{margin-right:-10px;}.m-b--10{margin-bottom:-10px;}.m-l--5{margin-left:-5px;}.m-t--5{margin-top:-5px;}.m-r--5{margin-right:-5px;}.m-b--5{margin-bottom:-5px;}.m-l-0{margin-left:0;}.m-t-0{margin-top:0;}.m-r-0{margin-right:0;}.m-b-0{margin-bottom:0;}.m-l-5{margin-left:5px;}.m-t-5{margin-top:5px;}.m-r-5{margin-right:5px;}.m-b-5{margin-bottom:5px;}.m-l-10{margin-left:10px;}.m-t-10{margin-top:10px;}.m-r-10{margin-right:10px;}.m-b-10{margin-bottom:10px;}.m-l-15{margin-left:15px;}.m-t-15{margin-top:15px;}.m-r-15{margin-right:15px;}.m-b-15{margin-bottom:15px;}.m-l-20{margin-left:20px;}.m-t-20{margin-top:20px;}.m-r-20{margin-right:20px;}.m-b-20{margin-bottom:20px;}.m-l-25{margin-left:25px;}.m-t-25{margin-top:25px;}.m-r-25{margin-right:25px;}.m-b-25{margin-bottom:25px;}.m-l-30{margin-left:30px;}.m-t-30{margin-top:30px;}.m-r-30{margin-right:30px;}.m-b-30{margin-bottom:30px;}.m-l-35{margin-left:35px;}.m-t-35{margin-top:35px;}.m-r-35{margin-right:35px;}.m-b-35{margin-bottom:35px;}.m-l-40{margin-left:40px;}.m-t-40{margin-top:40px;}.m-r-40{margin-right:40px;}.m-b-40{margin-bottom:40px;}.m-l-45{margin-left:45px;}.m-t-45{margin-top:45px;}.m-r-45{margin-right:45px;}.m-b-45{margin-bottom:45px;}.m-l-50{margin-left:50px;}.m-t-50{margin-top:50px;}.m-r-50{margin-right:50px;}.m-b-50{margin-bottom:50px;}.m-l-55{margin-left:55px;}.m-t-55{margin-top:55px;}.m-r-55{margin-right:55px;}.m-b-55{margin-bottom:55px;}.m-l-60{margin-left:60px;}.m-t-60{margin-top:60px;}.m-r-60{margin-right:60px;}.m-b-60{margin-bottom:60px;}.m-l-65{margin-left:65px;}.m-t-65{margin-top:65px;}.m-r-65{margin-right:65px;}.m-b-65{margin-bottom:65px;}.m-l-70{margin-left:70px;}.m-t-70{margin-top:70px;}.m-r-70{margin-right:70px;}.m-b-70{margin-bottom:70px;}.m-l-75{margin-left:75px;}.m-t-75{margin-top:75px;}.m-r-75{margin-right:75px;}.m-b-75{margin-bottom:75px;}.m-l-80{margin-left:80px;}.m-t-80{margin-top:80px;}.m-r-80{margin-right:80px;}.m-b-80{margin-bottom:80px;}.m-l-85{margin-left:85px;}.m-t-85{margin-top:85px;}.m-r-85{margin-right:85px;}.m-b-85{margin-bottom:85px;}.m-l-90{margin-left:90px;}.m-t-90{margin-top:90px;}.m-r-90{margin-right:90px;}.m-b-90{margin-bottom:90px;}.m-l-95{margin-left:95px;}.m-t-95{margin-top:95px;}.m-r-95{margin-right:95px;}.m-b-95{margin-bottom:95px;}.m-l-100{margin-left:100px;}.m-t-100{margin-top:100px;}.m-r-100{margin-right:100px;}.m-b-100{margin-bottom:100px;}.m-l-105{margin-left:105px;}.m-t-105{margin-top:105px;}.m-r-105{margin-right:105px;}.m-b-105{margin-bottom:105px;}.m-l-110{margin-left:110px;}.m-t-110{margin-top:110px;}.m-r-110{margin-right:110px;}.m-b-110{margin-bottom:110px;}.m-l-115{margin-left:115px;}.m-t-115{margin-top:115px;}.m-r-115{margin-right:115px;}.m-b-115{margin-bottom:115px;}.m-l-120{margin-left:120px;}.m-t-120{margin-top:120px;}.m-r-120{margin-right:120px;}.m-b-120{margin-bottom:120px;}.m-l-125{margin-left:125px;}.m-t-125{margin-top:125px;}.m-r-125{margin-right:125px;}.m-b-125{margin-bottom:125px;}.margin-0{margin:0;}.p-l-0{padding-left:0;}.p-t-0{padding-top:0;}.p-r-0{padding-right:0;}.p-b-0{padding-bottom:0;}.p-l-5{padding-left:5px;}.p-t-5{padding-top:5px;}.p-r-5{padding-right:5px;}.p-b-5{padding-bottom:5px;}.p-l-10{padding-left:10px;}.p-t-10{padding-top:10px;}.p-r-10{padding-right:10px;}.p-b-10{padding-bottom:10px;}.p-l-15{padding-left:15px;}.p-t-15{padding-top:15px;}.p-r-15{padding-right:15px;}.p-b-15{padding-bottom:15px;}.p-l-20{padding-left:20px;}.p-t-20{padding-top:20px;}.p-r-20{padding-right:20px;}.p-b-20{padding-bottom:20px;}.p-l-25{padding-left:25px;}.p-t-25{padding-top:25px;}.p-r-25{padding-right:25px;}.p-b-25{padding-bottom:25px;}.p-l-30{padding-left:30px;}.p-t-30{padding-top:30px;}.p-r-30{padding-right:30px;}.p-b-30{padding-bottom:30px;}.p-l-35{padding-left:35px;}.p-t-35{padding-top:35px;}.p-r-35{padding-right:35px;}.p-b-35{padding-bottom:35px;}.p-l-40{padding-left:40px;}.p-t-40{padding-top:40px;}.p-r-40{padding-right:40px;}.p-b-40{padding-bottom:40px;}.p-l-45{padding-left:45px;}.p-t-45{padding-top:45px;}.p-r-45{padding-right:45px;}.p-b-45{padding-bottom:45px;}.p-l-50{padding-left:50px;}.p-t-50{padding-top:50px;}.p-r-50{padding-right:50px;}.p-b-50{padding-bottom:50px;}.p-l-55{padding-left:55px;}.p-t-55{padding-top:55px;}.p-r-55{padding-right:55px;}.p-b-55{padding-bottom:55px;}.p-l-60{padding-left:60px;}.p-t-60{padding-top:60px;}.p-r-60{padding-right:60px;}.p-b-60{padding-bottom:60px;}.p-l-65{padding-left:65px;}.p-t-65{padding-top:65px;}.p-r-65{padding-right:65px;}.p-b-65{padding-bottom:65px;}.p-l-70{padding-left:70px;}.p-t-70{padding-top:70px;}.p-r-70{padding-right:70px;}.p-b-70{padding-bottom:70px;}.p-l-75{padding-left:75px;}.p-t-75{padding-top:75px;}.p-r-75{padding-right:75px;}.p-b-75{padding-bottom:75px;}.p-l-80{padding-left:80px;}.p-t-80{padding-top:80px;}.p-r-80{padding-right:80px;}.p-b-80{padding-bottom:80px;}.p-l-85{padding-left:85px;}.p-t-85{padding-top:85px;}.p-r-85{padding-right:85px;}.p-b-85{padding-bottom:85px;}.p-l-90{padding-left:90px;}.p-t-90{padding-top:90px;}.p-r-90{padding-right:90px;}.p-b-90{padding-bottom:90px;}.p-l-95{padding-left:95px;}.p-t-95{padding-top:95px;}.p-r-95{padding-right:95px;}.p-b-95{padding-bottom:95px;}.p-l-100{padding-left:100px;}.p-t-100{padding-top:100px;}.p-r-100{padding-right:100px;}.p-b-100{padding-bottom:100px;}.p-l-105{padding-left:105px;}.p-t-105{padding-top:105px;}.p-r-105{padding-right:105px;}.p-b-105{padding-bottom:105px;}.p-l-110{padding-left:110px;}.p-t-110{padding-top:110px;}.p-r-110{padding-right:110px;}.p-b-110{padding-bottom:110px;}.p-l-115{padding-left:115px;}.p-t-115{padding-top:115px;}.p-r-115{padding-right:115px;}.p-b-115{padding-bottom:115px;}.p-l-120{padding-left:120px;}.p-t-120{padding-top:120px;}.p-r-120{padding-right:120px;}.p-b-120{padding-bottom:120px;}.p-l-125{padding-left:125px;}.p-t-125{padding-top:125px;}.p-r-125{padding-right:125px;}.p-b-125{padding-bottom:125px;}.padding-0{padding:0;}.font-6{font-size:6px;}.font-7{font-size:7px;}.font-8{font-size:8px;}.font-9{font-size:9px;}.font-10{font-size:10px;}.font-11{font-size:11px;}.font-12{font-size:12px;}.font-13{font-size:13px;}.font-14{font-size:14px;}.font-15{font-size:15px;}.font-16{font-size:16px;}.font-17{font-size:17px;}.font-18{font-size:18px;}.font-19{font-size:19px;}.font-20{font-size:20px;}.font-21{font-size:21px;}.font-22{font-size:22px;}.font-23{font-size:23px;}.font-24{font-size:24px;}.font-25{font-size:25px;}.font-26{font-size:26px;}.font-27{font-size:27px;}.font-28{font-size:28px;}.font-29{font-size:29px;}.font-30{font-size:30px;}.font-31{font-size:31px;}.font-32{font-size:32px;}.font-33{font-size:33px;}.font-34{font-size:34px;}.font-35{font-size:35px;}.font-36{font-size:36px;}.font-37{font-size:37px;}.font-38{font-size:38px;}.font-39{font-size:39px;}.font-40{font-size:40px;}.font-41{font-size:41px;}.font-42{font-size:42px;}.font-43{font-size:43px;}.font-44{font-size:44px;}.font-45{font-size:45px;}.font-46{font-size:46px;}.font-47{font-size:47px;}.font-48{font-size:48px;}.font-49{font-size:49px;}.font-50{font-size:50px;}.align-left{text-align:left;}.align-center{text-align:center;}.align-right{text-align:right;}.align-justify{text-align:justify;}.no-resize{resize:none;}.font-bold{font-weight:bold;}.font-italic{font-style:italic;}.font-underline{text-decoration:underline;}.font-line-through{text-decoration:line-through;}.font-overline{text-decoration:overline;}.block-header{margin-bottom:15px;}.block-header h2{margin:0 !important;color:#666 !important;font-weight:normal;font-size:16px;}.block-header h2 small{display:block;font-size:12px;margin-top:8px;color:#888;}.block-header h2 small a{font-weight:bold;color:#777;}.bg-red{background-color:#f44336 !important;color:#fff;}.bg-red .content .text,.bg-red .content .number{color:#fff !important;}.bg-pink{background-color:#e91e63 !important;color:#fff;}.bg-pink .content .text,.bg-pink .content .number{color:#fff !important;}.bg-purple{background-color:#9c27b0 !important;color:#fff;}.bg-purple .content .text,.bg-purple .content .number{color:#fff !important;}.bg-deep-purple{background-color:#673ab7 !important;color:#fff;}.bg-deep-purple .content .text,.bg-deep-purple .content .number{color:#fff !important;}.bg-indigo{background-color:#3f51b5 !important;color:#fff;}.bg-indigo .content .text,.bg-indigo .content .number{color:#fff !important;}.bg-blue{background-color:#2196f3 !important;color:#fff;}.bg-blue .content .text,.bg-blue .content .number{color:#fff !important;}.bg-light-blue{background-color:#03a9f4 !important;color:#fff;}.bg-light-blue .content .text,.bg-light-blue .content .number{color:#fff !important;}.bg-cyan{background-color:#00bcd4 !important;color:#fff;}.bg-cyan .content .text,.bg-cyan .content .number{color:#fff !important;}.bg-teal{background-color:#009688 !important;color:#fff;}.bg-teal .content .text,.bg-teal .content .number{color:#fff !important;}.bg-green{background-color:#4caf50 !important;color:#fff;}.bg-green .content .text,.bg-green .content .number{color:#fff !important;}.bg-light-green{background-color:#8bc34a !important;color:#fff;}.bg-light-green .content .text,.bg-light-green .content .number{color:#fff !important;}.bg-lime{background-color:#cddc39 !important;color:#fff;}.bg-lime .content .text,.bg-lime .content .number{color:#fff !important;}.bg-yellow{background-color:#ffe821 !important;color:#fff;}.bg-yellow .content .text,.bg-yellow .content .number{color:#fff !important;}.bg-amber{background-color:#ffc107 !important;color:#fff;}.bg-amber .content .text,.bg-amber .content .number{color:#fff !important;}.bg-orange{background-color:#ff9800 !important;color:#fff;}.bg-orange .content .text,.bg-orange .content .number{color:#fff !important;}.bg-deep-orange{background-color:#ff5722 !important;color:#fff;}.bg-deep-orange .content .text,.bg-deep-orange .content .number{color:#fff !important;}.bg-brown{background-color:#795548 !important;color:#fff;}.bg-brown .content .text,.bg-brown .content .number{color:#fff !important;}.bg-grey{background-color:#9e9e9e !important;color:#fff;}.bg-grey .content .text,.bg-grey .content .number{color:#fff !important;}.bg-blue-grey{background-color:#607d8b !important;color:#fff;}.bg-blue-grey .content .text,.bg-blue-grey .content .number{color:#fff !important;}.bg-black{background-color:#000 !important;color:#fff;}.bg-black .content .text,.bg-black .content .number{color:#fff !important;}.bg-white{background-color:#fff !important;color:#fff;}.bg-white .content .text,.bg-white .content .number{color:#fff !important;}.col-red{color:#f44336 !important;}.col-pink{color:#e91e63 !important;}.col-purple{color:#9c27b0 !important;}.col-deep-purple{color:#673ab7 !important;}.col-indigo{color:#3f51b5 !important;}.col-blue{color:#2196f3 !important;}.col-light-blue{color:#03a9f4 !important;}.col-cyan{color:#00bcd4 !important;}.col-teal{color:#009688 !important;}.col-green{color:#4caf50 !important;}.col-light-green{color:#8bc34a !important;}.col-lime{color:#cddc39 !important;}.col-yellow{color:#ffe821 !important;}.col-amber{color:#ffc107 !important;}.col-orange{color:#ff9800 !important;}.col-deep-orange{color:#ff5722 !important;}.col-brown{color:#795548 !important;}.col-grey{color:#9e9e9e !important;}.col-blue-grey{color:#607d8b !important;}.col-black{color:#000 !important;}.col-white{color:#fff !important;}@-ms-keyframes spin{from{-ms-transform:rotate(0deg);-moz-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg);}to{-ms-transform:rotate(360deg);-moz-transform:rotate(360deg);-o-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@-moz-keyframes spin{from{-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg);}to{-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@-webkit-keyframes spin{from{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);transform:rotate(0deg);}to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes spin{from{-moz-transform:rotate(0deg);-ms-transform:rotate(0deg);-o-transform:rotate(0deg);-webkit-transform:rotate(0deg);transform:rotate(0deg);}to{-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg);}}.demo-button-sizes .btn{margin-bottom:5px;}.icon-button-demo button{margin-right:5px;margin-bottom:12px;}.icon-and-text-button-demo button{margin-right:5px;margin-bottom:12px;width:16.66666666666667%;}.button-demo ul{padding-left:0;}.button-demo ul li{list-style:none;padding-left:0;display:inline-block;margin-right:7px;}.button-demo ul li .btn{display:block;min-width:175px;}.button-demo .btn{margin-right:8px;margin-bottom:13px;min-width:120px;}.demo-button-groups .btn-group{margin-right:10px;}.demo-button-toolbar .btn-toolbar{float:left;margin-right:25px;}.demo-button-nesting>.btn-group{margin-right:15px;}.demo-single-button-dropdowns>.btn-group{margin-right:10px;}.demo-splite-button-dropdowns>.btn-group{margin-right:10px;}.demo-dropup .dropup{margin-right:10px;}.demo-checkbox label,.demo-radio-button label{min-width:150px;}.demo-knob-chart div{margin-right:15px;}.demo-switch .switch{display:inline-block;min-width:170px;}.demo-switch .demo-switch-title{min-width:95px;display:inline-block;}.demo-color-box{padding:15px 0;text-align:center;margin-bottom:20px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;border-radius:3px;}.demo-color-box .color-name{font-size:16px;margin-bottom:5px;}.demo-color-box .color-code,.demo-color-box .color-class-name{font-size:13px;}.demo-image-copyright{text-align:right;font-style:italic;font-size:12px;color:#777;margin:5px 0 10px 0;}.demo-image-copyright a{font-weight:bold;color:#555 !important;}.demo-tagsinput-area{margin-bottom:50px !important;}.demo-icon-container .demo-google-material-icon{margin-bottom:5px;text-align:left;}.demo-icon-container .demo-google-material-icon .icon-name{position:relative;top:-8px;left:7px;}.demo-icon-container .demo-google-material-icon .material-icons{width:24px;}.demo-preloader .preloader{margin-right:10px;}.irs-demo{margin-bottom:40px;}.irs-demo .irs{margin-top:15px;}.right-sidebar .nav-tabs+.tab-content{padding:0;}.right-sidebar p{margin:20px 15px 15px 15px;font-weight:bold;text-align:center;}.right-sidebar #settings .setting-list{list-style:none;padding-left:0;margin-bottom:20px;}.right-sidebar #settings .setting-list li{padding:15px;position:relative;border-top:1px solid #eee;}.right-sidebar #settings .setting-list li .switch{position:absolute;top:15px;right:5px;}.demo-choose-skin{list-style:none;padding-left:0;overflow-y:hidden;}.demo-choose-skin li{border-bottom:1px solid #eee;padding:10px 10px 4px 10px;position:relative;cursor:pointer;}.demo-choose-skin li.active{background-color:#eee;}.demo-choose-skin li.active:after{font-family:'Material Icons';position:absolute;top:10px;right:10px;content:'';font-size:18px;font-weight:bold;}.demo-choose-skin li:hover{background-color:#eee;}.demo-choose-skin li div{width:24px;height:24px;display:inline-block;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;border-radius:3px;}.demo-choose-skin li span{position:relative;bottom:7px;left:5px;}.demo-choose-skin .red{background-color:#f44336;}.demo-choose-skin .pink{background-color:#e91e63;}.demo-choose-skin .purple{background-color:#9c27b0;}.demo-choose-skin .deep-purple{background-color:#673ab7;}.demo-choose-skin .indigo{background-color:#3f51b5;}.demo-choose-skin .blue{background-color:#2196f3;}.demo-choose-skin .light-blue{background-color:#03a9f4;}.demo-choose-skin .cyan{background-color:#00bcd4;}.demo-choose-skin .teal{background-color:#009688;}.demo-choose-skin .green{background-color:#4caf50;}.demo-choose-skin .light-green{background-color:#8bc34a;}.demo-choose-skin .lime{background-color:#cddc39;}.demo-choose-skin .yellow{background-color:#ffe821;}.demo-choose-skin .amber{background-color:#ffc107;}.demo-choose-skin .orange{background-color:#ff9800;}.demo-choose-skin .deep-orange{background-color:#ff5722;}.demo-choose-skin .brown{background-color:#795548;}.demo-choose-skin .grey{background-color:#9e9e9e;}.demo-choose-skin .blue-grey{background-color:#607d8b;}.demo-choose-skin .black{background-color:#000;}.demo-choose-skin .white{background-color:#fff;}@media(max-width:767px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:35px;width:73%;}.navbar .navbar-header{display:inline-block;margin-bottom:-6px;width:calc(100% + 30px);}.navbar .nav>li{display:inline-block;}.navbar .navbar-nav{margin-top:-10px;margin-bottom:1px;margin-left:-7px;}.navbar .navbar-nav .open .dropdown-menu{background-color:#fff;position:absolute;}.navbar .dropdown-menu{margin-left:-50px;}.navbar .js-right-sidebar{margin-top:15px;}.dt-buttons{float:none !important;text-align:center;margin-bottom:15px;}.panel-switch-btn{top:12px;right:0 !important;}}@media(min-width:768px) and (max-width:991px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:20px;}}@media(min-width:992px) and (max-width:1169px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:20px;}}body{background-color:#e9e9e9;-moz-transition:all .5s;-o-transition:all .5s;-webkit-transition:all .5s;transition:all .5s;font-family:'Roboto',Arial,Tahoma,sans-serif;}h1,h2,h3,h4,h5,h6{font-weight:bold;}button,input,select,a{outline:none !important;}.no-animate{-o-transition-property:none !important;-moz-transition-property:none !important;-ms-transition-property:none !important;-webkit-transition-property:none !important;transition-property:none !important;-o-transform:none !important;-moz-transform:none !important;-ms-transform:none !important;-webkit-transform:none !important;transform:none !important;-webkit-animation:none !important;-moz-animation:none !important;-o-animation:none !important;-ms-animation:none !important;animation:none !important;}section.content{margin:100px 15px 0 315px;-moz-transition:.5s;-o-transition:.5s;-webkit-transition:.5s;transition:.5s;}.dashboard-flot-chart{height:275px;}.dashboard-donut-chart{height:265px;}.dashboard-line-chart{height:250px;}.dashboard-stat-list{list-style:none;padding-left:0;margin-top:40px;}.dashboard-stat-list li{padding:16px 0 0 0;}.dashboard-stat-list li small{font-size:8px;}.dashboard-task-infos .progress{height:10px;margin-bottom:0;position:relative;top:6px;}.btn:focus{outline:none !important;}.btn-circle{border:none;outline:none !important;overflow:hidden;width:40px;height:40px;-webkit-border-radius:50%;-moz-border-radius:50%;-ms-border-radius:50%;border-radius:50%;}.btn-circle i{font-size:18px;position:relative;left:-1px;}.btn-link{font-weight:bold;color:#333;-moz-transition:.5s;-o-transition:.5s;-webkit-transition:.5s;transition:.5s;}.btn-link:active,.btn-link:focus{text-decoration:none;color:#333;}.btn-link:hover{text-decoration:none;color:#333;background-color:#ddd;}.btn-circle-lg{border:none;outline:none !important;overflow:hidden;width:50px;height:50px;-webkit-border-radius:50% !important;-moz-border-radius:50% !important;-ms-border-radius:50% !important;border-radius:50% !important;}.btn-circle-lg i{font-size:26px !important;position:relative !important;left:0 !important;top:6px !important;}.btn:not(.btn-link):not(.btn-circle){box-shadow:0 2px 5px rgba(0,0,0,.16),0 2px 10px rgba(0,0,0,.12);-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;border-radius:2px;border:none;font-size:13px;outline:none;}.btn:not(.btn-link):not(.btn-circle):hover{outline:none;}.btn:not(.btn-link):not(.btn-circle) i{font-size:20px;position:relative;top:3px;}.btn:not(.btn-link):not(.btn-circle) span{position:relative;top:-2px;margin-left:3px;}.btn-warning,.btn-warning:hover,.btn-warning:active,.btn-warning:focus{background-color:#ff9600 !important;}.btn-danger,.btn-danger:hover,.btn-danger:active,.btn-danger:focus{background-color:#fb483a !important;}.btn-info,.btn-info:hover,.btn-info:active,.btn-info:focus{background-color:#00b0e4 !important;}.btn-success,.btn-success:hover,.btn-success:active,.btn-success:focus{background-color:#2b982b !important;}.btn-primary,.btn-primary:hover,.btn-primary:active,.btn-primary:focus{background-color:#1f91f3 !important;}.btn-default,.btn-default:hover,.btn-default:active,.btn-default:focus{background-color:#fff !important;}.btn-group,.btn-group-vertical{box-shadow:0 2px 5px rgba(0,0,0,.16),0 2px 10px rgba(0,0,0,.12);}.btn-group .btn,.btn-group-vertical .btn{box-shadow:none !important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.btn-group .btn .caret,.btn-group-vertical .btn .caret{position:relative;bottom:1px;}.btn-group .btn-group,.btn-group-vertical .btn-group{box-shadow:none !important;}.btn-group .btn+.dropdown-toggle,.btn-group-vertical .btn+.dropdown-toggle{border-left:1px solid rgba(0,0,0,.08) !important;}.bootstrap-tagsinput{-webkit-box-shadow:none !important;-moz-box-shadow:none !important;-ms-box-shadow:none !important;box-shadow:none !important;border:none !important;}.noUi-target{-webkit-touch-callout:none;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box;position:relative;direction:ltr;}.noUi-target *{-webkit-touch-callout:none;-webkit-user-select:none;-ms-touch-action:none;touch-action:none;-ms-user-select:none;-moz-user-select:none;user-select:none;-moz-box-sizing:border-box;box-sizing:border-box;}.noUi-base{width:100%;height:100%;position:relative;z-index:1;}.noUi-origin{position:absolute;right:0;top:6px;left:0;bottom:0;}.noUi-handle{position:relative;z-index:1;}.noUi-stacking .noUi-handle{z-index:10;}.noUi-state-tap .noUi-origin{-webkit-transition:left .25s,top .25s;transition:left .25s,top .25s;}.noUi-state-drag *{cursor:inherit !important;}.noUi-base{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}.noUi-horizontal{height:18px;}.noUi-horizontal .noUi-handle{width:34px;height:28px;left:-17px;top:-6px;}.noUi-vertical{width:18px;}.noUi-vertical .noUi-handle{width:28px;height:34px;left:-6px;top:-17px;}.noUi-background{background:#fafafa;box-shadow:inset 0 1px 1px #f0f0f0;}.noUi-connect{background:#3fb8af;box-shadow:inset 0 0 3px rgba(51,51,51,.45);-webkit-transition:background 450ms;transition:background 450ms;}.noUi-origin{border-radius:2px;}.noUi-target{border-radius:4px;border:1px solid #d3d3d3;box-shadow:inset 0 1px 1px #f0f0f0,0 3px 6px -5px #bbb;}.noUi-target.noUi-connect{box-shadow:inset 0 0 3px rgba(51,51,51,.45),0 3px 6px -5px #bbb;}.noUi-dragable{cursor:w-resize;}.noUi-vertical .noUi-dragable{cursor:n-resize;}.noUi-handle{border:1px solid #d9d9d9;border-radius:3px;background:#fff;cursor:default;box-shadow:inset 0 0 1px #fff,inset 0 1px 7px #ebebeb,0 3px 6px -3px #bbb;}.noUi-active{box-shadow:inset 0 0 1px #fff,inset 0 1px 7px #ddd,0 3px 6px -3px #bbb;}.noUi-handle:before{content:"";display:block;position:absolute;height:14px;width:1px;background:#e8e7e6;left:14px;top:6px;}.noUi-handle:after{content:"";display:block;position:absolute;height:14px;width:1px;background:#e8e7e6;left:14px;top:6px;left:17px;}.noUi-vertical .noUi-handle:before{width:14px;height:1px;left:6px;top:14px;}.noUi-vertical .noUi-handle:after{width:14px;height:1px;left:6px;top:14px;top:17px;}[disabled].noUi-connect,[disabled] .noUi-connect{background:#b8b8b8;}[disabled].noUi-origin,[disabled] .noUi-handle{cursor:not-allowed;}.noUi-target{box-shadow:none;border:none;}.noUi-base{height:15px;top:-6px;}.noUi-background{height:3px;top:6px;background-color:#bfbfbf;box-shadow:none;}.noUi-horizontal{height:3px;}.noUi-connect{height:3px;top:6px;background-color:#26a69a;box-shadow:none;}.noUi-horizontal .noUi-handle{width:15px;height:15px;border-radius:50%;box-shadow:none;background-color:#26a69a;border:none;left:-5px;top:-6px;transition:width .2s cubic-bezier(.215,.61,.355,1),height .2s cubic-bezier(.215,.61,.355,1),left .2s cubic-bezier(.215,.61,.355,1),top .2s cubic-bezier(.215,.61,.355,1);}.noUi-handle:before,.noUi-handle:after{content:none;}.noUi-target .noUi-active.noUi-handle{-webkit-box-shadow:0 0 20px rgba(0,0,0,.5);-moz-box-shadow:0 0 20px rgba(0,0,0,.5);-ms-box-shadow:0 0 20px rgba(0,0,0,.5);box-shadow:0 0 20px rgba(0,0,0,.5);}.noUi-target .range-label{position:absolute;height:30px;width:30px;top:-17px;left:-2px;background-color:#26a69a;border-radius:50%;transition:border-radius .25s cubic-bezier(.215,.61,.355,1),transform .25s cubic-bezier(.215,.61,.355,1);transform:scale(.5) rotate(-45deg);transform-origin:50% 100%;}.noUi-target .noUi-active .range-label{border-radius:15px 15px 15px 0;transform:rotate(-45deg) translate(23px,-25px);}.range-label span{width:100%;text-align:center;color:#fff;font-size:12px;transform:rotate(45deg);opacity:0;position:absolute;top:7px;left:-1px;transition:opacity .25s cubic-bezier(.215,.61,.355,1);}.noUi-active .range-label span{opacity:1;}.ms-container{width:auto !important;}.ms-container .ms-list{-webkit-box-shadow:none !important;-moz-box-shadow:none !important;-ms-box-shadow:none !important;box-shadow:none !important;-webkit-border-radius:0 !important;-moz-border-radius:0 !important;-ms-border-radius:0 !important;border-radius:0 !important;}.ms-container .ms-list.ms-focus{-webkit-box-shadow:none !important;-moz-box-shadow:none !important;-ms-box-shadow:none !important;box-shadow:none !important;}.ms-container .ms-selectable,.ms-container .ms-selection{min-width:250px !important;}.ms-container .ms-selectable li.ms-hover,.ms-container .ms-selection li.ms-hover{color:#000 !important;background-color:#e6e6e6 !important;}.ms-container .ms-selectable li.ms-elem-selectable,.ms-container .ms-selectable li.ms-elem-selection,.ms-container .ms-selection li.ms-elem-selectable,.ms-container .ms-selection li.ms-elem-selection{padding:9px 15px 6px 15px !important;}.ms-container .ms-optgroup-label{padding:5px 0 0 8px !important;}.card{background:#fff;min-height:50px;box-shadow:0 2px 10px rgba(0,0,0,.2);position:relative;margin-bottom:30px;-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;border-radius:2px;}.card .card-inside-title{margin-top:25px;margin-bottom:15px;display:block;font-size:15px;color:#000;}.card .card-inside-title small{color:#999;display:block;font-size:11px;margin-top:5px;}.card .card-inside-title small a{color:#777;font-weight:bold;}.card .card-inside-title:first-child{margin-top:0;}.card .bg-red,.card .bg-pink,.card .bg-purple,.card .bg-deep-purple,.card .bg-indigo,.card .bg-blue,.card .bg-light-blue,.card .bg-cyan,.card .bg-teal,.card .bg-green,.card .bg-light-green,.card .bg-lime,.card .bg-yellow,.card .bg-amber,.card .bg-orange,.card .bg-deep-orange,.card .bg-brown,.card .bg-grey,.card .bg-blue-grey,.card .bg-black{border-bottom:none !important;color:#fff !important;}.card .bg-red h2,.card .bg-red small,.card .bg-red .material-icons,.card .bg-pink h2,.card .bg-pink small,.card .bg-pink .material-icons,.card .bg-purple h2,.card .bg-purple small,.card .bg-purple .material-icons,.card .bg-deep-purple h2,.card .bg-deep-purple small,.card .bg-deep-purple .material-icons,.card .bg-indigo h2,.card .bg-indigo small,.card .bg-indigo .material-icons,.card .bg-blue h2,.card .bg-blue small,.card .bg-blue .material-icons,.card .bg-light-blue h2,.card .bg-light-blue small,.card .bg-light-blue .material-icons,.card .bg-cyan h2,.card .bg-cyan small,.card .bg-cyan .material-icons,.card .bg-teal h2,.card .bg-teal small,.card .bg-teal .material-icons,.card .bg-green h2,.card .bg-green small,.card .bg-green .material-icons,.card .bg-light-green h2,.card .bg-light-green small,.card .bg-light-green .material-icons,.card .bg-lime h2,.card .bg-lime small,.card .bg-lime .material-icons,.card .bg-yellow h2,.card .bg-yellow small,.card .bg-yellow .material-icons,.card .bg-amber h2,.card .bg-amber small,.card .bg-amber .material-icons,.card .bg-orange h2,.card .bg-orange small,.card .bg-orange .material-icons,.card .bg-deep-orange h2,.card .bg-deep-orange small,.card .bg-deep-orange .material-icons,.card .bg-brown h2,.card .bg-brown small,.card .bg-brown .material-icons,.card .bg-grey h2,.card .bg-grey small,.card .bg-grey .material-icons,.card .bg-blue-grey h2,.card .bg-blue-grey small,.card .bg-blue-grey .material-icons,.card .bg-black h2,.card .bg-black small,.card .bg-black .material-icons{color:#fff !important;}.card .bg-red .badge,.card .bg-pink .badge,.card .bg-purple .badge,.card .bg-deep-purple .badge,.card .bg-indigo .badge,.card .bg-blue .badge,.card .bg-light-blue .badge,.card .bg-cyan .badge,.card .bg-teal .badge,.card .bg-green .badge,.card .bg-light-green .badge,.card .bg-lime .badge,.card .bg-yellow .badge,.card .bg-amber .badge,.card .bg-orange .badge,.card .bg-deep-orange .badge,.card .bg-brown .badge,.card .bg-grey .badge,.card .bg-blue-grey .badge,.card .bg-black .badge{background-color:#fff;color:#555;}.card .header{color:#555;padding:20px;position:relative;border-bottom:1px solid rgba(204,204,204,.35);}.card .header .header-dropdown{position:absolute;top:20px;right:15px;list-style:none;}.card .header .header-dropdown .dropdown-menu li{display:block !important;}.card .header .header-dropdown li{display:inline-block;}.card .header .header-dropdown i{font-size:20px;color:#999;-moz-transition:all .5s;-o-transition:all .5s;-webkit-transition:all .5s;transition:all .5s;}.card .header .header-dropdown i:hover{color:#000;}.card .header h2{margin:0;font-size:18px;font-weight:normal;color:#111;}.card .header h2 small{display:block;font-size:12px;margin-top:5px;color:#999;line-height:15px;}.card .header h2 small a{font-weight:bold;color:#777;}.card .header .col-xs-12 h2{margin-top:5px;}.card .body{font-size:14px;color:#555;padding:20px;}.card .body .col-xs-1,.card .body .col-sm-1,.card .body .col-md-1,.card .body .col-lg-1{margin-bottom:20px;}.card .body .col-xs-2,.card .body .col-sm-2,.card .body .col-md-2,.card .body .col-lg-2{margin-bottom:20px;}.card .body .col-xs-3,.card .body .col-sm-3,.card .body .col-md-3,.card .body .col-lg-3{margin-bottom:20px;}.card .body .col-xs-4,.card .body .col-sm-4,.card .body .col-md-4,.card .body .col-lg-4{margin-bottom:20px;}.card .body .col-xs-5,.card .body .col-sm-5,.card .body .col-md-5,.card .body .col-lg-5{margin-bottom:20px;}.card .body .col-xs-6,.card .body .col-sm-6,.card .body .col-md-6,.card .body .col-lg-6{margin-bottom:20px;}.card .body .col-xs-7,.card .body .col-sm-7,.card .body .col-md-7,.card .body .col-lg-7{margin-bottom:20px;}.card .body .col-xs-8,.card .body .col-sm-8,.card .body .col-md-8,.card .body .col-lg-8{margin-bottom:20px;}.card .body .col-xs-9,.card .body .col-sm-9,.card .body .col-md-9,.card .body .col-lg-9{margin-bottom:20px;}.card .body .col-xs-10,.card .body .col-sm-10,.card .body .col-md-10,.card .body .col-lg-10{margin-bottom:20px;}.card .body .col-xs-11,.card .body .col-sm-11,.card .body .col-md-11,.card .body .col-lg-11{margin-bottom:20px;}.card .body .col-xs-12,.card .body .col-sm-12,.card .body .col-md-12,.card .body .col-lg-12{margin-bottom:20px;}.info-box{box-shadow:0 2px 10px rgba(0,0,0,.2);height:80px;display:flex;cursor:default;background-color:#fff;position:relative;overflow:hidden;margin-bottom:30px;}.info-box .icon{display:inline-block;text-align:center;background-color:rgba(0,0,0,.12);width:80px;}.info-box .icon i{color:#fff;font-size:50px;line-height:80px;}.info-box .icon .chart.chart-bar{height:100%;line-height:100px;}.info-box .icon .chart.chart-bar canvas{vertical-align:baseline !important;}.info-box .icon .chart.chart-pie{height:100%;line-height:123px;}.info-box .icon .chart.chart-pie canvas{vertical-align:baseline !important;}.info-box .icon .chart.chart-line{height:100%;line-height:115px;}.info-box .icon .chart.chart-line canvas{vertical-align:baseline !important;}.info-box .content{display:inline-block;padding:7px 10px;}.info-box .content .text{font-size:13px;margin-top:11px;color:#555;}.info-box .content .number{font-weight:normal;font-size:26px;margin-top:-4px;color:#555;}.info-box.hover-zoom-effect .icon{overflow:hidden;}.info-box.hover-zoom-effect .icon i{-moz-transition:all .3s ease;-o-transition:all .3s ease;-webkit-transition:all .3s ease;transition:all .3s ease;}.info-box.hover-zoom-effect:hover .icon i{opacity:.4;-moz-transform:rotate(-32deg) scale(1.4);-ms-transform:rotate(-32deg) scale(1.4);-o-transform:rotate(-32deg) scale(1.4);-webkit-transform:rotate(-32deg) scale(1.4);transform:rotate(-32deg) scale(1.4);}.info-box.hover-expand-effect:after{background-color:rgba(0,0,0,.05);content:".";position:absolute;left:80px;top:0;width:0;height:100%;color:transparent;-moz-transition:all .95s;-o-transition:all .95s;-webkit-transition:all .95s;transition:all .95s;}.info-box.hover-expand-effect:hover:after{width:100%;}.info-box-2{box-shadow:0 2px 10px rgba(0,0,0,.2);height:80px;display:flex;cursor:default;background-color:#fff;position:relative;overflow:hidden;margin-bottom:30px;}.info-box-2 .icon{display:inline-block;text-align:center;width:80px;}.info-box-2 .icon i{color:#fff;font-size:50px;line-height:80px;}.info-box-2 .chart.chart-bar{height:100%;line-height:105px;}.info-box-2 .chart.chart-bar canvas{vertical-align:baseline !important;}.info-box-2 .chart.chart-pie{height:100%;line-height:123px;}.info-box-2 .chart.chart-pie canvas{vertical-align:baseline !important;}.info-box-2 .chart.chart-line{height:100%;line-height:115px;}.info-box-2 .chart.chart-line canvas{vertical-align:baseline !important;}.info-box-2 .content{display:inline-block;padding:7px 10px;}.info-box-2 .content .text{font-size:13px;margin-top:11px;color:#555;}.info-box-2 .content .number{font-weight:normal;font-size:26px;margin-top:-4px;color:#555;}.info-box-2.hover-zoom-effect .icon{overflow:hidden;}.info-box-2.hover-zoom-effect .icon i{-moz-transition:all .3s ease;-o-transition:all .3s ease;-webkit-transition:all .3s ease;transition:all .3s ease;}.info-box-2.hover-zoom-effect:hover .icon i{opacity:.4;-moz-transform:rotate(-32deg) scale(1.4);-ms-transform:rotate(-32deg) scale(1.4);-o-transform:rotate(-32deg) scale(1.4);-webkit-transform:rotate(-32deg) scale(1.4);transform:rotate(-32deg) scale(1.4);}.info-box-2.hover-expand-effect:after{background-color:rgba(0,0,0,.05);content:".";position:absolute;left:0;top:0;width:0;height:100%;color:transparent;-moz-transition:all .95s;-o-transition:all .95s;-webkit-transition:all .95s;transition:all .95s;}.info-box-2.hover-expand-effect:hover:after{width:100%;}.info-box-3{box-shadow:0 2px 10px rgba(0,0,0,.2);height:80px;display:flex;cursor:default;background-color:#fff;position:relative;overflow:hidden;margin-bottom:30px;}.info-box-3 .icon{position:absolute;right:10px;bottom:2px;text-align:center;}.info-box-3 .icon i{color:rgba(0,0,0,.15);font-size:60px;}.info-box-3 .chart{margin-right:5px;}.info-box-3 .chart.chart-bar{height:100%;line-height:50px;}.info-box-3 .chart.chart-bar canvas{vertical-align:baseline !important;}.info-box-3 .chart.chart-pie{height:100%;line-height:34px;}.info-box-3 .chart.chart-pie canvas{vertical-align:baseline !important;}.info-box-3 .chart.chart-line{height:100%;line-height:40px;}.info-box-3 .chart.chart-line canvas{vertical-align:baseline !important;}.info-box-3 .content{display:inline-block;padding:7px 16px;}.info-box-3 .content .text{font-size:13px;margin-top:11px;color:#555;}.info-box-3 .content .number{font-weight:normal;font-size:26px;margin-top:-4px;color:#555;}.info-box-3.hover-zoom-effect .icon i{-moz-transition:all .3s ease;-o-transition:all .3s ease;-webkit-transition:all .3s ease;transition:all .3s ease;}.info-box-3.hover-zoom-effect:hover .icon i{opacity:.4;-moz-transform:rotate(-32deg) scale(1.4);-ms-transform:rotate(-32deg) scale(1.4);-o-transform:rotate(-32deg) scale(1.4);-webkit-transform:rotate(-32deg) scale(1.4);transform:rotate(-32deg) scale(1.4);}.info-box-3.hover-expand-effect:after{background-color:rgba(0,0,0,.05);content:".";position:absolute;left:0;top:0;width:0;height:100%;color:transparent;-moz-transition:all .95s;-o-transition:all .95s;-webkit-transition:all .95s;transition:all .95s;}.info-box-3.hover-expand-effect:hover:after{width:100%;}.info-box-4{box-shadow:0 2px 10px rgba(0,0,0,.2);height:80px;display:flex;cursor:default;background-color:#fff;position:relative;overflow:hidden;margin-bottom:30px;}.info-box-4 .icon{position:absolute;right:10px;bottom:2px;text-align:center;}.info-box-4 .icon i{color:rgba(0,0,0,.15);font-size:60px;}.info-box-4 .chart{margin-right:5px;}.info-box-4 .chart.chart-bar{height:100%;line-height:50px;}.info-box-4 .chart.chart-bar canvas{vertical-align:baseline !important;}.info-box-4 .chart.chart-pie{height:100%;line-height:34px;}.info-box-4 .chart.chart-pie canvas{vertical-align:baseline !important;}.info-box-4 .chart.chart-line{height:100%;line-height:40px;}.info-box-4 .chart.chart-line canvas{vertical-align:baseline !important;}.info-box-4 .content{display:inline-block;padding:7px 16px;}.info-box-4 .content .text{font-size:13px;margin-top:11px;color:#555;}.info-box-4 .content .number{font-weight:normal;font-size:26px;margin-top:-4px;color:#555;}.info-box-4.hover-zoom-effect .icon i{-moz-transition:all .3s ease;-o-transition:all .3s ease;-webkit-transition:all .3s ease;transition:all .3s ease;}.info-box-4.hover-zoom-effect:hover .icon i{opacity:.4;-moz-transform:rotate(-32deg) scale(1.4);-ms-transform:rotate(-32deg) scale(1.4);-o-transform:rotate(-32deg) scale(1.4);-webkit-transform:rotate(-32deg) scale(1.4);transform:rotate(-32deg) scale(1.4);}.info-box-4.hover-expand-effect:after{background-color:rgba(0,0,0,.05);content:".";position:absolute;left:0;top:0;width:0;height:100%;color:transparent;-moz-transition:all .95s;-o-transition:all .95s;-webkit-transition:all .95s;transition:all .95s;}.info-box-4.hover-expand-effect:hover:after{width:100%;}.alert{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;-ms-box-shadow:none;box-shadow:none;border:none;color:#fff !important;}.alert .alert-link{color:#fff;text-decoration:underline;font-weight:bold;}.alert-success{background-color:#2b982b;}.alert-info{background-color:#00b0e4;}.alert-warning{background-color:#ff9600 !important;}.alert-danger{background-color:#fb483a !important;}.alert-dismissible .close{color:#fff;opacity:1;border:none;text-shadow:none;}.sweet-alert{-webkit-border-radius:0 !important;-moz-border-radius:0 !important;-ms-border-radius:0 !important;border-radius:0 !important;}.sweet-alert p{font-size:14px !important;}.sweet-alert .sa-input-error{top:23px !important;right:13px !important;}.sweet-alert h2{font-size:18px !important;margin:0 0 5px 0 !important;}.sweet-alert button{font-size:15px !important;-webkit-border-radius:0 !important;-moz-border-radius:0 !important;-ms-border-radius:0 !important;border-radius:0 !important;padding:5px 20px !important;}[type="checkbox"]+label{padding-left:26px;height:25px;line-height:21px;font-size:13px;font-weight:normal;}[type="checkbox"]:checked+label:before{top:-4px;left:-2px;width:11px;height:19px;}[type="checkbox"]:checked.chk-col-red+label:before{border-right:2px solid #f44336;border-bottom:2px solid #f44336;}[type="checkbox"]:checked.chk-col-pink+label:before{border-right:2px solid #e91e63;border-bottom:2px solid #e91e63;}[type="checkbox"]:checked.chk-col-purple+label:before{border-right:2px solid #9c27b0;border-bottom:2px solid #9c27b0;}[type="checkbox"]:checked.chk-col-deep-purple+label:before{border-right:2px solid #673ab7;border-bottom:2px solid #673ab7;}[type="checkbox"]:checked.chk-col-indigo+label:before{border-right:2px solid #3f51b5;border-bottom:2px solid #3f51b5;}[type="checkbox"]:checked.chk-col-blue+label:before{border-right:2px solid #2196f3;border-bottom:2px solid #2196f3;}[type="checkbox"]:checked.chk-col-light-blue+label:before{border-right:2px solid #03a9f4;border-bottom:2px solid #03a9f4;}[type="checkbox"]:checked.chk-col-cyan+label:before{border-right:2px solid #00bcd4;border-bottom:2px solid #00bcd4;}[type="checkbox"]:checked.chk-col-teal+label:before{border-right:2px solid #009688;border-bottom:2px solid #009688;}[type="checkbox"]:checked.chk-col-green+label:before{border-right:2px solid #4caf50;border-bottom:2px solid #4caf50;}[type="checkbox"]:checked.chk-col-light-green+label:before{border-right:2px solid #8bc34a;border-bottom:2px solid #8bc34a;}[type="checkbox"]:checked.chk-col-lime+label:before{border-right:2px solid #cddc39;border-bottom:2px solid #cddc39;}[type="checkbox"]:checked.chk-col-yellow+label:before{border-right:2px solid #ffe821;border-bottom:2px solid #ffe821;}[type="checkbox"]:checked.chk-col-amber+label:before{border-right:2px solid #ffc107;border-bottom:2px solid #ffc107;}[type="checkbox"]:checked.chk-col-orange+label:before{border-right:2px solid #ff9800;border-bottom:2px solid #ff9800;}[type="checkbox"]:checked.chk-col-deep-orange+label:before{border-right:2px solid #ff5722;border-bottom:2px solid #ff5722;}[type="checkbox"]:checked.chk-col-brown+label:before{border-right:2px solid #795548;border-bottom:2px solid #795548;}[type="checkbox"]:checked.chk-col-grey+label:before{border-right:2px solid #9e9e9e;border-bottom:2px solid #9e9e9e;}[type="checkbox"]:checked.chk-col-blue-grey+label:before{border-right:2px solid #607d8b;border-bottom:2px solid #607d8b;}[type="checkbox"]:checked.chk-col-black+label:before{border-right:2px solid #000;border-bottom:2px solid #000;}[type="checkbox"]:checked.chk-col-white+label:before{border-right:2px solid #fff;border-bottom:2px solid #fff;}[type="checkbox"].filled-in:checked+label:after{top:0;width:20px;height:20px;border:2px solid #26a69a;background-color:#26a69a;z-index:0;}[type="checkbox"].filled-in:checked+label:before{border-right:2px solid #fff !important;border-bottom:2px solid #fff !important;}[type="checkbox"].filled-in:checked.chk-col-red+label:after{border:2px solid #f44336;background-color:#f44336;}[type="checkbox"].filled-in:checked.chk-col-pink+label:after{border:2px solid #e91e63;background-color:#e91e63;}[type="checkbox"].filled-in:checked.chk-col-purple+label:after{border:2px solid #9c27b0;background-color:#9c27b0;}[type="checkbox"].filled-in:checked.chk-col-deep-purple+label:after{border:2px solid #673ab7;background-color:#673ab7;}[type="checkbox"].filled-in:checked.chk-col-indigo+label:after{border:2px solid #3f51b5;background-color:#3f51b5;}[type="checkbox"].filled-in:checked.chk-col-blue+label:after{border:2px solid #2196f3;background-color:#2196f3;}[type="checkbox"].filled-in:checked.chk-col-light-blue+label:after{border:2px solid #03a9f4;background-color:#03a9f4;}[type="checkbox"].filled-in:checked.chk-col-cyan+label:after{border:2px solid #00bcd4;background-color:#00bcd4;}[type="checkbox"].filled-in:checked.chk-col-teal+label:after{border:2px solid #009688;background-color:#009688;}[type="checkbox"].filled-in:checked.chk-col-green+label:after{border:2px solid #4caf50;background-color:#4caf50;}[type="checkbox"].filled-in:checked.chk-col-light-green+label:after{border:2px solid #8bc34a;background-color:#8bc34a;}[type="checkbox"].filled-in:checked.chk-col-lime+label:after{border:2px solid #cddc39;background-color:#cddc39;}[type="checkbox"].filled-in:checked.chk-col-yellow+label:after{border:2px solid #ffe821;background-color:#ffe821;}[type="checkbox"].filled-in:checked.chk-col-amber+label:after{border:2px solid #ffc107;background-color:#ffc107;}[type="checkbox"].filled-in:checked.chk-col-orange+label:after{border:2px solid #ff9800;background-color:#ff9800;}[type="checkbox"].filled-in:checked.chk-col-deep-orange+label:after{border:2px solid #ff5722;background-color:#ff5722;}[type="checkbox"].filled-in:checked.chk-col-brown+label:after{border:2px solid #795548;background-color:#795548;}[type="checkbox"].filled-in:checked.chk-col-grey+label:after{border:2px solid #9e9e9e;background-color:#9e9e9e;}[type="checkbox"].filled-in:checked.chk-col-blue-grey+label:after{border:2px solid #607d8b;background-color:#607d8b;}[type="checkbox"].filled-in:checked.chk-col-black+label:after{border:2px solid #000;background-color:#000;}[type="checkbox"].filled-in:checked.chk-col-white+label:after{border:2px solid #fff;background-color:#fff;}[type="radio"]:not(:checked)+label{padding-left:26px;height:25px;line-height:25px;font-size:13px;font-weight:normal;}[type="radio"]:checked+label{padding-left:26px;height:25px;line-height:25px;font-size:13px;font-weight:normal;}[type="radio"].radio-col-red:checked+label:after{background-color:#f44336;border-color:#f44336;}[type="radio"].radio-col-pink:checked+label:after{background-color:#e91e63;border-color:#e91e63;}[type="radio"].radio-col-purple:checked+label:after{background-color:#9c27b0;border-color:#9c27b0;}[type="radio"].radio-col-deep-purple:checked+label:after{background-color:#673ab7;border-color:#673ab7;}[type="radio"].radio-col-indigo:checked+label:after{background-color:#3f51b5;border-color:#3f51b5;}[type="radio"].radio-col-blue:checked+label:after{background-color:#2196f3;border-color:#2196f3;}[type="radio"].radio-col-light-blue:checked+label:after{background-color:#03a9f4;border-color:#03a9f4;}[type="radio"].radio-col-cyan:checked+label:after{background-color:#00bcd4;border-color:#00bcd4;}[type="radio"].radio-col-teal:checked+label:after{background-color:#009688;border-color:#009688;}[type="radio"].radio-col-green:checked+label:after{background-color:#4caf50;border-color:#4caf50;}[type="radio"].radio-col-light-green:checked+label:after{background-color:#8bc34a;border-color:#8bc34a;}[type="radio"].radio-col-lime:checked+label:after{background-color:#cddc39;border-color:#cddc39;}[type="radio"].radio-col-yellow:checked+label:after{background-color:#ffe821;border-color:#ffe821;}[type="radio"].radio-col-amber:checked+label:after{background-color:#ffc107;border-color:#ffc107;}[type="radio"].radio-col-orange:checked+label:after{background-color:#ff9800;border-color:#ff9800;}[type="radio"].radio-col-deep-orange:checked+label:after{background-color:#ff5722;border-color:#ff5722;}[type="radio"].radio-col-brown:checked+label:after{background-color:#795548;border-color:#795548;}[type="radio"].radio-col-grey:checked+label:after{background-color:#9e9e9e;border-color:#9e9e9e;}[type="radio"].radio-col-blue-grey:checked+label:after{background-color:#607d8b;border-color:#607d8b;}[type="radio"].radio-col-black:checked+label:after{background-color:#000;border-color:#000;}[type="radio"].radio-col-white:checked+label:after{background-color:#fff;border-color:#fff;}[type="radio"].with-gap.radio-col-red:checked+label:before{border:2px solid #f44336;}[type="radio"].with-gap.radio-col-red:checked+label:after{background-color:#f44336;border:2px solid #f44336;}[type="radio"].with-gap.radio-col-pink:checked+label:before{border:2px solid #e91e63;}[type="radio"].with-gap.radio-col-pink:checked+label:after{background-color:#e91e63;border:2px solid #e91e63;}[type="radio"].with-gap.radio-col-purple:checked+label:before{border:2px solid #9c27b0;}[type="radio"].with-gap.radio-col-purple:checked+label:after{background-color:#9c27b0;border:2px solid #9c27b0;}[type="radio"].with-gap.radio-col-deep-purple:checked+label:before{border:2px solid #673ab7;}[type="radio"].with-gap.radio-col-deep-purple:checked+label:after{background-color:#673ab7;border:2px solid #673ab7;}[type="radio"].with-gap.radio-col-indigo:checked+label:before{border:2px solid #3f51b5;}[type="radio"].with-gap.radio-col-indigo:checked+label:after{background-color:#3f51b5;border:2px solid #3f51b5;}[type="radio"].with-gap.radio-col-blue:checked+label:before{border:2px solid #2196f3;}[type="radio"].with-gap.radio-col-blue:checked+label:after{background-color:#2196f3;border:2px solid #2196f3;}[type="radio"].with-gap.radio-col-light-blue:checked+label:before{border:2px solid #03a9f4;}[type="radio"].with-gap.radio-col-light-blue:checked+label:after{background-color:#03a9f4;border:2px solid #03a9f4;}[type="radio"].with-gap.radio-col-cyan:checked+label:before{border:2px solid #00bcd4;}[type="radio"].with-gap.radio-col-cyan:checked+label:after{background-color:#00bcd4;border:2px solid #00bcd4;}[type="radio"].with-gap.radio-col-teal:checked+label:before{border:2px solid #009688;}[type="radio"].with-gap.radio-col-teal:checked+label:after{background-color:#009688;border:2px solid #009688;}[type="radio"].with-gap.radio-col-green:checked+label:before{border:2px solid #4caf50;}[type="radio"].with-gap.radio-col-green:checked+label:after{background-color:#4caf50;border:2px solid #4caf50;}[type="radio"].with-gap.radio-col-light-green:checked+label:before{border:2px solid #8bc34a;}[type="radio"].with-gap.radio-col-light-green:checked+label:after{background-color:#8bc34a;border:2px solid #8bc34a;}[type="radio"].with-gap.radio-col-lime:checked+label:before{border:2px solid #cddc39;}[type="radio"].with-gap.radio-col-lime:checked+label:after{background-color:#cddc39;border:2px solid #cddc39;}[type="radio"].with-gap.radio-col-yellow:checked+label:before{border:2px solid #ffe821;}[type="radio"].with-gap.radio-col-yellow:checked+label:after{background-color:#ffe821;border:2px solid #ffe821;}[type="radio"].with-gap.radio-col-amber:checked+label:before{border:2px solid #ffc107;}[type="radio"].with-gap.radio-col-amber:checked+label:after{background-color:#ffc107;border:2px solid #ffc107;}[type="radio"].with-gap.radio-col-orange:checked+label:before{border:2px solid #ff9800;}[type="radio"].with-gap.radio-col-orange:checked+label:after{background-color:#ff9800;border:2px solid #ff9800;}[type="radio"].with-gap.radio-col-deep-orange:checked+label:before{border:2px solid #ff5722;}[type="radio"].with-gap.radio-col-deep-orange:checked+label:after{background-color:#ff5722;border:2px solid #ff5722;}[type="radio"].with-gap.radio-col-brown:checked+label:before{border:2px solid #795548;}[type="radio"].with-gap.radio-col-brown:checked+label:after{background-color:#795548;border:2px solid #795548;}[type="radio"].with-gap.radio-col-grey:checked+label:before{border:2px solid #9e9e9e;}[type="radio"].with-gap.radio-col-grey:checked+label:after{background-color:#9e9e9e;border:2px solid #9e9e9e;}[type="radio"].with-gap.radio-col-blue-grey:checked+label:before{border:2px solid #607d8b;}[type="radio"].with-gap.radio-col-blue-grey:checked+label:after{background-color:#607d8b;border:2px solid #607d8b;}[type="radio"].with-gap.radio-col-black:checked+label:before{border:2px solid #000;}[type="radio"].with-gap.radio-col-black:checked+label:after{background-color:#000;border:2px solid #000;}[type="radio"].with-gap.radio-col-white:checked+label:before{border:2px solid #fff;}[type="radio"].with-gap.radio-col-white:checked+label:after{background-color:#fff;border:2px solid #fff;}.switch label{font-weight:normal;font-size:13px;}.switch label .lever{margin:0 14px;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-red:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(244,67,54,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-red{background-color:rgba(244,67,54,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-red:after{background-color:#f44336;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-pink:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(233,30,99,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-pink{background-color:rgba(233,30,99,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-pink:after{background-color:#e91e63;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-purple:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(156,39,176,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-purple{background-color:rgba(156,39,176,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-purple:after{background-color:#9c27b0;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-deep-purple:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(103,58,183,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-deep-purple{background-color:rgba(103,58,183,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-deep-purple:after{background-color:#673ab7;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-indigo:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(63,81,181,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-indigo{background-color:rgba(63,81,181,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-indigo:after{background-color:#3f51b5;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-blue:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(33,150,243,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-blue{background-color:rgba(33,150,243,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-blue:after{background-color:#2196f3;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-light-blue:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(3,169,244,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-light-blue{background-color:rgba(3,169,244,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-light-blue:after{background-color:#03a9f4;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-cyan:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(0,188,212,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-cyan{background-color:rgba(0,188,212,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-cyan:after{background-color:#00bcd4;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-teal:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(0,150,136,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-teal{background-color:rgba(0,150,136,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-teal:after{background-color:#009688;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-green:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(76,175,80,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-green{background-color:rgba(76,175,80,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-green:after{background-color:#4caf50;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-light-green:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(139,195,74,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-light-green{background-color:rgba(139,195,74,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-light-green:after{background-color:#8bc34a;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-lime:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(205,220,57,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-lime{background-color:rgba(205,220,57,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-lime:after{background-color:#cddc39;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-yellow:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(255,232,33,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-yellow{background-color:rgba(255,232,33,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-yellow:after{background-color:#ffe821;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-amber:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(255,193,7,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-amber{background-color:rgba(255,193,7,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-amber:after{background-color:#ffc107;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-orange:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(255,152,0,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-orange{background-color:rgba(255,152,0,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-orange:after{background-color:#ff9800;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-deep-orange:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(255,87,34,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-deep-orange{background-color:rgba(255,87,34,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-deep-orange:after{background-color:#ff5722;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-brown:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(121,85,72,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-brown{background-color:rgba(121,85,72,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-brown:after{background-color:#795548;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-grey:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(158,158,158,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-grey{background-color:rgba(158,158,158,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-grey:after{background-color:#9e9e9e;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-blue-grey:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(96,125,139,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-blue-grey{background-color:rgba(96,125,139,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-blue-grey:after{background-color:#607d8b;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-black:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(0,0,0,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-black{background-color:rgba(0,0,0,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-black:after{background-color:#000;}.switch label input[type=checkbox]:checked:not(:disabled)~.lever.switch-col-white:active:after{box-shadow:0 1px 3px 1px rgba(0,0,0,.4),0 0 0 15px rgba(255,255,255,.1);}.switch label input[type=checkbox]:checked+.lever.switch-col-white{background-color:rgba(255,255,255,.5);}.switch label input[type=checkbox]:checked+.lever.switch-col-white:after{background-color:#fff;}.dtp div.dtp-date,.dtp div.dtp-time{background:#007d72;}.dtp>.dtp-content>.dtp-date-view>header.dtp-header{background:#009688;}.dtp .dtp-buttons .dtp-btn-ok{margin-left:10px;}.dtp .dtp-buttons .dtp-btn-clear{margin-right:10px !important;}.dtp .p10>a{color:#fff;}.dtp div.dtp-actual-year{font-size:1.5em;color:#fff;}.dtp table.dtp-picker-days tr td a.selected{background:#007d72;color:#fff;}.bootstrap-select{box-shadow:none !important;border-bottom:1px solid #ddd !important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.bootstrap-select .dropdown-toggle:focus,.bootstrap-select .dropdown-toggle:active{outline:none !important;}.bootstrap-select .bs-searchbox,.bootstrap-select .bs-actionsbox,.bootstrap-select .bs-donebutton{padding:0 0 5px 0;border-bottom:1px solid #e9e9e9;}.bootstrap-select .bs-searchbox .form-control,.bootstrap-select .bs-actionsbox .form-control,.bootstrap-select .bs-donebutton .form-control{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;-webkit-box-shadow:none !important;-moz-box-shadow:none !important;-ms-box-shadow:none !important;box-shadow:none !important;border:none;margin-left:30px;}.bootstrap-select .bs-searchbox{position:relative;}.bootstrap-select .bs-searchbox:after{content:'';font-family:'Material Icons';position:absolute;top:0;left:10px;font-size:25px;}.bootstrap-select ul.dropdown-menu{margin-top:0 !important;}.bootstrap-select .dropdown-menu li.selected a{background-color:#eee !important;color:#555 !important;}.bootstrap-select .dropdown-menu .active a{background-color:transparent;color:#333 !important;}.bootstrap-select .dropdown-menu .notify{background-color:#f44336 !important;color:#fff !important;border:none !important;}.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark{margin-top:9px;}.tooltip{font-size:13px;}.tooltip .tooltip-inner{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.popover{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;border:1px solid rgba(0,0,0,.08);}.popover .popover-title{font-weight:bold;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;background-color:#e9e9e9;border-bottom:1px solid #ddd;}.popover .popover-content{font-size:13px;color:#777;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.nav-tabs{border-bottom:2px solid #eee;}.nav-tabs>li{position:relative;top:3px;left:-2px;}.nav-tabs>li>a{border:none !important;color:#999 !important;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.nav-tabs>li>a:hover,.nav-tabs>li>a:active,.nav-tabs>li>a:focus{background-color:transparent !important;}.nav-tabs>li>a:before{content:'';position:absolute;left:0;width:100%;height:0;border-bottom:2px solid #2196f3;bottom:2px;-moz-transform:scaleX(0);-ms-transform:scaleX(0);-o-transform:scaleX(0);-webkit-transform:scaleX(0);transform:scaleX(0);-moz-transition:.1s ease-in;-o-transition:.1s ease-in;-webkit-transition:.1s ease-in;transition:.1s ease-in;}.nav-tabs>li>a .material-icons{position:relative;top:7px;margin-bottom:8px;}.nav-tabs li.active a{color:#222 !important;}.nav-tabs li.active a:hover,.nav-tabs li.active a:active,.nav-tabs li.active a:focus{background-color:transparent !important;}.nav-tabs li.active a:before{-moz-transform:scaleX(1);-ms-transform:scaleX(1);-o-transform:scaleX(1);-webkit-transform:scaleX(1);transform:scaleX(1);}.nav-tabs+.tab-content{padding:15px 0;}.nav-tabs.tab-col-red>li>a:before{border-bottom:2px solid #f44336;}.nav-tabs.tab-col-pink>li>a:before{border-bottom:2px solid #e91e63;}.nav-tabs.tab-col-purple>li>a:before{border-bottom:2px solid #9c27b0;}.nav-tabs.tab-col-deep-purple>li>a:before{border-bottom:2px solid #673ab7;}.nav-tabs.tab-col-indigo>li>a:before{border-bottom:2px solid #3f51b5;}.nav-tabs.tab-col-blue>li>a:before{border-bottom:2px solid #2196f3;}.nav-tabs.tab-col-light-blue>li>a:before{border-bottom:2px solid #03a9f4;}.nav-tabs.tab-col-cyan>li>a:before{border-bottom:2px solid #00bcd4;}.nav-tabs.tab-col-teal>li>a:before{border-bottom:2px solid #009688;}.nav-tabs.tab-col-green>li>a:before{border-bottom:2px solid #4caf50;}.nav-tabs.tab-col-light-green>li>a:before{border-bottom:2px solid #8bc34a;}.nav-tabs.tab-col-lime>li>a:before{border-bottom:2px solid #cddc39;}.nav-tabs.tab-col-yellow>li>a:before{border-bottom:2px solid #ffe821;}.nav-tabs.tab-col-amber>li>a:before{border-bottom:2px solid #ffc107;}.nav-tabs.tab-col-orange>li>a:before{border-bottom:2px solid #ff9800;}.nav-tabs.tab-col-deep-orange>li>a:before{border-bottom:2px solid #ff5722;}.nav-tabs.tab-col-brown>li>a:before{border-bottom:2px solid #795548;}.nav-tabs.tab-col-grey>li>a:before{border-bottom:2px solid #9e9e9e;}.nav-tabs.tab-col-blue-grey>li>a:before{border-bottom:2px solid #607d8b;}.nav-tabs.tab-col-black>li>a:before{border-bottom:2px solid #000;}.nav-tabs.tab-col-white>li>a:before{border-bottom:2px solid #fff;}.thumbnail{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.thumbnail p:not(button){color:#999;font-size:14px;}.thumbnail h3{font-weight:bold;font-size:17px;}.modal .modal-header{border:none;padding:25px 25px 5px 25px;}.modal .modal-header .modal-title{font-weight:bold;font-size:16px;}.modal .modal-content{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;box-shadow:0 5px 20px rgba(0,0,0,.31) !important;border:none;}.modal .modal-content .modal-body{color:#777;padding:15px 25px;}.modal .modal-footer{border:none;}.modal-col-red{background-color:#f44336;}.modal-col-red .modal-body,.modal-col-red .modal-title{color:#fff !important;}.modal-col-red .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-red .modal-footer .btn-link{color:#fff !important;}.modal-col-red .modal-footer .btn-link:hover,.modal-col-red .modal-footer .btn-link:active,.modal-col-red .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-pink{background-color:#e91e63;}.modal-col-pink .modal-body,.modal-col-pink .modal-title{color:#fff !important;}.modal-col-pink .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-pink .modal-footer .btn-link{color:#fff !important;}.modal-col-pink .modal-footer .btn-link:hover,.modal-col-pink .modal-footer .btn-link:active,.modal-col-pink .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-purple{background-color:#9c27b0;}.modal-col-purple .modal-body,.modal-col-purple .modal-title{color:#fff !important;}.modal-col-purple .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-purple .modal-footer .btn-link{color:#fff !important;}.modal-col-purple .modal-footer .btn-link:hover,.modal-col-purple .modal-footer .btn-link:active,.modal-col-purple .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-deep-purple{background-color:#673ab7;}.modal-col-deep-purple .modal-body,.modal-col-deep-purple .modal-title{color:#fff !important;}.modal-col-deep-purple .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-deep-purple .modal-footer .btn-link{color:#fff !important;}.modal-col-deep-purple .modal-footer .btn-link:hover,.modal-col-deep-purple .modal-footer .btn-link:active,.modal-col-deep-purple .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-indigo{background-color:#3f51b5;}.modal-col-indigo .modal-body,.modal-col-indigo .modal-title{color:#fff !important;}.modal-col-indigo .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-indigo .modal-footer .btn-link{color:#fff !important;}.modal-col-indigo .modal-footer .btn-link:hover,.modal-col-indigo .modal-footer .btn-link:active,.modal-col-indigo .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-blue{background-color:#2196f3;}.modal-col-blue .modal-body,.modal-col-blue .modal-title{color:#fff !important;}.modal-col-blue .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-blue .modal-footer .btn-link{color:#fff !important;}.modal-col-blue .modal-footer .btn-link:hover,.modal-col-blue .modal-footer .btn-link:active,.modal-col-blue .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-light-blue{background-color:#03a9f4;}.modal-col-light-blue .modal-body,.modal-col-light-blue .modal-title{color:#fff !important;}.modal-col-light-blue .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-light-blue .modal-footer .btn-link{color:#fff !important;}.modal-col-light-blue .modal-footer .btn-link:hover,.modal-col-light-blue .modal-footer .btn-link:active,.modal-col-light-blue .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-cyan{background-color:#00bcd4;}.modal-col-cyan .modal-body,.modal-col-cyan .modal-title{color:#fff !important;}.modal-col-cyan .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-cyan .modal-footer .btn-link{color:#fff !important;}.modal-col-cyan .modal-footer .btn-link:hover,.modal-col-cyan .modal-footer .btn-link:active,.modal-col-cyan .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-teal{background-color:#009688;}.modal-col-teal .modal-body,.modal-col-teal .modal-title{color:#fff !important;}.modal-col-teal .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-teal .modal-footer .btn-link{color:#fff !important;}.modal-col-teal .modal-footer .btn-link:hover,.modal-col-teal .modal-footer .btn-link:active,.modal-col-teal .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-green{background-color:#4caf50;}.modal-col-green .modal-body,.modal-col-green .modal-title{color:#fff !important;}.modal-col-green .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-green .modal-footer .btn-link{color:#fff !important;}.modal-col-green .modal-footer .btn-link:hover,.modal-col-green .modal-footer .btn-link:active,.modal-col-green .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-light-green{background-color:#8bc34a;}.modal-col-light-green .modal-body,.modal-col-light-green .modal-title{color:#fff !important;}.modal-col-light-green .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-light-green .modal-footer .btn-link{color:#fff !important;}.modal-col-light-green .modal-footer .btn-link:hover,.modal-col-light-green .modal-footer .btn-link:active,.modal-col-light-green .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-lime{background-color:#cddc39;}.modal-col-lime .modal-body,.modal-col-lime .modal-title{color:#fff !important;}.modal-col-lime .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-lime .modal-footer .btn-link{color:#fff !important;}.modal-col-lime .modal-footer .btn-link:hover,.modal-col-lime .modal-footer .btn-link:active,.modal-col-lime .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-yellow{background-color:#ffe821;}.modal-col-yellow .modal-body,.modal-col-yellow .modal-title{color:#fff !important;}.modal-col-yellow .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-yellow .modal-footer .btn-link{color:#fff !important;}.modal-col-yellow .modal-footer .btn-link:hover,.modal-col-yellow .modal-footer .btn-link:active,.modal-col-yellow .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-amber{background-color:#ffc107;}.modal-col-amber .modal-body,.modal-col-amber .modal-title{color:#fff !important;}.modal-col-amber .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-amber .modal-footer .btn-link{color:#fff !important;}.modal-col-amber .modal-footer .btn-link:hover,.modal-col-amber .modal-footer .btn-link:active,.modal-col-amber .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-orange{background-color:#ff9800;}.modal-col-orange .modal-body,.modal-col-orange .modal-title{color:#fff !important;}.modal-col-orange .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-orange .modal-footer .btn-link{color:#fff !important;}.modal-col-orange .modal-footer .btn-link:hover,.modal-col-orange .modal-footer .btn-link:active,.modal-col-orange .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-deep-orange{background-color:#ff5722;}.modal-col-deep-orange .modal-body,.modal-col-deep-orange .modal-title{color:#fff !important;}.modal-col-deep-orange .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-deep-orange .modal-footer .btn-link{color:#fff !important;}.modal-col-deep-orange .modal-footer .btn-link:hover,.modal-col-deep-orange .modal-footer .btn-link:active,.modal-col-deep-orange .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-brown{background-color:#795548;}.modal-col-brown .modal-body,.modal-col-brown .modal-title{color:#fff !important;}.modal-col-brown .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-brown .modal-footer .btn-link{color:#fff !important;}.modal-col-brown .modal-footer .btn-link:hover,.modal-col-brown .modal-footer .btn-link:active,.modal-col-brown .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-grey{background-color:#9e9e9e;}.modal-col-grey .modal-body,.modal-col-grey .modal-title{color:#fff !important;}.modal-col-grey .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-grey .modal-footer .btn-link{color:#fff !important;}.modal-col-grey .modal-footer .btn-link:hover,.modal-col-grey .modal-footer .btn-link:active,.modal-col-grey .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-blue-grey{background-color:#607d8b;}.modal-col-blue-grey .modal-body,.modal-col-blue-grey .modal-title{color:#fff !important;}.modal-col-blue-grey .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-blue-grey .modal-footer .btn-link{color:#fff !important;}.modal-col-blue-grey .modal-footer .btn-link:hover,.modal-col-blue-grey .modal-footer .btn-link:active,.modal-col-blue-grey .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-black{background-color:#000;}.modal-col-black .modal-body,.modal-col-black .modal-title{color:#fff !important;}.modal-col-black .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-black .modal-footer .btn-link{color:#fff !important;}.modal-col-black .modal-footer .btn-link:hover,.modal-col-black .modal-footer .btn-link:active,.modal-col-black .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.modal-col-white{background-color:#fff;}.modal-col-white .modal-body,.modal-col-white .modal-title{color:#fff !important;}.modal-col-white .modal-footer{background-color:rgba(0,0,0,.12);}.modal-col-white .modal-footer .btn-link{color:#fff !important;}.modal-col-white .modal-footer .btn-link:hover,.modal-col-white .modal-footer .btn-link:active,.modal-col-white .modal-footer .btn-link:focus{background-color:rgba(0,0,0,.12);}.label{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.label-primary{background-color:#1f91f3;}.label-success{background-color:#2b982b;}.label-info{background-color:#00b0e4;}.label-warning{background-color:#ff9600;}.label-danger{background-color:#fb483a;}.collapse .well,.collapse.in .well,.collapsing .well{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;margin-bottom:0;}.table tbody tr td,.table tbody tr th{padding:10px;border-top:1px solid #eee;border-bottom:1px solid #eee;}.table tbody tr.primary td,.table tbody tr.primary th{background-color:#1f91f3;color:#fff;}.table tbody tr.success td,.table tbody tr.success th{background-color:#2b982b;color:#fff;}.table tbody tr.info td,.table tbody tr.info th{background-color:#00b0e4;color:#fff;}.table tbody tr.warning td,.table tbody tr.warning th{background-color:#ff9600;color:#fff;}.table tbody tr.danger td,.table tbody tr.danger th{background-color:#fb483a;color:#fff;}.table thead tr th{padding:10px;border-bottom:1px solid #eee;}.table-bordered{border-top:1px solid #eee;}.table-bordered tbody tr td,.table-bordered tbody tr th{padding:10px;border:1px solid #eee;}.table-bordered thead tr th{padding:10px;border:1px solid #eee;}.panel-group .panel-col-red{border:1px solid #f44336;}.panel-group .panel-col-red .panel-title{background-color:#f44336 !important;color:#fff;}.panel-group .panel-col-red .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-pink{border:1px solid #e91e63;}.panel-group .panel-col-pink .panel-title{background-color:#e91e63 !important;color:#fff;}.panel-group .panel-col-pink .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-purple{border:1px solid #9c27b0;}.panel-group .panel-col-purple .panel-title{background-color:#9c27b0 !important;color:#fff;}.panel-group .panel-col-purple .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-deep-purple{border:1px solid #673ab7;}.panel-group .panel-col-deep-purple .panel-title{background-color:#673ab7 !important;color:#fff;}.panel-group .panel-col-deep-purple .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-indigo{border:1px solid #3f51b5;}.panel-group .panel-col-indigo .panel-title{background-color:#3f51b5 !important;color:#fff;}.panel-group .panel-col-indigo .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-blue{border:1px solid #2196f3;}.panel-group .panel-col-blue .panel-title{background-color:#2196f3 !important;color:#fff;}.panel-group .panel-col-blue .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-light-blue{border:1px solid #03a9f4;}.panel-group .panel-col-light-blue .panel-title{background-color:#03a9f4 !important;color:#fff;}.panel-group .panel-col-light-blue .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-cyan{border:1px solid #00bcd4;}.panel-group .panel-col-cyan .panel-title{background-color:#00bcd4 !important;color:#fff;}.panel-group .panel-col-cyan .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-teal{border:1px solid #009688;}.panel-group .panel-col-teal .panel-title{background-color:#009688 !important;color:#fff;}.panel-group .panel-col-teal .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-green{border:1px solid #4caf50;}.panel-group .panel-col-green .panel-title{background-color:#4caf50 !important;color:#fff;}.panel-group .panel-col-green .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-light-green{border:1px solid #8bc34a;}.panel-group .panel-col-light-green .panel-title{background-color:#8bc34a !important;color:#fff;}.panel-group .panel-col-light-green .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-lime{border:1px solid #cddc39;}.panel-group .panel-col-lime .panel-title{background-color:#cddc39 !important;color:#fff;}.panel-group .panel-col-lime .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-yellow{border:1px solid #ffe821;}.panel-group .panel-col-yellow .panel-title{background-color:#ffe821 !important;color:#fff;}.panel-group .panel-col-yellow .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-amber{border:1px solid #ffc107;}.panel-group .panel-col-amber .panel-title{background-color:#ffc107 !important;color:#fff;}.panel-group .panel-col-amber .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-orange{border:1px solid #ff9800;}.panel-group .panel-col-orange .panel-title{background-color:#ff9800 !important;color:#fff;}.panel-group .panel-col-orange .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-deep-orange{border:1px solid #ff5722;}.panel-group .panel-col-deep-orange .panel-title{background-color:#ff5722 !important;color:#fff;}.panel-group .panel-col-deep-orange .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-brown{border:1px solid #795548;}.panel-group .panel-col-brown .panel-title{background-color:#795548 !important;color:#fff;}.panel-group .panel-col-brown .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-grey{border:1px solid #9e9e9e;}.panel-group .panel-col-grey .panel-title{background-color:#9e9e9e !important;color:#fff;}.panel-group .panel-col-grey .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-blue-grey{border:1px solid #607d8b;}.panel-group .panel-col-blue-grey .panel-title{background-color:#607d8b !important;color:#fff;}.panel-group .panel-col-blue-grey .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-black{border:1px solid #000;}.panel-group .panel-col-black .panel-title{background-color:#000 !important;color:#fff;}.panel-group .panel-col-black .panel-body{border-top-color:transparent !important;}.panel-group .panel-col-white{border:1px solid #fff;}.panel-group .panel-col-white .panel-title{background-color:#fff !important;color:#fff;}.panel-group .panel-col-white .panel-body{border-top-color:transparent !important;}.panel-group .panel{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.panel-group .panel .panel-title .material-icons{float:left;line-height:16px;margin-right:8px;}.panel-group .panel .panel-heading{padding:0;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.panel-group .panel .panel-heading a{display:block;padding:10px 15px;}.panel-group .panel .panel-heading a:hover,.panel-group .panel .panel-heading a:focus,.panel-group .panel .panel-heading a:active{text-decoration:none;}.panel-group .panel .panel-body{color:#555;}.panel-group .panel-primary{border:1px solid #1f91f3;}.panel-group .panel-primary .panel-title{background-color:#1f91f3;}.panel-group .panel-success{border:1px solid #2b982b;}.panel-group .panel-success .panel-title{background-color:#2b982b;color:#fff;}.panel-group .panel-warning{border:1px solid #ff9600;}.panel-group .panel-warning .panel-title{background-color:#ff9600;color:#fff;}.panel-group .panel-danger{border:1px solid #fb483a;}.panel-group .panel-danger .panel-title{background-color:#fb483a;color:#fff;}.full-body .panel-col-red .panel-body{border-top-color:#fff !important;background-color:#f44336;color:#fff;}.full-body .panel-col-pink .panel-body{border-top-color:#fff !important;background-color:#e91e63;color:#fff;}.full-body .panel-col-purple .panel-body{border-top-color:#fff !important;background-color:#9c27b0;color:#fff;}.full-body .panel-col-deep-purple .panel-body{border-top-color:#fff !important;background-color:#673ab7;color:#fff;}.full-body .panel-col-indigo .panel-body{border-top-color:#fff !important;background-color:#3f51b5;color:#fff;}.full-body .panel-col-blue .panel-body{border-top-color:#fff !important;background-color:#2196f3;color:#fff;}.full-body .panel-col-light-blue .panel-body{border-top-color:#fff !important;background-color:#03a9f4;color:#fff;}.full-body .panel-col-cyan .panel-body{border-top-color:#fff !important;background-color:#00bcd4;color:#fff;}.full-body .panel-col-teal .panel-body{border-top-color:#fff !important;background-color:#009688;color:#fff;}.full-body .panel-col-green .panel-body{border-top-color:#fff !important;background-color:#4caf50;color:#fff;}.full-body .panel-col-light-green .panel-body{border-top-color:#fff !important;background-color:#8bc34a;color:#fff;}.full-body .panel-col-lime .panel-body{border-top-color:#fff !important;background-color:#cddc39;color:#fff;}.full-body .panel-col-yellow .panel-body{border-top-color:#fff !important;background-color:#ffe821;color:#fff;}.full-body .panel-col-amber .panel-body{border-top-color:#fff !important;background-color:#ffc107;color:#fff;}.full-body .panel-col-orange .panel-body{border-top-color:#fff !important;background-color:#ff9800;color:#fff;}.full-body .panel-col-deep-orange .panel-body{border-top-color:#fff !important;background-color:#ff5722;color:#fff;}.full-body .panel-col-brown .panel-body{border-top-color:#fff !important;background-color:#795548;color:#fff;}.full-body .panel-col-grey .panel-body{border-top-color:#fff !important;background-color:#9e9e9e;color:#fff;}.full-body .panel-col-blue-grey .panel-body{border-top-color:#fff !important;background-color:#607d8b;color:#fff;}.full-body .panel-col-black .panel-body{border-top-color:#fff !important;background-color:#000;color:#fff;}.full-body .panel-col-white .panel-body{border-top-color:#fff !important;background-color:#fff;color:#fff;}.full-body .panel-primary .panel-body{border-top-color:#fff !important;background-color:#1f91f3;color:#fff;}.full-body .panel-success .panel-body{border-top-color:#fff !important;background-color:#2b982b;color:#fff;}.full-body .panel-warning .panel-body{border-top-color:#fff !important;background-color:#ff9600;color:#fff;}.full-body .panel-danger .panel-body{border-top-color:#fff !important;background-color:#fb483a;color:#fff;}.progress{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;height:22px;}.progress .progress-bar{line-height:23px;background-color:#1f91f3;}.progress .progress-bar-success{background-color:#2b982b;}.progress .progress-bar-info{background-color:#00b0e4;}.progress .progress-bar-warning{background-color:#ff9600;}.progress .progress-bar-danger{background-color:#fb483a;}.irs .irs-min,.irs .irs-max,.irs .irs-from,.irs .irs-to,.irs .irs-single{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.input-group{width:100%;margin-bottom:20px;}.input-group .form-line{display:inline-block;width:100%;border-bottom:1px solid #ddd;position:relative;}.input-group .form-line:after{content:'';position:absolute;left:0;width:100%;bottom:-2px;-moz-transform:scaleX(0);-ms-transform:scaleX(0);-o-transform:scaleX(0);-webkit-transform:scaleX(0);transform:scaleX(0);-moz-transition:.25s ease-in;-o-transition:.25s ease-in;-webkit-transition:.25s ease-in;transition:.25s ease-in;border-bottom:2px solid #1f91f3;}.input-group .form-line+.input-group-addon{padding-right:0;padding-left:10px;}.input-group .help-info{float:right;font-size:12px;margin-top:5px;color:#999;}.input-group label.error{font-size:12px;display:block;margin-top:5px;font-weight:normal;color:#f44336;}.input-group .form-line.error:after{border-bottom:2px solid #f44336;}.input-group .form-line.success:after{border-bottom:2px solid #4caf50;}.input-group .form-line.warning:after{border-bottom:2px solid #ffc107;}.input-group .form-line.focused:after{-moz-transform:scaleX(1);-ms-transform:scaleX(1);-o-transform:scaleX(1);-webkit-transform:scaleX(1);transform:scaleX(1);}.input-group .form-line.focused .form-label{bottom:25px;left:0;font-size:12px;}.input-group .input-group-addon{border:none;background-color:transparent;padding-left:0;font-weight:bold;}.input-group .input-group-addon .material-icons{font-size:18px;color:#555;}.input-group input[type="text"],.input-group .form-control{border:none;box-shadow:none;padding-left:0;}.input-group .form-control:focus{-webkit-box-shadow:none !important;-moz-box-shadow:none !important;-ms-box-shadow:none !important;box-shadow:none !important;}.input-group.input-group-sm .input-group-addon i{font-size:14px;}.input-group.input-group-sm .form-control{font-size:12px;}.input-group.input-group-lg .input-group-addon i{font-size:26px;}.input-group.input-group-lg .form-control{font-size:18px;}.form-control-label{text-align:right;}.form-control-label label{margin-top:8px;}.form-horizontal .form-group{margin-bottom:0;}.form-group{width:100%;margin-bottom:25px;}.form-group .form-control{width:100%;border:none;box-shadow:none;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;padding-left:0;}.form-group .help-info{float:right;font-size:12px;margin-top:5px;color:#999;}.form-group label.error{font-size:12px;display:block;margin-top:5px;font-weight:normal;color:#f44336;}.form-group .form-line{width:100%;position:relative;border-bottom:1px solid #ddd;}.form-group .form-line:after{content:'';position:absolute;left:0;width:100%;height:0;bottom:-1px;-moz-transform:scaleX(0);-ms-transform:scaleX(0);-o-transform:scaleX(0);-webkit-transform:scaleX(0);transform:scaleX(0);-moz-transition:.25s ease-in;-o-transition:.25s ease-in;-webkit-transition:.25s ease-in;transition:.25s ease-in;border-bottom:2px solid #1f91f3;}.form-group .form-line .form-label{font-weight:normal;color:#aaa;position:absolute;top:10px;left:0;cursor:text;-moz-transition:.2s;-o-transition:.2s;-webkit-transition:.2s;transition:.2s;}.form-group .form-line.error:after{border-bottom:2px solid #f44336;}.form-group .form-line.success:after{border-bottom:2px solid #4caf50;}.form-group .form-line.warning:after{border-bottom:2px solid #ffc107;}.form-group .form-line.focused:after{-moz-transform:scaleX(1);-ms-transform:scaleX(1);-o-transform:scaleX(1);-webkit-transform:scaleX(1);transform:scaleX(1);}.form-group .form-line.focused .form-label{top:-10px;left:0;font-size:12px;}.form-group-sm .form-label{font-size:12px;}.form-group-sm .form-line.focused .form-label{bottom:20px;font-size:10px;}.form-group-lg .form-label{font-size:18px;}.form-group-lg .form-line.focused .form-label{bottom:35px;font-size:12px;}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:transparent;}.colorpicker{z-index:1;}.colorpicker:before,.colorpicker:after{display:none !important;}.dropzone{border:2px solid transparent !important;background-color:#eee !important;}.dropzone .dz-message .drag-icon-cph .material-icons{font-size:80px;color:#777;}.dz-drag-hover{border:2px dashed #888 !important;}.breadcrumb{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;background-color:transparent;font-size:13px;margin-bottom:10px;}.breadcrumb li a{color:#444;text-decoration:none;}.breadcrumb li a .material-icons{font-size:18px;position:relative;top:4px;}.breadcrumb li .material-icons{font-size:18px;position:relative;top:4px;}.breadcrumb>li+li:before{content:'> ';}.breadcrumb-col-red li a{color:#f44336 !important;font-weight:bold;}.breadcrumb-bg-red{background-color:#f44336 !important;}.breadcrumb-bg-red li{color:#fff !important;}.breadcrumb-bg-red li a{color:#fff;font-weight:bold;}.breadcrumb-bg-red li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-red li+li:before{color:#fff;}.breadcrumb-col-pink li a{color:#e91e63 !important;font-weight:bold;}.breadcrumb-bg-pink{background-color:#e91e63 !important;}.breadcrumb-bg-pink li{color:#fff !important;}.breadcrumb-bg-pink li a{color:#fff;font-weight:bold;}.breadcrumb-bg-pink li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-pink li+li:before{color:#fff;}.breadcrumb-col-purple li a{color:#9c27b0 !important;font-weight:bold;}.breadcrumb-bg-purple{background-color:#9c27b0 !important;}.breadcrumb-bg-purple li{color:#fff !important;}.breadcrumb-bg-purple li a{color:#fff;font-weight:bold;}.breadcrumb-bg-purple li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-purple li+li:before{color:#fff;}.breadcrumb-col-deep-purple li a{color:#673ab7 !important;font-weight:bold;}.breadcrumb-bg-deep-purple{background-color:#673ab7 !important;}.breadcrumb-bg-deep-purple li{color:#fff !important;}.breadcrumb-bg-deep-purple li a{color:#fff;font-weight:bold;}.breadcrumb-bg-deep-purple li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-deep-purple li+li:before{color:#fff;}.breadcrumb-col-indigo li a{color:#3f51b5 !important;font-weight:bold;}.breadcrumb-bg-indigo{background-color:#3f51b5 !important;}.breadcrumb-bg-indigo li{color:#fff !important;}.breadcrumb-bg-indigo li a{color:#fff;font-weight:bold;}.breadcrumb-bg-indigo li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-indigo li+li:before{color:#fff;}.breadcrumb-col-blue li a{color:#2196f3 !important;font-weight:bold;}.breadcrumb-bg-blue{background-color:#2196f3 !important;}.breadcrumb-bg-blue li{color:#fff !important;}.breadcrumb-bg-blue li a{color:#fff;font-weight:bold;}.breadcrumb-bg-blue li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-blue li+li:before{color:#fff;}.breadcrumb-col-light-blue li a{color:#03a9f4 !important;font-weight:bold;}.breadcrumb-bg-light-blue{background-color:#03a9f4 !important;}.breadcrumb-bg-light-blue li{color:#fff !important;}.breadcrumb-bg-light-blue li a{color:#fff;font-weight:bold;}.breadcrumb-bg-light-blue li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-light-blue li+li:before{color:#fff;}.breadcrumb-col-cyan li a{color:#00bcd4 !important;font-weight:bold;}.breadcrumb-bg-cyan{background-color:#00bcd4 !important;}.breadcrumb-bg-cyan li{color:#fff !important;}.breadcrumb-bg-cyan li a{color:#fff;font-weight:bold;}.breadcrumb-bg-cyan li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-cyan li+li:before{color:#fff;}.breadcrumb-col-teal li a{color:#009688 !important;font-weight:bold;}.breadcrumb-bg-teal{background-color:#009688 !important;}.breadcrumb-bg-teal li{color:#fff !important;}.breadcrumb-bg-teal li a{color:#fff;font-weight:bold;}.breadcrumb-bg-teal li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-teal li+li:before{color:#fff;}.breadcrumb-col-green li a{color:#4caf50 !important;font-weight:bold;}.breadcrumb-bg-green{background-color:#4caf50 !important;}.breadcrumb-bg-green li{color:#fff !important;}.breadcrumb-bg-green li a{color:#fff;font-weight:bold;}.breadcrumb-bg-green li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-green li+li:before{color:#fff;}.breadcrumb-col-light-green li a{color:#8bc34a !important;font-weight:bold;}.breadcrumb-bg-light-green{background-color:#8bc34a !important;}.breadcrumb-bg-light-green li{color:#fff !important;}.breadcrumb-bg-light-green li a{color:#fff;font-weight:bold;}.breadcrumb-bg-light-green li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-light-green li+li:before{color:#fff;}.breadcrumb-col-lime li a{color:#cddc39 !important;font-weight:bold;}.breadcrumb-bg-lime{background-color:#cddc39 !important;}.breadcrumb-bg-lime li{color:#fff !important;}.breadcrumb-bg-lime li a{color:#fff;font-weight:bold;}.breadcrumb-bg-lime li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-lime li+li:before{color:#fff;}.breadcrumb-col-yellow li a{color:#ffe821 !important;font-weight:bold;}.breadcrumb-bg-yellow{background-color:#ffe821 !important;}.breadcrumb-bg-yellow li{color:#fff !important;}.breadcrumb-bg-yellow li a{color:#fff;font-weight:bold;}.breadcrumb-bg-yellow li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-yellow li+li:before{color:#fff;}.breadcrumb-col-amber li a{color:#ffc107 !important;font-weight:bold;}.breadcrumb-bg-amber{background-color:#ffc107 !important;}.breadcrumb-bg-amber li{color:#fff !important;}.breadcrumb-bg-amber li a{color:#fff;font-weight:bold;}.breadcrumb-bg-amber li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-amber li+li:before{color:#fff;}.breadcrumb-col-orange li a{color:#ff9800 !important;font-weight:bold;}.breadcrumb-bg-orange{background-color:#ff9800 !important;}.breadcrumb-bg-orange li{color:#fff !important;}.breadcrumb-bg-orange li a{color:#fff;font-weight:bold;}.breadcrumb-bg-orange li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-orange li+li:before{color:#fff;}.breadcrumb-col-deep-orange li a{color:#ff5722 !important;font-weight:bold;}.breadcrumb-bg-deep-orange{background-color:#ff5722 !important;}.breadcrumb-bg-deep-orange li{color:#fff !important;}.breadcrumb-bg-deep-orange li a{color:#fff;font-weight:bold;}.breadcrumb-bg-deep-orange li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-deep-orange li+li:before{color:#fff;}.breadcrumb-col-brown li a{color:#795548 !important;font-weight:bold;}.breadcrumb-bg-brown{background-color:#795548 !important;}.breadcrumb-bg-brown li{color:#fff !important;}.breadcrumb-bg-brown li a{color:#fff;font-weight:bold;}.breadcrumb-bg-brown li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-brown li+li:before{color:#fff;}.breadcrumb-col-grey li a{color:#9e9e9e !important;font-weight:bold;}.breadcrumb-bg-grey{background-color:#9e9e9e !important;}.breadcrumb-bg-grey li{color:#fff !important;}.breadcrumb-bg-grey li a{color:#fff;font-weight:bold;}.breadcrumb-bg-grey li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-grey li+li:before{color:#fff;}.breadcrumb-col-blue-grey li a{color:#607d8b !important;font-weight:bold;}.breadcrumb-bg-blue-grey{background-color:#607d8b !important;}.breadcrumb-bg-blue-grey li{color:#fff !important;}.breadcrumb-bg-blue-grey li a{color:#fff;font-weight:bold;}.breadcrumb-bg-blue-grey li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-blue-grey li+li:before{color:#fff;}.breadcrumb-col-black li a{color:#000 !important;font-weight:bold;}.breadcrumb-bg-black{background-color:#000 !important;}.breadcrumb-bg-black li{color:#fff !important;}.breadcrumb-bg-black li a{color:#fff;font-weight:bold;}.breadcrumb-bg-black li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-black li+li:before{color:#fff;}.breadcrumb-col-white li a{color:#fff !important;font-weight:bold;}.breadcrumb-bg-white{background-color:#fff !important;}.breadcrumb-bg-white li{color:#fff !important;}.breadcrumb-bg-white li a{color:#fff;font-weight:bold;}.breadcrumb-bg-white li a .material-icons{padding-bottom:8px;}.breadcrumb-bg-white li+li:before{color:#fff;}.badge{-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;border-radius:2px;}.list-group-item{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;-moz-transition:.5s;-o-transition:.5s;-webkit-transition:.5s;transition:.5s;}.list-group .active{background-color:#2196f3;border-color:#2196f3;}.list-group .active:hover,.list-group .active:focus,.list-group .active:active{background-color:#2196f3;border-color:#2196f3;}.list-group .active .list-group-item-text{color:#dfe9f1;font-size:13px;}.list-group .active .list-group-item-text:hover,.list-group .active .list-group-item-text:active,.list-group .active .list-group-item-text:focus{color:#dfe9f1;}.list-group .list-group-item.active:hover .list-group-item-text,.list-group .list-group-item.active:focus .list-group-item-text,.list-group .list-group-item.active:active .list-group-item-text{color:#dfe9f1;}.list-group .list-group-item:first-child,.list-group .list-group-item:last-child{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.list-group .list-group-item .list-group-item-heading{font-weight:bold;font-size:17px;}.list-group .list-group-item-success{background-color:#2b982b;border:none;color:#fff;}.list-group .list-group-item-success:hover,.list-group .list-group-item-success:focus{background-color:#2b982b;color:#fff;opacity:.8;}.list-group .list-group-item-info{background-color:#00b0e4;border:none;color:#fff;}.list-group .list-group-item-info:hover,.list-group .list-group-item-info:focus{background-color:#00b0e4;color:#fff;opacity:.8;}.list-group .list-group-item-warning{background-color:#ff9600;border:none;color:#fff;}.list-group .list-group-item-warning:hover,.list-group .list-group-item-warning:focus{background-color:#ff9600;color:#fff;opacity:.8;}.list-group .list-group-item-danger{background-color:#fb483a;border:none;color:#fff;}.list-group .list-group-item-danger:hover,.list-group .list-group-item-danger:focus{background-color:#fb483a;color:#fff;opacity:.8;}.list-group .pl-red{stroke:#f44336;}.list-group .list-group-bg-red{background-color:#f44336;border:none;color:#fff;}.list-group .list-group-bg-red:hover,.list-group .list-group-bg-red:focus{background-color:#f44336;color:#fff;opacity:.8;}.list-group .pl-pink{stroke:#e91e63;}.list-group .list-group-bg-pink{background-color:#e91e63;border:none;color:#fff;}.list-group .list-group-bg-pink:hover,.list-group .list-group-bg-pink:focus{background-color:#e91e63;color:#fff;opacity:.8;}.list-group .pl-purple{stroke:#9c27b0;}.list-group .list-group-bg-purple{background-color:#9c27b0;border:none;color:#fff;}.list-group .list-group-bg-purple:hover,.list-group .list-group-bg-purple:focus{background-color:#9c27b0;color:#fff;opacity:.8;}.list-group .pl-deep-purple{stroke:#673ab7;}.list-group .list-group-bg-deep-purple{background-color:#673ab7;border:none;color:#fff;}.list-group .list-group-bg-deep-purple:hover,.list-group .list-group-bg-deep-purple:focus{background-color:#673ab7;color:#fff;opacity:.8;}.list-group .pl-indigo{stroke:#3f51b5;}.list-group .list-group-bg-indigo{background-color:#3f51b5;border:none;color:#fff;}.list-group .list-group-bg-indigo:hover,.list-group .list-group-bg-indigo:focus{background-color:#3f51b5;color:#fff;opacity:.8;}.list-group .pl-blue{stroke:#2196f3;}.list-group .list-group-bg-blue{background-color:#2196f3;border:none;color:#fff;}.list-group .list-group-bg-blue:hover,.list-group .list-group-bg-blue:focus{background-color:#2196f3;color:#fff;opacity:.8;}.list-group .pl-light-blue{stroke:#03a9f4;}.list-group .list-group-bg-light-blue{background-color:#03a9f4;border:none;color:#fff;}.list-group .list-group-bg-light-blue:hover,.list-group .list-group-bg-light-blue:focus{background-color:#03a9f4;color:#fff;opacity:.8;}.list-group .pl-cyan{stroke:#00bcd4;}.list-group .list-group-bg-cyan{background-color:#00bcd4;border:none;color:#fff;}.list-group .list-group-bg-cyan:hover,.list-group .list-group-bg-cyan:focus{background-color:#00bcd4;color:#fff;opacity:.8;}.list-group .pl-teal{stroke:#009688;}.list-group .list-group-bg-teal{background-color:#009688;border:none;color:#fff;}.list-group .list-group-bg-teal:hover,.list-group .list-group-bg-teal:focus{background-color:#009688;color:#fff;opacity:.8;}.list-group .pl-green{stroke:#4caf50;}.list-group .list-group-bg-green{background-color:#4caf50;border:none;color:#fff;}.list-group .list-group-bg-green:hover,.list-group .list-group-bg-green:focus{background-color:#4caf50;color:#fff;opacity:.8;}.list-group .pl-light-green{stroke:#8bc34a;}.list-group .list-group-bg-light-green{background-color:#8bc34a;border:none;color:#fff;}.list-group .list-group-bg-light-green:hover,.list-group .list-group-bg-light-green:focus{background-color:#8bc34a;color:#fff;opacity:.8;}.list-group .pl-lime{stroke:#cddc39;}.list-group .list-group-bg-lime{background-color:#cddc39;border:none;color:#fff;}.list-group .list-group-bg-lime:hover,.list-group .list-group-bg-lime:focus{background-color:#cddc39;color:#fff;opacity:.8;}.list-group .pl-yellow{stroke:#ffe821;}.list-group .list-group-bg-yellow{background-color:#ffe821;border:none;color:#fff;}.list-group .list-group-bg-yellow:hover,.list-group .list-group-bg-yellow:focus{background-color:#ffe821;color:#fff;opacity:.8;}.list-group .pl-amber{stroke:#ffc107;}.list-group .list-group-bg-amber{background-color:#ffc107;border:none;color:#fff;}.list-group .list-group-bg-amber:hover,.list-group .list-group-bg-amber:focus{background-color:#ffc107;color:#fff;opacity:.8;}.list-group .pl-orange{stroke:#ff9800;}.list-group .list-group-bg-orange{background-color:#ff9800;border:none;color:#fff;}.list-group .list-group-bg-orange:hover,.list-group .list-group-bg-orange:focus{background-color:#ff9800;color:#fff;opacity:.8;}.list-group .pl-deep-orange{stroke:#ff5722;}.list-group .list-group-bg-deep-orange{background-color:#ff5722;border:none;color:#fff;}.list-group .list-group-bg-deep-orange:hover,.list-group .list-group-bg-deep-orange:focus{background-color:#ff5722;color:#fff;opacity:.8;}.list-group .pl-brown{stroke:#795548;}.list-group .list-group-bg-brown{background-color:#795548;border:none;color:#fff;}.list-group .list-group-bg-brown:hover,.list-group .list-group-bg-brown:focus{background-color:#795548;color:#fff;opacity:.8;}.list-group .pl-grey{stroke:#9e9e9e;}.list-group .list-group-bg-grey{background-color:#9e9e9e;border:none;color:#fff;}.list-group .list-group-bg-grey:hover,.list-group .list-group-bg-grey:focus{background-color:#9e9e9e;color:#fff;opacity:.8;}.list-group .pl-blue-grey{stroke:#607d8b;}.list-group .list-group-bg-blue-grey{background-color:#607d8b;border:none;color:#fff;}.list-group .list-group-bg-blue-grey:hover,.list-group .list-group-bg-blue-grey:focus{background-color:#607d8b;color:#fff;opacity:.8;}.list-group .pl-black{stroke:#000;}.list-group .list-group-bg-black{background-color:#000;border:none;color:#fff;}.list-group .list-group-bg-black:hover,.list-group .list-group-bg-black:focus{background-color:#000;color:#fff;opacity:.8;}.list-group .pl-white{stroke:#fff;}.list-group .list-group-bg-white{background-color:#fff;border:none;color:#fff;}.list-group .list-group-bg-white:hover,.list-group .list-group-bg-white:focus{background-color:#fff;color:#fff;opacity:.8;}.pager li>a{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;border:none;background-color:transparent;color:#222;font-weight:bold;}.pager li a:focus,.pager li a:active{background-color:transparent;}.pagination .disabled a,.pagination .disabled a:hover,.pagination .disabled a:focus,.pagination .disabled a:active{color:#bbb;}.pagination li.active a{background-color:#2196f3;}.pagination li{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.pagination li a:focus,.pagination li a:active{background-color:transparent;color:#555;}.pagination>li>a{border:none;font-weight:bold;color:#555;}.pagination>li:first-child>a,.pagination>li:last-child>a{width:auto;height:32px;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.pagination>li:first-child>a .material-icons,.pagination>li:last-child>a .material-icons{position:relative;bottom:2px;}.pagination-sm>li:first-child>a,.pagination-sm>li:last-child>a{width:28px;height:28px;}.pagination-sm>li:first-child>a .material-icons,.pagination-sm>li:last-child>a .material-icons{position:relative;top:-1px;left:-6px;font-size:20px;}.pagination-lg>li:first-child>a,.pagination-lg>li:last-child>a{width:44px;height:44px;}.pagination-lg>li:first-child>a .material-icons,.pagination-lg>li:last-child>a .material-icons{font-size:30px;position:relative;top:-3px;left:-10px;}.media{margin-bottom:25px;}.media .media-body{color:#777;font-size:13px;}.media .media-body .media-heading{font-size:16px;font-weight:bold;color:#333;}.wizard,.tabcontrol{display:block;width:100%;overflow:hidden;}.wizard a,.tabcontrol a{outline:0;}.wizard ul,.tabcontrol ul{list-style:none !important;padding:0;margin:0;}.wizard ul>li,.tabcontrol ul>li{display:block;padding:0;}.wizard>.steps .current-info,.tabcontrol>.steps .current-info,.wizard>.content>.title,.tabcontrol>.content>.title{position:absolute;left:-999em;}.wizard>.steps{position:relative;display:block;width:100%;}.wizard.vertical>.steps{float:left;width:30%;}.wizard.vertical>.steps>ul>li{float:none;width:100%;}.wizard.vertical>.content{float:left;margin:0 0 .5em 0;width:70%;}.wizard.vertical>.actions{float:right;width:100%;}.wizard.vertical>.actions>ul>li{margin:0 0 0 1em;}.wizard>.steps .number{font-size:1.429em;}.wizard>.steps>ul>li{width:25%;float:left;}.wizard>.actions>ul>li{float:left;}.wizard>.steps a{display:block;width:auto;margin:0 .5em .5em;padding:1em 1em;text-decoration:none;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}.wizard>.steps a:hover,.wizard>.steps a:active{display:block;width:auto;margin:0 .5em .5em;padding:1em 1em;text-decoration:none;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}.wizard>.steps .disabled a{background:#eee;color:#aaa;cursor:default;}.wizard>.steps .disabled a:hover,.wizard>.steps .disabled a:active{background:#eee;color:#aaa;cursor:default;}.wizard>.steps .current a{background:#2184be;color:#fff;cursor:default;}.wizard>.steps .current a:hover,.wizard>.steps .current a:active{background:#2184be;color:#fff;cursor:default;}.wizard>.steps .done a{background:#9dc8e2;color:#fff;}.wizard>.steps .done a:hover,.wizard>.steps .done a:active{background:#9dc8e2;color:#fff;}.wizard>.steps .error a{background:#ff3111;color:#fff;}.wizard>.steps .error a:hover,.wizard>.steps .error a:active{background:#ff3111;color:#fff;}.wizard>.content{border:1px solid #ddd;display:block;margin:.5em;min-height:35em;overflow:hidden;position:relative;width:auto;}.wizard>.actions{position:relative;display:block;text-align:right;width:100%;}.wizard>.actions>ul{display:inline-block;text-align:right;}.wizard>.actions>ul>li{margin:0 .5em;}.wizard>.actions a{background:#009688;color:#fff;display:block;padding:.5em 1em;text-decoration:none;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.wizard>.actions a:hover,.wizard>.actions a:active{background:#009688;color:#fff;display:block;padding:.5em 1em;text-decoration:none;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.wizard>.actions .disabled a{background:#eee;color:#aaa;}.wizard>.actions .disabled a:hover,.wizard>.actions .disabled a:active{background:#eee;color:#aaa;}.tabcontrol>.steps{position:relative;display:block;width:100%;}.tabcontrol>.steps>ul{position:relative;margin:6px 0 0 0;top:1px;z-index:1;}.tabcontrol>.steps>ul>li{float:left;margin:5px 2px 0 0;padding:1px;-webkit-border-top-left-radius:5px;-webkit-border-top-right-radius:5px;-moz-border-radius-topleft:5px;-moz-border-radius-topright:5px;border-top-left-radius:5px;border-top-right-radius:5px;}.tabcontrol>.steps>ul>li:hover{background:#edecec;border:1px solid #bbb;padding:0;}.tabcontrol>.steps>ul>li.current{background:#fff;border:1px solid #bbb;border-bottom:0 none;padding:0 0 1px 0;margin-top:0;}.tabcontrol>.steps>ul>li.current>a{padding:15px 30px 10px 30px;}.tabcontrol>.steps>ul>li>a{color:#5f5f5f;display:inline-block;border:0 none;margin:0;padding:10px 30px;text-decoration:none;}.tabcontrol>.steps>ul>li>a:hover{text-decoration:none;}.tabcontrol>.content{position:relative;display:inline-block;width:100%;height:35em;overflow:hidden;border-top:1px solid #bbb;padding-top:20px;}.tabcontrol>.content>.body{float:left;position:absolute;width:95%;height:95%;padding:2.5%;}.tabcontrol>.content>.body ul{list-style:disc !important;}.tabcontrol>.content>.body ul>li{display:list-item;}.wizard .content{min-height:245px;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;overflow-y:auto;}.wizard .content .body{padding:15px;}.wizard .steps a{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;-moz-transition:.5s;-o-transition:.5s;-webkit-transition:.5s;transition:.5s;}.wizard .steps a:active,.wizard .steps a:focus,.wizard .steps a:hover{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.wizard .steps .done a{background-color:rgba(0,150,136,.6);}.wizard .steps .done a:hover,.wizard .steps .done a:active,.wizard .steps .done a:focus{background-color:rgba(0,150,136,.5);}.wizard .steps .error a{background-color:#f44336 !important;}.wizard .steps .current a{background-color:#009688;}.wizard .steps .current a:active,.wizard .steps .current a:focus,.wizard .steps .current a:hover{background-color:#009688;}.waves-effect.waves-red .waves-ripple{background:rgba(244,67,54,.5);}.waves-effect.waves-pink .waves-ripple{background:rgba(233,30,99,.5);}.waves-effect.waves-purple .waves-ripple{background:rgba(156,39,176,.5);}.waves-effect.waves-deep-purple .waves-ripple{background:rgba(103,58,183,.5);}.waves-effect.waves-indigo .waves-ripple{background:rgba(63,81,181,.5);}.waves-effect.waves-blue .waves-ripple{background:rgba(33,150,243,.5);}.waves-effect.waves-light-blue .waves-ripple{background:rgba(3,169,244,.5);}.waves-effect.waves-cyan .waves-ripple{background:rgba(0,188,212,.5);}.waves-effect.waves-teal .waves-ripple{background:rgba(0,150,136,.5);}.waves-effect.waves-green .waves-ripple{background:rgba(76,175,80,.5);}.waves-effect.waves-light-green .waves-ripple{background:rgba(139,195,74,.5);}.waves-effect.waves-lime .waves-ripple{background:rgba(205,220,57,.5);}.waves-effect.waves-yellow .waves-ripple{background:rgba(255,232,33,.5);}.waves-effect.waves-amber .waves-ripple{background:rgba(255,193,7,.5);}.waves-effect.waves-orange .waves-ripple{background:rgba(255,152,0,.5);}.waves-effect.waves-deep-orange .waves-ripple{background:rgba(255,87,34,.5);}.waves-effect.waves-brown .waves-ripple{background:rgba(121,85,72,.5);}.waves-effect.waves-grey .waves-ripple{background:rgba(158,158,158,.5);}.waves-effect.waves-blue-grey .waves-ripple{background:rgba(96,125,139,.5);}.waves-effect.waves-black .waves-ripple{background:rgba(0,0,0,.5);}.waves-effect.waves-white .waves-ripple{background:rgba(255,255,255,.5);}.page-loader-wrapper{z-index:99999999;position:fixed;top:0;left:0;bottom:0;right:0;width:100%;height:100%;background:#eee;overflow:hidden;text-align:center;}.page-loader-wrapper p{font-size:13px;margin-top:10px;font-weight:bold;color:#444;}.page-loader-wrapper .loader{position:relative;top:calc(50% - 30px);}.md-preloader .pl-red{stroke:#f44336;}.md-preloader .pl-pink{stroke:#e91e63;}.md-preloader .pl-purple{stroke:#9c27b0;}.md-preloader .pl-deep-purple{stroke:#673ab7;}.md-preloader .pl-indigo{stroke:#3f51b5;}.md-preloader .pl-blue{stroke:#2196f3;}.md-preloader .pl-light-blue{stroke:#03a9f4;}.md-preloader .pl-cyan{stroke:#00bcd4;}.md-preloader .pl-teal{stroke:#009688;}.md-preloader .pl-green{stroke:#4caf50;}.md-preloader .pl-light-green{stroke:#8bc34a;}.md-preloader .pl-lime{stroke:#cddc39;}.md-preloader .pl-yellow{stroke:#ffe821;}.md-preloader .pl-amber{stroke:#ffc107;}.md-preloader .pl-orange{stroke:#ff9800;}.md-preloader .pl-deep-orange{stroke:#ff5722;}.md-preloader .pl-brown{stroke:#795548;}.md-preloader .pl-grey{stroke:#9e9e9e;}.md-preloader .pl-blue-grey{stroke:#607d8b;}.md-preloader .pl-black{stroke:#000;}.md-preloader .pl-white{stroke:#fff;}.preloader{display:inline-block;position:relative;width:50px;height:50px;-webkit-animation:container-rotate 1568ms linear infinite;-moz-animation:container-rotate 1568ms linear infinite;-o-animation:container-rotate 1568ms linear infinite;animation:container-rotate 1568ms linear infinite;}.preloader.pl-size-xl{width:75px;height:75px;}.preloader.pl-size-l{width:60px;height:60px;}.preloader.pl-size-md{width:50px;height:50px;}.preloader.pl-size-sm{width:40px;height:40px;}.preloader.pl-size-xs{width:25px;height:25px;}.spinner-layer{position:absolute;width:100%;height:100%;border-color:#f44336;-ms-opacity:1;opacity:1;-webkit-animation:fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both;-moz-animation:fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both;-o-animation:fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both;animation:fill-unfill-rotate 5332ms cubic-bezier(.4,0,.2,1) infinite both;}.spinner-layer.pl-red{border-color:#f44336;}.spinner-layer.pl-pink{border-color:#e91e63;}.spinner-layer.pl-purple{border-color:#9c27b0;}.spinner-layer.pl-deep-purple{border-color:#673ab7;}.spinner-layer.pl-indigo{border-color:#3f51b5;}.spinner-layer.pl-blue{border-color:#2196f3;}.spinner-layer.pl-light-blue{border-color:#03a9f4;}.spinner-layer.pl-cyan{border-color:#00bcd4;}.spinner-layer.pl-teal{border-color:#009688;}.spinner-layer.pl-green{border-color:#4caf50;}.spinner-layer.pl-light-green{border-color:#8bc34a;}.spinner-layer.pl-lime{border-color:#cddc39;}.spinner-layer.pl-yellow{border-color:#ffe821;}.spinner-layer.pl-amber{border-color:#ffc107;}.spinner-layer.pl-orange{border-color:#ff9800;}.spinner-layer.pl-deep-orange{border-color:#ff5722;}.spinner-layer.pl-brown{border-color:#795548;}.spinner-layer.pl-grey{border-color:#9e9e9e;}.spinner-layer.pl-blue-grey{border-color:#607d8b;}.spinner-layer.pl-black{border-color:#000;}.spinner-layer.pl-white{border-color:#fff;}.right{float:right !important;}.gap-patch{position:absolute;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit;}.gap-patch.circle{width:1000%;left:-450%;}.circle-clipper{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit;}.circle-clipper .circle{width:200%;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent !important;-ms-border-radius:50%;border-radius:50%;-webkit-animation:none;animation:none;position:absolute;top:0;right:0;bottom:0;}.circle-clipper.left .circle{left:0;border-right-color:transparent !important;-webkit-transform:rotate(129deg);-moz-transform:rotate(129deg);-ms-transform:rotate(129deg);-o-transform:rotate(129deg);transform:rotate(129deg);-webkit-animation:left-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;-moz-animation:left-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;-o-animation:left-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;animation:left-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;}.circle-clipper.right .circle{left:-100%;border-left-color:transparent !important;-webkit-transform:rotate(-129deg);-moz-transform:rotate(-129deg);-ms-transform:rotate(-129deg);-o-transform:rotate(-129deg);transform:rotate(-129deg);-webkit-animation:right-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;-moz-animation:right-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;-o-animation:right-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;animation:right-spin 1333ms cubic-bezier(.4,0,.2,1) infinite both;}@-webkit-keyframes container-rotate{to{-webkit-transform:rotate(360deg);-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);transform:rotate(360deg);}}@keyframes container-rotate{to{-moz-transform:rotate(360deg);-ms-transform:rotate(360deg);-o-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg);}}@-webkit-keyframes fill-unfill-rotate{12.5%{-webkit-transform:rotate(135deg);transform:rotate(135deg);}25%{-webkit-transform:rotate(270deg);transform:rotate(270deg);}37.5%{-webkit-transform:rotate(405deg);transform:rotate(405deg);}50%{-webkit-transform:rotate(540deg);transform:rotate(540deg);}62.5%{-webkit-transform:rotate(675deg);transform:rotate(675deg);}75%{-webkit-transform:rotate(810deg);transform:rotate(810deg);}87.5%{-webkit-transform:rotate(945deg);transform:rotate(945deg);}to{-webkit-transform:rotate(1080deg);transform:rotate(1080deg);}}@keyframes fill-unfill-rotate{12.5%{transform:rotate(135deg);}25%{transform:rotate(270deg);}37.5%{transform:rotate(405deg);}50%{transform:rotate(540deg);}62.5%{transform:rotate(675deg);}75%{transform:rotate(810deg);}87.5%{transform:rotate(945deg);}to{transform:rotate(1080deg);}}@-webkit-keyframes left-spin{from{-webkit-transform:rotate(130deg);-moz-transform:rotate(130deg);-ms-transform:rotate(130deg);-o-transform:rotate(130deg);transform:rotate(130deg);}50%{-webkit-transform:rotate(-5deg);-moz-transform:rotate(-5deg);-ms-transform:rotate(-5deg);-o-transform:rotate(-5deg);transform:rotate(-5deg);}to{-webkit-transform:rotate(130deg);-moz-transform:rotate(130deg);-ms-transform:rotate(130deg);-o-transform:rotate(130deg);transform:rotate(130deg);}}@keyframes left-spin{from{-moz-transform:rotate(130deg);-ms-transform:rotate(130deg);-o-transform:rotate(130deg);-webkit-transform:rotate(130deg);transform:rotate(130deg);}50%{-moz-transform:rotate(-5deg);-ms-transform:rotate(-5deg);-o-transform:rotate(-5deg);-webkit-transform:rotate(-5deg);transform:rotate(-5deg);}to{-moz-transform:rotate(130deg);-ms-transform:rotate(130deg);-o-transform:rotate(130deg);-webkit-transform:rotate(130deg);transform:rotate(130deg);}}@-webkit-keyframes right-spin{from{-webkit-transform:rotate(-130deg);-moz-transform:rotate(-130deg);-ms-transform:rotate(-130deg);-o-transform:rotate(-130deg);transform:rotate(-130deg);}50%{-webkit-transform:rotate(5deg);-moz-transform:rotate(5deg);-ms-transform:rotate(5deg);-o-transform:rotate(5deg);transform:rotate(5deg);}to{-webkit-transform:rotate(-130deg);-moz-transform:rotate(-130deg);-ms-transform:rotate(-130deg);-o-transform:rotate(-130deg);transform:rotate(-130deg);}}@-moz-keyframes right-spin{from{-moz-transform:rotate(-130deg);-ms-transform:rotate(-130deg);-o-transform:rotate(-130deg);-webkit-transform:rotate(-130deg);transform:rotate(-130deg);}50%{-moz-transform:rotate(5deg);-ms-transform:rotate(5deg);-o-transform:rotate(5deg);-webkit-transform:rotate(5deg);transform:rotate(5deg);}to{-moz-transform:rotate(-130deg);-ms-transform:rotate(-130deg);-o-transform:rotate(-130deg);-webkit-transform:rotate(-130deg);transform:rotate(-130deg);}}@keyframes right-spin{from{-moz-transform:rotate(-130deg);-ms-transform:rotate(-130deg);-o-transform:rotate(-130deg);-webkit-transform:rotate(-130deg);transform:rotate(-130deg);}50%{-moz-transform:rotate(5deg);-ms-transform:rotate(5deg);-o-transform:rotate(5deg);-webkit-transform:rotate(5deg);transform:rotate(5deg);}to{-moz-transform:rotate(-130deg);-ms-transform:rotate(-130deg);-o-transform:rotate(-130deg);-webkit-transform:rotate(-130deg);transform:rotate(-130deg);}}.navbar{font-family:"Roboto",sans-serif;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;-webkit-box-shadow:0 1px 5px rgba(0,0,0,.3);-moz-box-shadow:0 1px 5px rgba(0,0,0,.3);-ms-box-shadow:0 1px 5px rgba(0,0,0,.3);box-shadow:0 1px 5px rgba(0,0,0,.3);border:none;position:fixed;top:0;left:0;z-index:12;width:100%;}.navbar .navbar-brand{white-space:nowrap;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;overflow:hidden;}.navbar .navbar-custom-right-menu{float:right;}.navbar .navbar-toggle{text-decoration:none;color:#fff;width:20px;height:20px;margin-top:-4px;margin-right:17px;}.navbar .navbar-toggle:before{content:'';font-family:'Material Icons';font-size:26px;}.navbar .navbar-collapse.in{overflow:visible;}.ls-closed .sidebar{margin-left:-300px;}.ls-closed section.content{margin-left:15px;}.ls-closed .bars:after,.ls-closed .bars:before{font-family:'Material Icons';font-size:24px;position:absolute;top:18px;left:20px;margin-right:10px;-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-moz-transition:all .3s;-o-transition:all .3s;-webkit-transition:all .3s;transition:all .3s;}.ls-closed .bars:before{content:'';-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);}.ls-closed .bars:after{content:'';-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);}.ls-closed .navbar-brand{margin-left:30px;}.overlay-open .bars:before{-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);}.overlay-open .bars:after{-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);}.navbar-header{padding:10px 7px;}.navbar-header .bars{float:left;text-decoration:none;}.navbar-nav>li>a{padding:7px 7px 2px 7px;margin-top:17px;margin-left:5px;}.navbar-nav .dropdown-menu{margin-top:-40px !important;}.label-count{position:absolute;top:2px;right:6px;font-size:10px;line-height:15px;background-color:#000;padding:0 4px;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;border-radius:3px;}.col-red .navbar .navbar-brand,.col-red .navbar .navbar-brand:hover,.col-red .navbar .navbar-brand:active,.col-red .navbar .navbar-brand:focus{color:#fff;}.col-red .navbar .nav>li>a:hover,.col-red .navbar .nav>li>a:focus,.col-red .navbar .nav .open>a,.col-red .navbar .nav .open>a:hover,.col-red .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-red .navbar .nav>li>a{color:#fff;}.col-red .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-red .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-pink .navbar .navbar-brand,.col-pink .navbar .navbar-brand:hover,.col-pink .navbar .navbar-brand:active,.col-pink .navbar .navbar-brand:focus{color:#fff;}.col-pink .navbar .nav>li>a:hover,.col-pink .navbar .nav>li>a:focus,.col-pink .navbar .nav .open>a,.col-pink .navbar .nav .open>a:hover,.col-pink .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-pink .navbar .nav>li>a{color:#fff;}.col-pink .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-pink .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-purple .navbar .navbar-brand,.col-purple .navbar .navbar-brand:hover,.col-purple .navbar .navbar-brand:active,.col-purple .navbar .navbar-brand:focus{color:#fff;}.col-purple .navbar .nav>li>a:hover,.col-purple .navbar .nav>li>a:focus,.col-purple .navbar .nav .open>a,.col-purple .navbar .nav .open>a:hover,.col-purple .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-purple .navbar .nav>li>a{color:#fff;}.col-purple .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-purple .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-deep-purple .navbar .navbar-brand,.col-deep-purple .navbar .navbar-brand:hover,.col-deep-purple .navbar .navbar-brand:active,.col-deep-purple .navbar .navbar-brand:focus{color:#fff;}.col-deep-purple .navbar .nav>li>a:hover,.col-deep-purple .navbar .nav>li>a:focus,.col-deep-purple .navbar .nav .open>a,.col-deep-purple .navbar .nav .open>a:hover,.col-deep-purple .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-deep-purple .navbar .nav>li>a{color:#fff;}.col-deep-purple .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-deep-purple .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-indigo .navbar .navbar-brand,.col-indigo .navbar .navbar-brand:hover,.col-indigo .navbar .navbar-brand:active,.col-indigo .navbar .navbar-brand:focus{color:#fff;}.col-indigo .navbar .nav>li>a:hover,.col-indigo .navbar .nav>li>a:focus,.col-indigo .navbar .nav .open>a,.col-indigo .navbar .nav .open>a:hover,.col-indigo .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-indigo .navbar .nav>li>a{color:#fff;}.col-indigo .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-indigo .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-blue .navbar .navbar-brand,.col-blue .navbar .navbar-brand:hover,.col-blue .navbar .navbar-brand:active,.col-blue .navbar .navbar-brand:focus{color:#fff;}.col-blue .navbar .nav>li>a:hover,.col-blue .navbar .nav>li>a:focus,.col-blue .navbar .nav .open>a,.col-blue .navbar .nav .open>a:hover,.col-blue .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-blue .navbar .nav>li>a{color:#fff;}.col-blue .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-blue .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-light-blue .navbar .navbar-brand,.col-light-blue .navbar .navbar-brand:hover,.col-light-blue .navbar .navbar-brand:active,.col-light-blue .navbar .navbar-brand:focus{color:#fff;}.col-light-blue .navbar .nav>li>a:hover,.col-light-blue .navbar .nav>li>a:focus,.col-light-blue .navbar .nav .open>a,.col-light-blue .navbar .nav .open>a:hover,.col-light-blue .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-light-blue .navbar .nav>li>a{color:#fff;}.col-light-blue .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-light-blue .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-cyan .navbar .navbar-brand,.col-cyan .navbar .navbar-brand:hover,.col-cyan .navbar .navbar-brand:active,.col-cyan .navbar .navbar-brand:focus{color:#fff;}.col-cyan .navbar .nav>li>a:hover,.col-cyan .navbar .nav>li>a:focus,.col-cyan .navbar .nav .open>a,.col-cyan .navbar .nav .open>a:hover,.col-cyan .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-cyan .navbar .nav>li>a{color:#fff;}.col-cyan .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-cyan .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-teal .navbar .navbar-brand,.col-teal .navbar .navbar-brand:hover,.col-teal .navbar .navbar-brand:active,.col-teal .navbar .navbar-brand:focus{color:#fff;}.col-teal .navbar .nav>li>a:hover,.col-teal .navbar .nav>li>a:focus,.col-teal .navbar .nav .open>a,.col-teal .navbar .nav .open>a:hover,.col-teal .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-teal .navbar .nav>li>a{color:#fff;}.col-teal .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-teal .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-green .navbar .navbar-brand,.col-green .navbar .navbar-brand:hover,.col-green .navbar .navbar-brand:active,.col-green .navbar .navbar-brand:focus{color:#fff;}.col-green .navbar .nav>li>a:hover,.col-green .navbar .nav>li>a:focus,.col-green .navbar .nav .open>a,.col-green .navbar .nav .open>a:hover,.col-green .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-green .navbar .nav>li>a{color:#fff;}.col-green .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-green .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-light-green .navbar .navbar-brand,.col-light-green .navbar .navbar-brand:hover,.col-light-green .navbar .navbar-brand:active,.col-light-green .navbar .navbar-brand:focus{color:#fff;}.col-light-green .navbar .nav>li>a:hover,.col-light-green .navbar .nav>li>a:focus,.col-light-green .navbar .nav .open>a,.col-light-green .navbar .nav .open>a:hover,.col-light-green .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-light-green .navbar .nav>li>a{color:#fff;}.col-light-green .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-light-green .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-lime .navbar .navbar-brand,.col-lime .navbar .navbar-brand:hover,.col-lime .navbar .navbar-brand:active,.col-lime .navbar .navbar-brand:focus{color:#fff;}.col-lime .navbar .nav>li>a:hover,.col-lime .navbar .nav>li>a:focus,.col-lime .navbar .nav .open>a,.col-lime .navbar .nav .open>a:hover,.col-lime .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-lime .navbar .nav>li>a{color:#fff;}.col-lime .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-lime .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-yellow .navbar .navbar-brand,.col-yellow .navbar .navbar-brand:hover,.col-yellow .navbar .navbar-brand:active,.col-yellow .navbar .navbar-brand:focus{color:#fff;}.col-yellow .navbar .nav>li>a:hover,.col-yellow .navbar .nav>li>a:focus,.col-yellow .navbar .nav .open>a,.col-yellow .navbar .nav .open>a:hover,.col-yellow .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-yellow .navbar .nav>li>a{color:#fff;}.col-yellow .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-yellow .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-amber .navbar .navbar-brand,.col-amber .navbar .navbar-brand:hover,.col-amber .navbar .navbar-brand:active,.col-amber .navbar .navbar-brand:focus{color:#fff;}.col-amber .navbar .nav>li>a:hover,.col-amber .navbar .nav>li>a:focus,.col-amber .navbar .nav .open>a,.col-amber .navbar .nav .open>a:hover,.col-amber .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-amber .navbar .nav>li>a{color:#fff;}.col-amber .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-amber .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-orange .navbar .navbar-brand,.col-orange .navbar .navbar-brand:hover,.col-orange .navbar .navbar-brand:active,.col-orange .navbar .navbar-brand:focus{color:#fff;}.col-orange .navbar .nav>li>a:hover,.col-orange .navbar .nav>li>a:focus,.col-orange .navbar .nav .open>a,.col-orange .navbar .nav .open>a:hover,.col-orange .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-orange .navbar .nav>li>a{color:#fff;}.col-orange .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-orange .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-deep-orange .navbar .navbar-brand,.col-deep-orange .navbar .navbar-brand:hover,.col-deep-orange .navbar .navbar-brand:active,.col-deep-orange .navbar .navbar-brand:focus{color:#fff;}.col-deep-orange .navbar .nav>li>a:hover,.col-deep-orange .navbar .nav>li>a:focus,.col-deep-orange .navbar .nav .open>a,.col-deep-orange .navbar .nav .open>a:hover,.col-deep-orange .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-deep-orange .navbar .nav>li>a{color:#fff;}.col-deep-orange .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-deep-orange .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-brown .navbar .navbar-brand,.col-brown .navbar .navbar-brand:hover,.col-brown .navbar .navbar-brand:active,.col-brown .navbar .navbar-brand:focus{color:#fff;}.col-brown .navbar .nav>li>a:hover,.col-brown .navbar .nav>li>a:focus,.col-brown .navbar .nav .open>a,.col-brown .navbar .nav .open>a:hover,.col-brown .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-brown .navbar .nav>li>a{color:#fff;}.col-brown .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-brown .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-grey .navbar .navbar-brand,.col-grey .navbar .navbar-brand:hover,.col-grey .navbar .navbar-brand:active,.col-grey .navbar .navbar-brand:focus{color:#fff;}.col-grey .navbar .nav>li>a:hover,.col-grey .navbar .nav>li>a:focus,.col-grey .navbar .nav .open>a,.col-grey .navbar .nav .open>a:hover,.col-grey .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-grey .navbar .nav>li>a{color:#fff;}.col-grey .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-grey .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-blue-grey .navbar .navbar-brand,.col-blue-grey .navbar .navbar-brand:hover,.col-blue-grey .navbar .navbar-brand:active,.col-blue-grey .navbar .navbar-brand:focus{color:#fff;}.col-blue-grey .navbar .nav>li>a:hover,.col-blue-grey .navbar .nav>li>a:focus,.col-blue-grey .navbar .nav .open>a,.col-blue-grey .navbar .nav .open>a:hover,.col-blue-grey .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-blue-grey .navbar .nav>li>a{color:#fff;}.col-blue-grey .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-blue-grey .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-black .navbar .navbar-brand,.col-black .navbar .navbar-brand:hover,.col-black .navbar .navbar-brand:active,.col-black .navbar .navbar-brand:focus{color:#fff;}.col-black .navbar .nav>li>a:hover,.col-black .navbar .nav>li>a:focus,.col-black .navbar .nav .open>a,.col-black .navbar .nav .open>a:hover,.col-black .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-black .navbar .nav>li>a{color:#fff;}.col-black .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-black .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.col-white .navbar .navbar-brand,.col-white .navbar .navbar-brand:hover,.col-white .navbar .navbar-brand:active,.col-white .navbar .navbar-brand:focus{color:#fff;}.col-white .navbar .nav>li>a:hover,.col-white .navbar .nav>li>a:focus,.col-white .navbar .nav .open>a,.col-white .navbar .nav .open>a:hover,.col-white .navbar .nav .open>a:focus{background-color:rgba(0,0,0,.05);}.col-white .navbar .nav>li>a{color:#fff;}.col-white .navbar .bars{float:left;padding:10px 20px;font-size:22px;color:#fff;margin-right:10px;margin-left:-10px;margin-top:4px;}.col-white .navbar .bars:hover{background-color:rgba(0,0,0,.08);}.dropdown-menu{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;margin-top:-35px !important;box-shadow:0 2px 10px rgba(0,0,0,.2);border:none;}.dropdown-menu .divider{margin:5px 0;}.dropdown-menu .header{font-size:13px;font-weight:bold;min-width:270px;border-bottom:1px solid #eee;text-align:center;padding:4px 0 6px 0;}.dropdown-menu ul.menu{padding-left:0;}.dropdown-menu ul.menu.tasks h4{color:#333;font-size:13px;margin:0 0 8px 0;}.dropdown-menu ul.menu.tasks h4 small{float:right;margin-top:6px;}.dropdown-menu ul.menu.tasks .progress{height:7px;margin-bottom:7px;}.dropdown-menu ul.menu .icon-circle{width:36px;height:36px;-webkit-border-radius:50%;-moz-border-radius:50%;-ms-border-radius:50%;border-radius:50%;color:#fff;text-align:center;display:inline-block;}.dropdown-menu ul.menu .icon-circle i{font-size:18px;line-height:36px;}.dropdown-menu ul.menu li{border-bottom:1px solid #eee;}.dropdown-menu ul.menu li:last-child{border-bottom:none;}.dropdown-menu ul.menu li a{padding:7px 11px;text-decoration:none;-moz-transition:.5s;-o-transition:.5s;-webkit-transition:.5s;transition:.5s;}.dropdown-menu ul.menu li a:hover{background-color:#e9e9e9;}.dropdown-menu ul.menu .menu-info{display:inline-block;position:relative;top:3px;left:5px;}.dropdown-menu ul.menu .menu-info h4{margin:0;font-size:13px;color:#333;}.dropdown-menu ul.menu .menu-info p{margin:0;font-size:11px;color:#aaa;}.dropdown-menu ul.menu .menu-info p .material-icons{font-size:13px;color:#aaa;position:relative;top:2px;}.dropdown-menu .footer a{text-align:center;border-top:1px solid #eee;padding:5px 0 5px 0;font-size:12px;margin-bottom:-5px;}.dropdown-menu .footer a:hover{background-color:transparent;}.dropdown-menu>li>a{padding:7px 18px;color:#666;-moz-transition:all .5s;-o-transition:all .5s;-webkit-transition:all .5s;transition:all .5s;font-size:14px;line-height:25px;}.dropdown-menu>li>a:hover{background-color:rgba(0,0,0,.075);}.dropdown-menu>li>a i.material-icons{float:left;margin-right:7px;margin-top:2px;font-size:20px;}.dropdown-animated{-webkit-animation-duration:.3s !important;-moz-animation-duration:.3s !important;-o-animation-duration:.3s !important;animation-duration:.3s !important;}.overlay{position:fixed;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,.5);display:none;z-index:10;}.overlay-open .sidebar{margin-left:0;z-index:99999999;}.sidebar{-moz-transition:all .5s;-o-transition:all .5s;-webkit-transition:all .5s;transition:all .5s;font-family:"Roboto",sans-serif;background:#fdfdfd;width:300px;overflow:hidden;display:inline-block;height:calc(100vh - 70px);position:fixed;top:70px;left:0;-webkit-box-shadow:2px 2px 5px rgba(0,0,0,.1);-moz-box-shadow:2px 2px 5px rgba(0,0,0,.1);-ms-box-shadow:2px 2px 5px rgba(0,0,0,.1);box-shadow:2px 2px 5px rgba(0,0,0,.1);z-index:11 !important;}.sidebar .legal{position:absolute;bottom:0;width:100%;border-top:1px solid #eee;padding:15px;overflow:hidden;}.sidebar .legal .copyright{font-size:13px;white-space:nowrap;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;overflow:hidden;}.sidebar .legal .copyright a{font-weight:bold;text-decoration:none;}.sidebar .legal .version{white-space:nowrap;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;overflow:hidden;margin-top:5px;font-size:13px;}.sidebar .user-info{padding:13px 15px 12px 15px;white-space:nowrap;position:relative;border-bottom:1px solid #e9e9e9;background:url("../images/user-img-background.jpg") no-repeat no-repeat;height:135px;}.sidebar .user-info .image{margin-right:12px;display:inline-block;}.sidebar .user-info .image img{-webkit-border-radius:50%;-moz-border-radius:50%;-ms-border-radius:50%;border-radius:50%;vertical-align:bottom !important;}.sidebar .user-info .info-container{cursor:default;display:block;position:relative;top:25px;}.sidebar .user-info .info-container .name{white-space:nowrap;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;overflow:hidden;font-size:14px;max-width:200px;color:#fff;}.sidebar .user-info .info-container .email{white-space:nowrap;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;overflow:hidden;font-size:12px;max-width:200px;color:#fff;}.sidebar .user-info .info-container .user-helper-dropdown{position:absolute;right:-3px;bottom:-12px;-webkit-box-shadow:none;-moz-box-shadow:none;-ms-box-shadow:none;box-shadow:none;cursor:pointer;color:#fff;}.sidebar .menu{position:relative;overflow-y:auto;height:90vh;}.sidebar .menu .list{list-style:none;padding-left:0;}.sidebar .menu .list li.active>:first-child span{font-weight:bold;}.sidebar .menu .list .header{background:#eee;font-size:12px;font-weight:600;padding:8px 16px;}.sidebar .menu .list i.material-icons{margin-top:4px;}.sidebar .menu .list .menu-toggle:after,.sidebar .menu .list .menu-toggle:before{position:absolute;top:calc(50% - 14px);right:17px;font-size:19px;-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);-moz-transition:all .3s;-o-transition:all .3s;-webkit-transition:all .3s;transition:all .3s;}.sidebar .menu .list .menu-toggle:before{content:'+';-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);}.sidebar .menu .list .menu-toggle:after{content:'–';-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);}.sidebar .menu .list .menu-toggle.toggled:before{-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);-webkit-transform:scale(0);transform:scale(0);}.sidebar .menu .list .menu-toggle.toggled:after{-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);-webkit-transform:scale(1);transform:scale(1);}.sidebar .menu .list a{color:#747474;position:relative;display:inline-flex;vertical-align:middle;width:100%;padding:10px 13px;}.sidebar .menu .list a:hover,.sidebar .menu .list a:active,.sidebar .menu .list a:focus{text-decoration:none !important;}.sidebar .menu .list a small{position:absolute;top:calc(50% - 7.5px);right:15px;}.sidebar .menu .list a span{margin:7px 0 7px 12px;color:#333;font-weight:bold;font-size:14px;overflow:hidden;}.sidebar .menu .list .ml-menu{list-style:none;display:none;padding-left:0;}.sidebar .menu .list .ml-menu span{font-weight:normal;font-size:14px;margin:3px 0 1px 6px;}.sidebar .menu .list .ml-menu li a{padding-left:55px;padding-top:7px;padding-bottom:7px;}.sidebar .menu .list .ml-menu li.active a.toggled:not(.menu-toggle){font-weight:600;margin-left:5px;}.sidebar .menu .list .ml-menu li.active a.toggled:not(.menu-toggle):before{content:'';font-family:'Material Icons';position:relative;font-size:21px;height:20px;top:-5px;right:0;}.sidebar .menu .list .ml-menu li .ml-menu li a{padding-left:80px;}.sidebar .menu .list .ml-menu li .ml-menu .ml-menu li a{padding-left:95px;}.right-sidebar{width:280px;height:calc(100vh - 70px);position:fixed;right:-300px;top:70px;background:#fdfdfd;z-index:11 !important;-webkit-box-shadow:-2px 2px 5px rgba(0,0,0,.1);-moz-box-shadow:-2px 2px 5px rgba(0,0,0,.1);-ms-box-shadow:-2px 2px 5px rgba(0,0,0,.1);box-shadow:-2px 2px 5px rgba(0,0,0,.1);overflow:hidden;-moz-transition:.5s;-o-transition:.5s;-webkit-transition:.5s;transition:.5s;}.right-sidebar.open{right:0;}.right-sidebar .nav-tabs{font-weight:600;font-size:13px;width:100%;margin-left:2px;}.right-sidebar .nav-tabs li{text-align:center;}.right-sidebar .nav-tabs li>a{margin-right:0;}.right-sidebar .nav-tabs li:first-child{width:45%;}.right-sidebar .nav-tabs li:last-child{width:55%;}.bootstrap-notify-container{max-width:320px;text-align:center;}.dd-handle{background-color:#f9f9f9 !important;}.dd-handle:hover{color:#2196f3;}.nestable-dark-theme .dd-handle{background:#ccc !important;border:1px solid #999 !important;}.dd3-handle{background:#999 !important;}.dd3-content:hover{color:#2196f3;}.login-page{background-color:#00bcd4;padding-left:0;max-width:360px;margin:5% auto;overflow-x:hidden;}.login-page .login-box .msg{color:#555;margin-bottom:30px;text-align:center;}.login-page .login-box a{font-size:14px;text-decoration:none;color:#00bcd4;}.login-page .login-box .logo{margin-bottom:20px;}.login-page .login-box .logo a{font-size:36px;display:block;width:100%;text-align:center;color:#fff;}.login-page .login-box .logo small{display:block;width:100%;text-align:center;color:#fff;margin-top:-5px;}.signup-page{background-color:#00bcd4;padding-left:0;max-width:360px;margin:5% auto;overflow-x:hidden;}.signup-page .signup-box .msg{color:#555;margin-bottom:30px;text-align:center;}.signup-page .signup-box a{font-size:14px;text-decoration:none;color:#00bcd4;}.signup-page .signup-box .logo{margin-bottom:20px;}.signup-page .signup-box .logo a{font-size:36px;display:block;width:100%;text-align:center;color:#fff;}.signup-page .signup-box .logo small{display:block;width:100%;text-align:center;color:#fff;margin-top:-5px;}.fp-page{background-color:#00bcd4;padding-left:0;max-width:360px;margin:5% auto;overflow-x:hidden;}.fp-page .fp-box .msg{color:#555;margin-bottom:30px;text-align:center;}.fp-page .fp-box a{font-size:14px;text-decoration:none;color:#00bcd4;}.fp-page .fp-box .logo{margin-bottom:20px;}.fp-page .fp-box .logo a{font-size:36px;display:block;width:100%;text-align:center;color:#fff;}.fp-page .fp-box .logo small{display:block;width:100%;text-align:center;color:#fff;margin-top:-5px;}.four-zero-four{width:100%;text-align:center;margin:5% auto;}.four-zero-four .four-zero-four-container .error-code{font-size:160px;}.four-zero-four .four-zero-four-container .error-message{font-size:26px;color:#333;font-weight:bold;margin-top:-40px;}.four-zero-four .four-zero-four-container .button-place{margin-top:32px;}.five-zero-zero{width:100%;text-align:center;margin:5% auto;}.five-zero-zero .five-zero-zero-container .error-code{font-size:160px;}.five-zero-zero .five-zero-zero-container .error-message{font-size:27px;color:#333;font-weight:bold;margin-top:-40px;}.five-zero-zero .five-zero-zero-container .button-place{margin-top:32px;}.gmap{width:100%;height:400px;}.jvector-map{width:100%;height:600px;}.morris-hover.morris-default-style{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;}.flot-chart{width:100%;height:320px;}.panel-switch-btn{position:relative;right:20px;z-index:9;}.panel-switch-btn label{font-weight:bold !important;}.legendLabel{width:85px !important;position:relative;left:3px;}#multiple_axis_chart .legendLabel{width:160px !important;}.sparkline{text-align:center;}.search-bar{position:fixed;top:-100px;left:0;z-index:9999999;width:100%;-moz-transition:.25s;-o-transition:.25s;-webkit-transition:.25s;transition:.25s;}.search-bar.open{top:0;}.search-bar .search-icon{position:absolute;top:20px;left:14px;}.search-bar .search-icon .material-icons{font-size:32px;color:#999;}.search-bar .close-search{position:absolute;cursor:pointer;font-size:30px;top:16px;right:18px;}.search-bar .close-search .material-icons{color:#999;opacity:1;-moz-transition:.5s;-o-transition:.5s;-webkit-transition:.5s;transition:.5s;}.search-bar .close-search .material-icons:hover{opacity:.5;}.search-bar input[type="text"]{width:100%;font-size:16px;padding:25px 60px 23px 56px;border:none;}.dataTables_wrapper{position:relative;}.dataTables_wrapper select{border:none;border-bottom:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;-ms-box-shadow:none;box-shadow:none;}.dataTables_wrapper select:active,.dataTables_wrapper select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;-ms-box-shadow:none;box-shadow:none;}.dataTables_wrapper input[type="search"]{-webkit-border-radius:0;-moz-border-radius:0;-ms-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;-ms-box-shadow:none;box-shadow:none;border:none;font-size:12px;border-bottom:1px solid #ddd;}.dataTables_wrapper input[type="search"]:focus,.dataTables_wrapper input[type="search"]:active{border-bottom:2px solid #1f91f3;}.dataTables_wrapper .dt-buttons{float:left;}.dataTables_wrapper .dt-buttons a.dt-button{background-color:#607d8b;color:#fff;padding:7px 12px;margin-right:5px;text-decoration:none;box-shadow:0 2px 5px rgba(0,0,0,.16),0 2px 10px rgba(0,0,0,.12);-webkit-border-radius:2px;-moz-border-radius:2px;-ms-border-radius:2px;border-radius:2px;border:none;font-size:13px;outline:none;}.dataTables_wrapper .dt-buttons a.dt-button:active{opacity:.8;}.dt-button-info{position:fixed;top:50%;left:50%;min-width:400px;text-align:center;background-color:#fff;border:2px solid #999;-webkit-border-radius:3px;-moz-border-radius:3px;-ms-border-radius:3px;border-radius:3px;margin-top:-100px;margin-left:-200px;z-index:21;}.dt-button-info h2{color:#777;}.dt-button-info div{color:#777;margin-bottom:20px;}.lg-outer .lg-thumb-item,.lg-outer .lg-toogle-thumb{-webkit-border-radius:0 !important;-moz-border-radius:0 !important;-ms-border-radius:0 !important;border-radius:0 !important;}html.ie10 .sidebar .menu .list li{line-height:30px;}html.ie10 .sidebar .menu .list .ml-menu li.active a:not(.menu-toggle).toggled:before{top:6px !important;line-height:20px !important;}html.ie10 .sidebar .user-info .info-container{top:15px;}html.ie10 .search-bar input[type="text"]{padding:26px 60px 26px 56px;}html.ie10 .dropdown-menu ul.menu li a{margin-top:-22px;}html.ie10 .bs-searchbox .form-control{width:90%;}html.ie11 .sidebar .menu .list .ml-menu li.active a:not(.menu-toggle).toggled:before{top:6px !important;line-height:20px !important;}html.ie11 .sidebar .user-info .info-container{top:15px;}html.ie11 .search-bar input[type="text"]{padding:26px 60px 26px 56px;}html.ie11 .dropdown-menu ul.menu li a{margin-top:-22px;}html.ie11 .bs-searchbox .form-control{width:90%;} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/css/themes/all-themes.css b/Plan/common/src/main/resources/assets/plan/web/css/themes/all-themes.css deleted file mode 100644 index cca9450cc..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/themes/all-themes.css +++ /dev/null @@ -1,900 +0,0 @@ -.theme-red .navbar { - background-color: #F44336; } - -.theme-red .navbar-brand { - color: #fff; } - .theme-red .navbar-brand:hover { - color: #fff; } - .theme-red .navbar-brand:active { - color: #fff; } - .theme-red .navbar-brand:focus { - color: #fff; } - -.theme-red .nav > li > a { - color: #fff; } - .theme-red .nav > li > a:hover { - background-color: transparent; } - .theme-red .nav > li > a:focus { - background-color: transparent; } - -.theme-red .nav .open > a { - background-color: transparent; } - .theme-red .nav .open > a:hover { - background-color: transparent; } - .theme-red .nav .open > a:focus { - background-color: transparent; } - -.theme-red .bars { - color: #fff; } - -.theme-red .sidebar .menu .list li.active { - background-color: transparent; } - .theme-red .sidebar .menu .list li.active > :first-child i, .theme-red .sidebar .menu .list li.active > :first-child span { - color: #F44336; } - -.theme-red .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-red .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-red .sidebar .legal { - background-color: #fff; } - .theme-red .sidebar .legal .copyright a { - color: #F44336 !important; } - -.theme-pink .navbar { - background-color: #E91E63; } - -.theme-pink .navbar-brand { - color: #fff; } - .theme-pink .navbar-brand:hover { - color: #fff; } - .theme-pink .navbar-brand:active { - color: #fff; } - .theme-pink .navbar-brand:focus { - color: #fff; } - -.theme-pink .nav > li > a { - color: #fff; } - .theme-pink .nav > li > a:hover { - background-color: transparent; } - .theme-pink .nav > li > a:focus { - background-color: transparent; } - -.theme-pink .nav .open > a { - background-color: transparent; } - .theme-pink .nav .open > a:hover { - background-color: transparent; } - .theme-pink .nav .open > a:focus { - background-color: transparent; } - -.theme-pink .bars { - color: #fff; } - -.theme-pink .sidebar .menu .list li.active { - background-color: transparent; } - .theme-pink .sidebar .menu .list li.active > :first-child i, .theme-pink .sidebar .menu .list li.active > :first-child span { - color: #E91E63; } - -.theme-pink .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-pink .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-pink .sidebar .legal { - background-color: #fff; } - .theme-pink .sidebar .legal .copyright a { - color: #E91E63 !important; } - -.theme-purple .navbar { - background-color: #9C27B0; } - -.theme-purple .navbar-brand { - color: #fff; } - .theme-purple .navbar-brand:hover { - color: #fff; } - .theme-purple .navbar-brand:active { - color: #fff; } - .theme-purple .navbar-brand:focus { - color: #fff; } - -.theme-purple .nav > li > a { - color: #fff; } - .theme-purple .nav > li > a:hover { - background-color: transparent; } - .theme-purple .nav > li > a:focus { - background-color: transparent; } - -.theme-purple .nav .open > a { - background-color: transparent; } - .theme-purple .nav .open > a:hover { - background-color: transparent; } - .theme-purple .nav .open > a:focus { - background-color: transparent; } - -.theme-purple .bars { - color: #fff; } - -.theme-purple .sidebar .menu .list li.active { - background-color: transparent; } - .theme-purple .sidebar .menu .list li.active > :first-child i, .theme-purple .sidebar .menu .list li.active > :first-child span { - color: #9C27B0; } - -.theme-purple .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-purple .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-purple .sidebar .legal { - background-color: #fff; } - .theme-purple .sidebar .legal .copyright a { - color: #9C27B0 !important; } - -.theme-deep-purple .navbar { - background-color: #673AB7; } - -.theme-deep-purple .navbar-brand { - color: #fff; } - .theme-deep-purple .navbar-brand:hover { - color: #fff; } - .theme-deep-purple .navbar-brand:active { - color: #fff; } - .theme-deep-purple .navbar-brand:focus { - color: #fff; } - -.theme-deep-purple .nav > li > a { - color: #fff; } - .theme-deep-purple .nav > li > a:hover { - background-color: transparent; } - .theme-deep-purple .nav > li > a:focus { - background-color: transparent; } - -.theme-deep-purple .nav .open > a { - background-color: transparent; } - .theme-deep-purple .nav .open > a:hover { - background-color: transparent; } - .theme-deep-purple .nav .open > a:focus { - background-color: transparent; } - -.theme-deep-purple .bars { - color: #fff; } - -.theme-deep-purple .sidebar .menu .list li.active { - background-color: transparent; } - .theme-deep-purple .sidebar .menu .list li.active > :first-child i, .theme-deep-purple .sidebar .menu .list li.active > :first-child span { - color: #673AB7; } - -.theme-deep-purple .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-deep-purple .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-deep-purple .sidebar .legal { - background-color: #fff; } - .theme-deep-purple .sidebar .legal .copyright a { - color: #673AB7 !important; } - -.theme-indigo .navbar { - background-color: #3F51B5; } - -.theme-indigo .navbar-brand { - color: #fff; } - .theme-indigo .navbar-brand:hover { - color: #fff; } - .theme-indigo .navbar-brand:active { - color: #fff; } - .theme-indigo .navbar-brand:focus { - color: #fff; } - -.theme-indigo .nav > li > a { - color: #fff; } - .theme-indigo .nav > li > a:hover { - background-color: transparent; } - .theme-indigo .nav > li > a:focus { - background-color: transparent; } - -.theme-indigo .nav .open > a { - background-color: transparent; } - .theme-indigo .nav .open > a:hover { - background-color: transparent; } - .theme-indigo .nav .open > a:focus { - background-color: transparent; } - -.theme-indigo .bars { - color: #fff; } - -.theme-indigo .sidebar .menu .list li.active { - background-color: transparent; } - .theme-indigo .sidebar .menu .list li.active > :first-child i, .theme-indigo .sidebar .menu .list li.active > :first-child span { - color: #3F51B5; } - -.theme-indigo .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-indigo .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-indigo .sidebar .legal { - background-color: #fff; } - .theme-indigo .sidebar .legal .copyright a { - color: #3F51B5 !important; } - -.theme-blue .navbar { - background-color: #2196F3; } - -.theme-blue .navbar-brand { - color: #fff; } - .theme-blue .navbar-brand:hover { - color: #fff; } - .theme-blue .navbar-brand:active { - color: #fff; } - .theme-blue .navbar-brand:focus { - color: #fff; } - -.theme-blue .nav > li > a { - color: #fff; } - .theme-blue .nav > li > a:hover { - background-color: transparent; } - .theme-blue .nav > li > a:focus { - background-color: transparent; } - -.theme-blue .nav .open > a { - background-color: transparent; } - .theme-blue .nav .open > a:hover { - background-color: transparent; } - .theme-blue .nav .open > a:focus { - background-color: transparent; } - -.theme-blue .bars { - color: #fff; } - -.theme-blue .sidebar .menu .list li.active { - background-color: transparent; } - .theme-blue .sidebar .menu .list li.active > :first-child i, .theme-blue .sidebar .menu .list li.active > :first-child span { - color: #2196F3; } - -.theme-blue .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-blue .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-blue .sidebar .legal { - background-color: #fff; } - .theme-blue .sidebar .legal .copyright a { - color: #2196F3 !important; } - -.theme-light-blue .navbar { - background-color: #03A9F4; } - -.theme-light-blue .navbar-brand { - color: #fff; } - .theme-light-blue .navbar-brand:hover { - color: #fff; } - .theme-light-blue .navbar-brand:active { - color: #fff; } - .theme-light-blue .navbar-brand:focus { - color: #fff; } - -.theme-light-blue .nav > li > a { - color: #fff; } - .theme-light-blue .nav > li > a:hover { - background-color: transparent; } - .theme-light-blue .nav > li > a:focus { - background-color: transparent; } - -.theme-light-blue .nav .open > a { - background-color: transparent; } - .theme-light-blue .nav .open > a:hover { - background-color: transparent; } - .theme-light-blue .nav .open > a:focus { - background-color: transparent; } - -.theme-light-blue .bars { - color: #fff; } - -.theme-light-blue .sidebar .menu .list li.active { - background-color: transparent; } - .theme-light-blue .sidebar .menu .list li.active > :first-child i, .theme-light-blue .sidebar .menu .list li.active > :first-child span { - color: #03A9F4; } - -.theme-light-blue .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-light-blue .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-light-blue .sidebar .legal { - background-color: #fff; } - .theme-light-blue .sidebar .legal .copyright a { - color: #03A9F4 !important; } - -.theme-cyan .navbar { - background-color: #00BCD4; } - -.theme-cyan .navbar-brand { - color: #fff; } - .theme-cyan .navbar-brand:hover { - color: #fff; } - .theme-cyan .navbar-brand:active { - color: #fff; } - .theme-cyan .navbar-brand:focus { - color: #fff; } - -.theme-cyan .nav > li > a { - color: #fff; } - .theme-cyan .nav > li > a:hover { - background-color: transparent; } - .theme-cyan .nav > li > a:focus { - background-color: transparent; } - -.theme-cyan .nav .open > a { - background-color: transparent; } - .theme-cyan .nav .open > a:hover { - background-color: transparent; } - .theme-cyan .nav .open > a:focus { - background-color: transparent; } - -.theme-cyan .bars { - color: #fff; } - -.theme-cyan .sidebar .menu .list li.active { - background-color: transparent; } - .theme-cyan .sidebar .menu .list li.active > :first-child i, .theme-cyan .sidebar .menu .list li.active > :first-child span { - color: #00BCD4; } - -.theme-cyan .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-cyan .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-cyan .sidebar .legal { - background-color: #fff; } - .theme-cyan .sidebar .legal .copyright a { - color: #00BCD4 !important; } - -.theme-teal .navbar { - background-color: #009688; } - -.theme-teal .navbar-brand { - color: #fff; } - .theme-teal .navbar-brand:hover { - color: #fff; } - .theme-teal .navbar-brand:active { - color: #fff; } - .theme-teal .navbar-brand:focus { - color: #fff; } - -.theme-teal .nav > li > a { - color: #fff; } - .theme-teal .nav > li > a:hover { - background-color: transparent; } - .theme-teal .nav > li > a:focus { - background-color: transparent; } - -.theme-teal .nav .open > a { - background-color: transparent; } - .theme-teal .nav .open > a:hover { - background-color: transparent; } - .theme-teal .nav .open > a:focus { - background-color: transparent; } - -.theme-teal .bars { - color: #fff; } - -.theme-teal .sidebar .menu .list li.active { - background-color: transparent; } - .theme-teal .sidebar .menu .list li.active > :first-child i, .theme-teal .sidebar .menu .list li.active > :first-child span { - color: #009688; } - -.theme-teal .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-teal .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-teal .sidebar .legal { - background-color: #fff; } - .theme-teal .sidebar .legal .copyright a { - color: #009688 !important; } - -.theme-green .navbar { - background-color: #4CAF50; } - -.theme-green .navbar-brand { - color: #fff; } - .theme-green .navbar-brand:hover { - color: #fff; } - .theme-green .navbar-brand:active { - color: #fff; } - .theme-green .navbar-brand:focus { - color: #fff; } - -.theme-green .nav > li > a { - color: #fff; } - .theme-green .nav > li > a:hover { - background-color: transparent; } - .theme-green .nav > li > a:focus { - background-color: transparent; } - -.theme-green .nav .open > a { - background-color: transparent; } - .theme-green .nav .open > a:hover { - background-color: transparent; } - .theme-green .nav .open > a:focus { - background-color: transparent; } - -.theme-green .bars { - color: #fff; } - -.theme-green .sidebar .menu .list li.active { - background-color: transparent; } - .theme-green .sidebar .menu .list li.active > :first-child i, .theme-green .sidebar .menu .list li.active > :first-child span { - color: #4CAF50; } - -.theme-green .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-green .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-green .sidebar .legal { - background-color: #fff; } - .theme-green .sidebar .legal .copyright a { - color: #4CAF50 !important; } - -.theme-light-green .navbar { - background-color: #8BC34A; } - -.theme-light-green .navbar-brand { - color: #fff; } - .theme-light-green .navbar-brand:hover { - color: #fff; } - .theme-light-green .navbar-brand:active { - color: #fff; } - .theme-light-green .navbar-brand:focus { - color: #fff; } - -.theme-light-green .nav > li > a { - color: #fff; } - .theme-light-green .nav > li > a:hover { - background-color: transparent; } - .theme-light-green .nav > li > a:focus { - background-color: transparent; } - -.theme-light-green .nav .open > a { - background-color: transparent; } - .theme-light-green .nav .open > a:hover { - background-color: transparent; } - .theme-light-green .nav .open > a:focus { - background-color: transparent; } - -.theme-light-green .bars { - color: #fff; } - -.theme-light-green .sidebar .menu .list li.active { - background-color: transparent; } - .theme-light-green .sidebar .menu .list li.active > :first-child i, .theme-light-green .sidebar .menu .list li.active > :first-child span { - color: #8BC34A; } - -.theme-light-green .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-light-green .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-light-green .sidebar .legal { - background-color: #fff; } - .theme-light-green .sidebar .legal .copyright a { - color: #8BC34A !important; } - -.theme-lime .navbar { - background-color: #CDDC39; } - -.theme-lime .navbar-brand { - color: #fff; } - .theme-lime .navbar-brand:hover { - color: #fff; } - .theme-lime .navbar-brand:active { - color: #fff; } - .theme-lime .navbar-brand:focus { - color: #fff; } - -.theme-lime .nav > li > a { - color: #fff; } - .theme-lime .nav > li > a:hover { - background-color: transparent; } - .theme-lime .nav > li > a:focus { - background-color: transparent; } - -.theme-lime .nav .open > a { - background-color: transparent; } - .theme-lime .nav .open > a:hover { - background-color: transparent; } - .theme-lime .nav .open > a:focus { - background-color: transparent; } - -.theme-lime .bars { - color: #fff; } - -.theme-lime .sidebar .menu .list li.active { - background-color: transparent; } - .theme-lime .sidebar .menu .list li.active > :first-child i, .theme-lime .sidebar .menu .list li.active > :first-child span { - color: #CDDC39; } - -.theme-lime .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-lime .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-lime .sidebar .legal { - background-color: #fff; } - .theme-lime .sidebar .legal .copyright a { - color: #CDDC39 !important; } - -.theme-yellow .navbar { - background-color: #FFEB3B; } - -.theme-yellow .navbar-brand { - color: #fff; } - .theme-yellow .navbar-brand:hover { - color: #fff; } - .theme-yellow .navbar-brand:active { - color: #fff; } - .theme-yellow .navbar-brand:focus { - color: #fff; } - -.theme-yellow .nav > li > a { - color: #fff; } - .theme-yellow .nav > li > a:hover { - background-color: transparent; } - .theme-yellow .nav > li > a:focus { - background-color: transparent; } - -.theme-yellow .nav .open > a { - background-color: transparent; } - .theme-yellow .nav .open > a:hover { - background-color: transparent; } - .theme-yellow .nav .open > a:focus { - background-color: transparent; } - -.theme-yellow .bars { - color: #fff; } - -.theme-yellow .sidebar .menu .list li.active { - background-color: transparent; } - .theme-yellow .sidebar .menu .list li.active > :first-child i, .theme-yellow .sidebar .menu .list li.active > :first-child span { - color: #FFEB3B; } - -.theme-yellow .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-yellow .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-yellow .sidebar .legal { - background-color: #fff; } - .theme-yellow .sidebar .legal .copyright a { - color: #FFEB3B !important; } - -.theme-amber .navbar { - background-color: #FFC107; } - -.theme-amber .navbar-brand { - color: #fff; } - .theme-amber .navbar-brand:hover { - color: #fff; } - .theme-amber .navbar-brand:active { - color: #fff; } - .theme-amber .navbar-brand:focus { - color: #fff; } - -.theme-amber .nav > li > a { - color: #fff; } - .theme-amber .nav > li > a:hover { - background-color: transparent; } - .theme-amber .nav > li > a:focus { - background-color: transparent; } - -.theme-amber .nav .open > a { - background-color: transparent; } - .theme-amber .nav .open > a:hover { - background-color: transparent; } - .theme-amber .nav .open > a:focus { - background-color: transparent; } - -.theme-amber .bars { - color: #fff; } - -.theme-amber .sidebar .menu .list li.active { - background-color: transparent; } - .theme-amber .sidebar .menu .list li.active > :first-child i, .theme-amber .sidebar .menu .list li.active > :first-child span { - color: #FFC107; } - -.theme-amber .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-amber .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-amber .sidebar .legal { - background-color: #fff; } - .theme-amber .sidebar .legal .copyright a { - color: #FFC107 !important; } - -.theme-orange .navbar { - background-color: #FF9800; } - -.theme-orange .navbar-brand { - color: #fff; } - .theme-orange .navbar-brand:hover { - color: #fff; } - .theme-orange .navbar-brand:active { - color: #fff; } - .theme-orange .navbar-brand:focus { - color: #fff; } - -.theme-orange .nav > li > a { - color: #fff; } - .theme-orange .nav > li > a:hover { - background-color: transparent; } - .theme-orange .nav > li > a:focus { - background-color: transparent; } - -.theme-orange .nav .open > a { - background-color: transparent; } - .theme-orange .nav .open > a:hover { - background-color: transparent; } - .theme-orange .nav .open > a:focus { - background-color: transparent; } - -.theme-orange .bars { - color: #fff; } - -.theme-orange .sidebar .menu .list li.active { - background-color: transparent; } - .theme-orange .sidebar .menu .list li.active > :first-child i, .theme-orange .sidebar .menu .list li.active > :first-child span { - color: #FF9800; } - -.theme-orange .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-orange .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-orange .sidebar .legal { - background-color: #fff; } - .theme-orange .sidebar .legal .copyright a { - color: #FF9800 !important; } - -.theme-deep-orange .navbar { - background-color: #FF5722; } - -.theme-deep-orange .navbar-brand { - color: #fff; } - .theme-deep-orange .navbar-brand:hover { - color: #fff; } - .theme-deep-orange .navbar-brand:active { - color: #fff; } - .theme-deep-orange .navbar-brand:focus { - color: #fff; } - -.theme-deep-orange .nav > li > a { - color: #fff; } - .theme-deep-orange .nav > li > a:hover { - background-color: transparent; } - .theme-deep-orange .nav > li > a:focus { - background-color: transparent; } - -.theme-deep-orange .nav .open > a { - background-color: transparent; } - .theme-deep-orange .nav .open > a:hover { - background-color: transparent; } - .theme-deep-orange .nav .open > a:focus { - background-color: transparent; } - -.theme-deep-orange .bars { - color: #fff; } - -.theme-deep-orange .sidebar .menu .list li.active { - background-color: transparent; } - .theme-deep-orange .sidebar .menu .list li.active > :first-child i, .theme-deep-orange .sidebar .menu .list li.active > :first-child span { - color: #FF5722; } - -.theme-deep-orange .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-deep-orange .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-deep-orange .sidebar .legal { - background-color: #fff; } - .theme-deep-orange .sidebar .legal .copyright a { - color: #FF5722 !important; } - -.theme-brown .navbar { - background-color: #795548; } - -.theme-brown .navbar-brand { - color: #fff; } - .theme-brown .navbar-brand:hover { - color: #fff; } - .theme-brown .navbar-brand:active { - color: #fff; } - .theme-brown .navbar-brand:focus { - color: #fff; } - -.theme-brown .nav > li > a { - color: #fff; } - .theme-brown .nav > li > a:hover { - background-color: transparent; } - .theme-brown .nav > li > a:focus { - background-color: transparent; } - -.theme-brown .nav .open > a { - background-color: transparent; } - .theme-brown .nav .open > a:hover { - background-color: transparent; } - .theme-brown .nav .open > a:focus { - background-color: transparent; } - -.theme-brown .bars { - color: #fff; } - -.theme-brown .sidebar .menu .list li.active { - background-color: transparent; } - .theme-brown .sidebar .menu .list li.active > :first-child i, .theme-brown .sidebar .menu .list li.active > :first-child span { - color: #795548; } - -.theme-brown .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-brown .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-brown .sidebar .legal { - background-color: #fff; } - .theme-brown .sidebar .legal .copyright a { - color: #795548 !important; } - -.theme-grey .navbar { - background-color: #9E9E9E; } - -.theme-grey .navbar-brand { - color: #fff; } - .theme-grey .navbar-brand:hover { - color: #fff; } - .theme-grey .navbar-brand:active { - color: #fff; } - .theme-grey .navbar-brand:focus { - color: #fff; } - -.theme-grey .nav > li > a { - color: #fff; } - .theme-grey .nav > li > a:hover { - background-color: transparent; } - .theme-grey .nav > li > a:focus { - background-color: transparent; } - -.theme-grey .nav .open > a { - background-color: transparent; } - .theme-grey .nav .open > a:hover { - background-color: transparent; } - .theme-grey .nav .open > a:focus { - background-color: transparent; } - -.theme-grey .bars { - color: #fff; } - -.theme-grey .sidebar .menu .list li.active { - background-color: transparent; } - .theme-grey .sidebar .menu .list li.active > :first-child i, .theme-grey .sidebar .menu .list li.active > :first-child span { - color: #9E9E9E; } - -.theme-grey .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-grey .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-grey .sidebar .legal { - background-color: #fff; } - .theme-grey .sidebar .legal .copyright a { - color: #9E9E9E !important; } - -.theme-blue-grey .navbar { - background-color: #607D8B; } - -.theme-blue-grey .navbar-brand { - color: #fff; } - .theme-blue-grey .navbar-brand:hover { - color: #fff; } - .theme-blue-grey .navbar-brand:active { - color: #fff; } - .theme-blue-grey .navbar-brand:focus { - color: #fff; } - -.theme-blue-grey .nav > li > a { - color: #fff; } - .theme-blue-grey .nav > li > a:hover { - background-color: transparent; } - .theme-blue-grey .nav > li > a:focus { - background-color: transparent; } - -.theme-blue-grey .nav .open > a { - background-color: transparent; } - .theme-blue-grey .nav .open > a:hover { - background-color: transparent; } - .theme-blue-grey .nav .open > a:focus { - background-color: transparent; } - -.theme-blue-grey .bars { - color: #fff; } - -.theme-blue-grey .sidebar .menu .list li.active { - background-color: transparent; } - .theme-blue-grey .sidebar .menu .list li.active > :first-child i, .theme-blue-grey .sidebar .menu .list li.active > :first-child span { - color: #607D8B; } - -.theme-blue-grey .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-blue-grey .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-blue-grey .sidebar .legal { - background-color: #fff; } - .theme-blue-grey .sidebar .legal .copyright a { - color: #607D8B !important; } - -.theme-black .navbar { - background-color: #000; } - -.theme-black .navbar-brand { - color: #fff; } - .theme-black .navbar-brand:hover { - color: #fff; } - .theme-black .navbar-brand:active { - color: #fff; } - .theme-black .navbar-brand:focus { - color: #fff; } - -.theme-black .nav > li > a { - color: #fff; } - .theme-black .nav > li > a:hover { - background-color: transparent; } - .theme-black .nav > li > a:focus { - background-color: transparent; } - -.theme-black .nav .open > a { - background-color: transparent; } - .theme-black .nav .open > a:hover { - background-color: transparent; } - .theme-black .nav .open > a:focus { - background-color: transparent; } - -.theme-black .bars { - color: #fff; } - -.theme-black .sidebar .menu .list li.active { - background-color: transparent; } - .theme-black .sidebar .menu .list li.active > :first-child i, .theme-black .sidebar .menu .list li.active > :first-child span { - color: #000; } - -.theme-black .sidebar .menu .list .toggled { - background-color: transparent; } - -.theme-black .sidebar .menu .list .ml-menu { - background-color: transparent; } - -.theme-black .sidebar .legal { - background-color: #fff; } - .theme-black .sidebar .legal .copyright a { - color: #000 !important; } - diff --git a/Plan/common/src/main/resources/assets/plan/web/css/themes/all-themes.min.css b/Plan/common/src/main/resources/assets/plan/web/css/themes/all-themes.min.css deleted file mode 100644 index ecb402362..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/css/themes/all-themes.min.css +++ /dev/null @@ -1 +0,0 @@ -.theme-red .navbar{background-color:#f44336;}.theme-red .navbar-brand{color:#fff;}.theme-red .navbar-brand:hover{color:#fff;}.theme-red .navbar-brand:active{color:#fff;}.theme-red .navbar-brand:focus{color:#fff;}.theme-red .nav>li>a{color:#fff;}.theme-red .nav>li>a:hover{background-color:transparent;}.theme-red .nav>li>a:focus{background-color:transparent;}.theme-red .nav .open>a{background-color:transparent;}.theme-red .nav .open>a:hover{background-color:transparent;}.theme-red .nav .open>a:focus{background-color:transparent;}.theme-red .bars{color:#fff;}.theme-red .sidebar .menu .list li.active{background-color:transparent;}.theme-red .sidebar .menu .list li.active>:first-child i,.theme-red .sidebar .menu .list li.active>:first-child span{color:#f44336;}.theme-red .sidebar .menu .list .toggled{background-color:transparent;}.theme-red .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-red .sidebar .legal{background-color:#fff;}.theme-red .sidebar .legal .copyright a{color:#f44336 !important;}.theme-pink .navbar{background-color:#e91e63;}.theme-pink .navbar-brand{color:#fff;}.theme-pink .navbar-brand:hover{color:#fff;}.theme-pink .navbar-brand:active{color:#fff;}.theme-pink .navbar-brand:focus{color:#fff;}.theme-pink .nav>li>a{color:#fff;}.theme-pink .nav>li>a:hover{background-color:transparent;}.theme-pink .nav>li>a:focus{background-color:transparent;}.theme-pink .nav .open>a{background-color:transparent;}.theme-pink .nav .open>a:hover{background-color:transparent;}.theme-pink .nav .open>a:focus{background-color:transparent;}.theme-pink .bars{color:#fff;}.theme-pink .sidebar .menu .list li.active{background-color:transparent;}.theme-pink .sidebar .menu .list li.active>:first-child i,.theme-pink .sidebar .menu .list li.active>:first-child span{color:#e91e63;}.theme-pink .sidebar .menu .list .toggled{background-color:transparent;}.theme-pink .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-pink .sidebar .legal{background-color:#fff;}.theme-pink .sidebar .legal .copyright a{color:#e91e63 !important;}.theme-purple .navbar{background-color:#9c27b0;}.theme-purple .navbar-brand{color:#fff;}.theme-purple .navbar-brand:hover{color:#fff;}.theme-purple .navbar-brand:active{color:#fff;}.theme-purple .navbar-brand:focus{color:#fff;}.theme-purple .nav>li>a{color:#fff;}.theme-purple .nav>li>a:hover{background-color:transparent;}.theme-purple .nav>li>a:focus{background-color:transparent;}.theme-purple .nav .open>a{background-color:transparent;}.theme-purple .nav .open>a:hover{background-color:transparent;}.theme-purple .nav .open>a:focus{background-color:transparent;}.theme-purple .bars{color:#fff;}.theme-purple .sidebar .menu .list li.active{background-color:transparent;}.theme-purple .sidebar .menu .list li.active>:first-child i,.theme-purple .sidebar .menu .list li.active>:first-child span{color:#9c27b0;}.theme-purple .sidebar .menu .list .toggled{background-color:transparent;}.theme-purple .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-purple .sidebar .legal{background-color:#fff;}.theme-purple .sidebar .legal .copyright a{color:#9c27b0 !important;}.theme-deep-purple .navbar{background-color:#673ab7;}.theme-deep-purple .navbar-brand{color:#fff;}.theme-deep-purple .navbar-brand:hover{color:#fff;}.theme-deep-purple .navbar-brand:active{color:#fff;}.theme-deep-purple .navbar-brand:focus{color:#fff;}.theme-deep-purple .nav>li>a{color:#fff;}.theme-deep-purple .nav>li>a:hover{background-color:transparent;}.theme-deep-purple .nav>li>a:focus{background-color:transparent;}.theme-deep-purple .nav .open>a{background-color:transparent;}.theme-deep-purple .nav .open>a:hover{background-color:transparent;}.theme-deep-purple .nav .open>a:focus{background-color:transparent;}.theme-deep-purple .bars{color:#fff;}.theme-deep-purple .sidebar .menu .list li.active{background-color:transparent;}.theme-deep-purple .sidebar .menu .list li.active>:first-child i,.theme-deep-purple .sidebar .menu .list li.active>:first-child span{color:#673ab7;}.theme-deep-purple .sidebar .menu .list .toggled{background-color:transparent;}.theme-deep-purple .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-deep-purple .sidebar .legal{background-color:#fff;}.theme-deep-purple .sidebar .legal .copyright a{color:#673ab7 !important;}.theme-indigo .navbar{background-color:#3f51b5;}.theme-indigo .navbar-brand{color:#fff;}.theme-indigo .navbar-brand:hover{color:#fff;}.theme-indigo .navbar-brand:active{color:#fff;}.theme-indigo .navbar-brand:focus{color:#fff;}.theme-indigo .nav>li>a{color:#fff;}.theme-indigo .nav>li>a:hover{background-color:transparent;}.theme-indigo .nav>li>a:focus{background-color:transparent;}.theme-indigo .nav .open>a{background-color:transparent;}.theme-indigo .nav .open>a:hover{background-color:transparent;}.theme-indigo .nav .open>a:focus{background-color:transparent;}.theme-indigo .bars{color:#fff;}.theme-indigo .sidebar .menu .list li.active{background-color:transparent;}.theme-indigo .sidebar .menu .list li.active>:first-child i,.theme-indigo .sidebar .menu .list li.active>:first-child span{color:#3f51b5;}.theme-indigo .sidebar .menu .list .toggled{background-color:transparent;}.theme-indigo .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-indigo .sidebar .legal{background-color:#fff;}.theme-indigo .sidebar .legal .copyright a{color:#3f51b5 !important;}.theme-blue .navbar{background-color:#2196f3;}.theme-blue .navbar-brand{color:#fff;}.theme-blue .navbar-brand:hover{color:#fff;}.theme-blue .navbar-brand:active{color:#fff;}.theme-blue .navbar-brand:focus{color:#fff;}.theme-blue .nav>li>a{color:#fff;}.theme-blue .nav>li>a:hover{background-color:transparent;}.theme-blue .nav>li>a:focus{background-color:transparent;}.theme-blue .nav .open>a{background-color:transparent;}.theme-blue .nav .open>a:hover{background-color:transparent;}.theme-blue .nav .open>a:focus{background-color:transparent;}.theme-blue .bars{color:#fff;}.theme-blue .sidebar .menu .list li.active{background-color:transparent;}.theme-blue .sidebar .menu .list li.active>:first-child i,.theme-blue .sidebar .menu .list li.active>:first-child span{color:#2196f3;}.theme-blue .sidebar .menu .list .toggled{background-color:transparent;}.theme-blue .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-blue .sidebar .legal{background-color:#fff;}.theme-blue .sidebar .legal .copyright a{color:#2196f3 !important;}.theme-light-blue .navbar{background-color:#03a9f4;}.theme-light-blue .navbar-brand{color:#fff;}.theme-light-blue .navbar-brand:hover{color:#fff;}.theme-light-blue .navbar-brand:active{color:#fff;}.theme-light-blue .navbar-brand:focus{color:#fff;}.theme-light-blue .nav>li>a{color:#fff;}.theme-light-blue .nav>li>a:hover{background-color:transparent;}.theme-light-blue .nav>li>a:focus{background-color:transparent;}.theme-light-blue .nav .open>a{background-color:transparent;}.theme-light-blue .nav .open>a:hover{background-color:transparent;}.theme-light-blue .nav .open>a:focus{background-color:transparent;}.theme-light-blue .bars{color:#fff;}.theme-light-blue .sidebar .menu .list li.active{background-color:transparent;}.theme-light-blue .sidebar .menu .list li.active>:first-child i,.theme-light-blue .sidebar .menu .list li.active>:first-child span{color:#03a9f4;}.theme-light-blue .sidebar .menu .list .toggled{background-color:transparent;}.theme-light-blue .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-light-blue .sidebar .legal{background-color:#fff;}.theme-light-blue .sidebar .legal .copyright a{color:#03a9f4 !important;}.theme-cyan .navbar{background-color:#00bcd4;}.theme-cyan .navbar-brand{color:#fff;}.theme-cyan .navbar-brand:hover{color:#fff;}.theme-cyan .navbar-brand:active{color:#fff;}.theme-cyan .navbar-brand:focus{color:#fff;}.theme-cyan .nav>li>a{color:#fff;}.theme-cyan .nav>li>a:hover{background-color:transparent;}.theme-cyan .nav>li>a:focus{background-color:transparent;}.theme-cyan .nav .open>a{background-color:transparent;}.theme-cyan .nav .open>a:hover{background-color:transparent;}.theme-cyan .nav .open>a:focus{background-color:transparent;}.theme-cyan .bars{color:#fff;}.theme-cyan .sidebar .menu .list li.active{background-color:transparent;}.theme-cyan .sidebar .menu .list li.active>:first-child i,.theme-cyan .sidebar .menu .list li.active>:first-child span{color:#00bcd4;}.theme-cyan .sidebar .menu .list .toggled{background-color:transparent;}.theme-cyan .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-cyan .sidebar .legal{background-color:#fff;}.theme-cyan .sidebar .legal .copyright a{color:#00bcd4 !important;}.theme-teal .navbar{background-color:#009688;}.theme-teal .navbar-brand{color:#fff;}.theme-teal .navbar-brand:hover{color:#fff;}.theme-teal .navbar-brand:active{color:#fff;}.theme-teal .navbar-brand:focus{color:#fff;}.theme-teal .nav>li>a{color:#fff;}.theme-teal .nav>li>a:hover{background-color:transparent;}.theme-teal .nav>li>a:focus{background-color:transparent;}.theme-teal .nav .open>a{background-color:transparent;}.theme-teal .nav .open>a:hover{background-color:transparent;}.theme-teal .nav .open>a:focus{background-color:transparent;}.theme-teal .bars{color:#fff;}.theme-teal .sidebar .menu .list li.active{background-color:transparent;}.theme-teal .sidebar .menu .list li.active>:first-child i,.theme-teal .sidebar .menu .list li.active>:first-child span{color:#009688;}.theme-teal .sidebar .menu .list .toggled{background-color:transparent;}.theme-teal .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-teal .sidebar .legal{background-color:#fff;}.theme-teal .sidebar .legal .copyright a{color:#009688 !important;}.theme-green .navbar{background-color:#4caf50;}.theme-green .navbar-brand{color:#fff;}.theme-green .navbar-brand:hover{color:#fff;}.theme-green .navbar-brand:active{color:#fff;}.theme-green .navbar-brand:focus{color:#fff;}.theme-green .nav>li>a{color:#fff;}.theme-green .nav>li>a:hover{background-color:transparent;}.theme-green .nav>li>a:focus{background-color:transparent;}.theme-green .nav .open>a{background-color:transparent;}.theme-green .nav .open>a:hover{background-color:transparent;}.theme-green .nav .open>a:focus{background-color:transparent;}.theme-green .bars{color:#fff;}.theme-green .sidebar .menu .list li.active{background-color:transparent;}.theme-green .sidebar .menu .list li.active>:first-child i,.theme-green .sidebar .menu .list li.active>:first-child span{color:#4caf50;}.theme-green .sidebar .menu .list .toggled{background-color:transparent;}.theme-green .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-green .sidebar .legal{background-color:#fff;}.theme-green .sidebar .legal .copyright a{color:#4caf50 !important;}.theme-light-green .navbar{background-color:#8bc34a;}.theme-light-green .navbar-brand{color:#fff;}.theme-light-green .navbar-brand:hover{color:#fff;}.theme-light-green .navbar-brand:active{color:#fff;}.theme-light-green .navbar-brand:focus{color:#fff;}.theme-light-green .nav>li>a{color:#fff;}.theme-light-green .nav>li>a:hover{background-color:transparent;}.theme-light-green .nav>li>a:focus{background-color:transparent;}.theme-light-green .nav .open>a{background-color:transparent;}.theme-light-green .nav .open>a:hover{background-color:transparent;}.theme-light-green .nav .open>a:focus{background-color:transparent;}.theme-light-green .bars{color:#fff;}.theme-light-green .sidebar .menu .list li.active{background-color:transparent;}.theme-light-green .sidebar .menu .list li.active>:first-child i,.theme-light-green .sidebar .menu .list li.active>:first-child span{color:#8bc34a;}.theme-light-green .sidebar .menu .list .toggled{background-color:transparent;}.theme-light-green .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-light-green .sidebar .legal{background-color:#fff;}.theme-light-green .sidebar .legal .copyright a{color:#8bc34a !important;}.theme-lime .navbar{background-color:#cddc39;}.theme-lime .navbar-brand{color:#fff;}.theme-lime .navbar-brand:hover{color:#fff;}.theme-lime .navbar-brand:active{color:#fff;}.theme-lime .navbar-brand:focus{color:#fff;}.theme-lime .nav>li>a{color:#fff;}.theme-lime .nav>li>a:hover{background-color:transparent;}.theme-lime .nav>li>a:focus{background-color:transparent;}.theme-lime .nav .open>a{background-color:transparent;}.theme-lime .nav .open>a:hover{background-color:transparent;}.theme-lime .nav .open>a:focus{background-color:transparent;}.theme-lime .bars{color:#fff;}.theme-lime .sidebar .menu .list li.active{background-color:transparent;}.theme-lime .sidebar .menu .list li.active>:first-child i,.theme-lime .sidebar .menu .list li.active>:first-child span{color:#cddc39;}.theme-lime .sidebar .menu .list .toggled{background-color:transparent;}.theme-lime .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-lime .sidebar .legal{background-color:#fff;}.theme-lime .sidebar .legal .copyright a{color:#cddc39 !important;}.theme-yellow .navbar{background-color:#ffeb3b;}.theme-yellow .navbar-brand{color:#fff;}.theme-yellow .navbar-brand:hover{color:#fff;}.theme-yellow .navbar-brand:active{color:#fff;}.theme-yellow .navbar-brand:focus{color:#fff;}.theme-yellow .nav>li>a{color:#fff;}.theme-yellow .nav>li>a:hover{background-color:transparent;}.theme-yellow .nav>li>a:focus{background-color:transparent;}.theme-yellow .nav .open>a{background-color:transparent;}.theme-yellow .nav .open>a:hover{background-color:transparent;}.theme-yellow .nav .open>a:focus{background-color:transparent;}.theme-yellow .bars{color:#fff;}.theme-yellow .sidebar .menu .list li.active{background-color:transparent;}.theme-yellow .sidebar .menu .list li.active>:first-child i,.theme-yellow .sidebar .menu .list li.active>:first-child span{color:#ffeb3b;}.theme-yellow .sidebar .menu .list .toggled{background-color:transparent;}.theme-yellow .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-yellow .sidebar .legal{background-color:#fff;}.theme-yellow .sidebar .legal .copyright a{color:#ffeb3b !important;}.theme-amber .navbar{background-color:#ffc107;}.theme-amber .navbar-brand{color:#fff;}.theme-amber .navbar-brand:hover{color:#fff;}.theme-amber .navbar-brand:active{color:#fff;}.theme-amber .navbar-brand:focus{color:#fff;}.theme-amber .nav>li>a{color:#fff;}.theme-amber .nav>li>a:hover{background-color:transparent;}.theme-amber .nav>li>a:focus{background-color:transparent;}.theme-amber .nav .open>a{background-color:transparent;}.theme-amber .nav .open>a:hover{background-color:transparent;}.theme-amber .nav .open>a:focus{background-color:transparent;}.theme-amber .bars{color:#fff;}.theme-amber .sidebar .menu .list li.active{background-color:transparent;}.theme-amber .sidebar .menu .list li.active>:first-child i,.theme-amber .sidebar .menu .list li.active>:first-child span{color:#ffc107;}.theme-amber .sidebar .menu .list .toggled{background-color:transparent;}.theme-amber .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-amber .sidebar .legal{background-color:#fff;}.theme-amber .sidebar .legal .copyright a{color:#ffc107 !important;}.theme-orange .navbar{background-color:#ff9800;}.theme-orange .navbar-brand{color:#fff;}.theme-orange .navbar-brand:hover{color:#fff;}.theme-orange .navbar-brand:active{color:#fff;}.theme-orange .navbar-brand:focus{color:#fff;}.theme-orange .nav>li>a{color:#fff;}.theme-orange .nav>li>a:hover{background-color:transparent;}.theme-orange .nav>li>a:focus{background-color:transparent;}.theme-orange .nav .open>a{background-color:transparent;}.theme-orange .nav .open>a:hover{background-color:transparent;}.theme-orange .nav .open>a:focus{background-color:transparent;}.theme-orange .bars{color:#fff;}.theme-orange .sidebar .menu .list li.active{background-color:transparent;}.theme-orange .sidebar .menu .list li.active>:first-child i,.theme-orange .sidebar .menu .list li.active>:first-child span{color:#ff9800;}.theme-orange .sidebar .menu .list .toggled{background-color:transparent;}.theme-orange .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-orange .sidebar .legal{background-color:#fff;}.theme-orange .sidebar .legal .copyright a{color:#ff9800 !important;}.theme-deep-orange .navbar{background-color:#ff5722;}.theme-deep-orange .navbar-brand{color:#fff;}.theme-deep-orange .navbar-brand:hover{color:#fff;}.theme-deep-orange .navbar-brand:active{color:#fff;}.theme-deep-orange .navbar-brand:focus{color:#fff;}.theme-deep-orange .nav>li>a{color:#fff;}.theme-deep-orange .nav>li>a:hover{background-color:transparent;}.theme-deep-orange .nav>li>a:focus{background-color:transparent;}.theme-deep-orange .nav .open>a{background-color:transparent;}.theme-deep-orange .nav .open>a:hover{background-color:transparent;}.theme-deep-orange .nav .open>a:focus{background-color:transparent;}.theme-deep-orange .bars{color:#fff;}.theme-deep-orange .sidebar .menu .list li.active{background-color:transparent;}.theme-deep-orange .sidebar .menu .list li.active>:first-child i,.theme-deep-orange .sidebar .menu .list li.active>:first-child span{color:#ff5722;}.theme-deep-orange .sidebar .menu .list .toggled{background-color:transparent;}.theme-deep-orange .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-deep-orange .sidebar .legal{background-color:#fff;}.theme-deep-orange .sidebar .legal .copyright a{color:#ff5722 !important;}.theme-brown .navbar{background-color:#795548;}.theme-brown .navbar-brand{color:#fff;}.theme-brown .navbar-brand:hover{color:#fff;}.theme-brown .navbar-brand:active{color:#fff;}.theme-brown .navbar-brand:focus{color:#fff;}.theme-brown .nav>li>a{color:#fff;}.theme-brown .nav>li>a:hover{background-color:transparent;}.theme-brown .nav>li>a:focus{background-color:transparent;}.theme-brown .nav .open>a{background-color:transparent;}.theme-brown .nav .open>a:hover{background-color:transparent;}.theme-brown .nav .open>a:focus{background-color:transparent;}.theme-brown .bars{color:#fff;}.theme-brown .sidebar .menu .list li.active{background-color:transparent;}.theme-brown .sidebar .menu .list li.active>:first-child i,.theme-brown .sidebar .menu .list li.active>:first-child span{color:#795548;}.theme-brown .sidebar .menu .list .toggled{background-color:transparent;}.theme-brown .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-brown .sidebar .legal{background-color:#fff;}.theme-brown .sidebar .legal .copyright a{color:#795548 !important;}.theme-grey .navbar{background-color:#9e9e9e;}.theme-grey .navbar-brand{color:#fff;}.theme-grey .navbar-brand:hover{color:#fff;}.theme-grey .navbar-brand:active{color:#fff;}.theme-grey .navbar-brand:focus{color:#fff;}.theme-grey .nav>li>a{color:#fff;}.theme-grey .nav>li>a:hover{background-color:transparent;}.theme-grey .nav>li>a:focus{background-color:transparent;}.theme-grey .nav .open>a{background-color:transparent;}.theme-grey .nav .open>a:hover{background-color:transparent;}.theme-grey .nav .open>a:focus{background-color:transparent;}.theme-grey .bars{color:#fff;}.theme-grey .sidebar .menu .list li.active{background-color:transparent;}.theme-grey .sidebar .menu .list li.active>:first-child i,.theme-grey .sidebar .menu .list li.active>:first-child span{color:#9e9e9e;}.theme-grey .sidebar .menu .list .toggled{background-color:transparent;}.theme-grey .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-grey .sidebar .legal{background-color:#fff;}.theme-grey .sidebar .legal .copyright a{color:#9e9e9e !important;}.theme-blue-grey .navbar{background-color:#607d8b;}.theme-blue-grey .navbar-brand{color:#fff;}.theme-blue-grey .navbar-brand:hover{color:#fff;}.theme-blue-grey .navbar-brand:active{color:#fff;}.theme-blue-grey .navbar-brand:focus{color:#fff;}.theme-blue-grey .nav>li>a{color:#fff;}.theme-blue-grey .nav>li>a:hover{background-color:transparent;}.theme-blue-grey .nav>li>a:focus{background-color:transparent;}.theme-blue-grey .nav .open>a{background-color:transparent;}.theme-blue-grey .nav .open>a:hover{background-color:transparent;}.theme-blue-grey .nav .open>a:focus{background-color:transparent;}.theme-blue-grey .bars{color:#fff;}.theme-blue-grey .sidebar .menu .list li.active{background-color:transparent;}.theme-blue-grey .sidebar .menu .list li.active>:first-child i,.theme-blue-grey .sidebar .menu .list li.active>:first-child span{color:#607d8b;}.theme-blue-grey .sidebar .menu .list .toggled{background-color:transparent;}.theme-blue-grey .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-blue-grey .sidebar .legal{background-color:#fff;}.theme-blue-grey .sidebar .legal .copyright a{color:#607d8b !important;}.theme-black .navbar{background-color:#000;}.theme-black .navbar-brand{color:#fff;}.theme-black .navbar-brand:hover{color:#fff;}.theme-black .navbar-brand:active{color:#fff;}.theme-black .navbar-brand:focus{color:#fff;}.theme-black .nav>li>a{color:#fff;}.theme-black .nav>li>a:hover{background-color:transparent;}.theme-black .nav>li>a:focus{background-color:transparent;}.theme-black .nav .open>a{background-color:transparent;}.theme-black .nav .open>a:hover{background-color:transparent;}.theme-black .nav .open>a:focus{background-color:transparent;}.theme-black .bars{color:#fff;}.theme-black .sidebar .menu .list li.active{background-color:transparent;}.theme-black .sidebar .menu .list li.active>:first-child i,.theme-black .sidebar .menu .list li.active>:first-child span{color:#000;}.theme-black .sidebar .menu .list .toggled{background-color:transparent;}.theme-black .sidebar .menu .list .ml-menu{background-color:transparent;}.theme-black .sidebar .legal{background-color:#fff;}.theme-black .sidebar .legal .copyright a{color:#000 !important;} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/error.html b/Plan/common/src/main/resources/assets/plan/web/error.html index 107462cee..f7ed77f1d 100644 --- a/Plan/common/src/main/resources/assets/plan/web/error.html +++ b/Plan/common/src/main/resources/assets/plan/web/error.html @@ -1,270 +1,261 @@ - + - - - + + + + + + + ${titleText} - - - - - + + + - - - - - - - - - - + + - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Loading Data...

    -
    -
    - - -
    - - - - - - - -
    - - - - - - -
    + -
    - -
    -
    -
    -
    -
    -
    -

    ${title}

    + +
    + + + + + + +
    + + + +
    + +
    +
    + +
    +

    ${title}

    +
    + +
    + +
    +
    +
    + ${paragraph} +
    +
    +
    +
    +
    +
    + + + +
    - - + + - - + + - - + + - - + + + + - - + + + + + + + + + + - - - + \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/img/Flaticon_circle.png b/Plan/common/src/main/resources/assets/plan/web/img/Flaticon_circle.png new file mode 100644 index 000000000..89c9f5ea3 Binary files /dev/null and b/Plan/common/src/main/resources/assets/plan/web/img/Flaticon_circle.png differ diff --git a/Plan/common/src/main/resources/assets/plan/web/js/admin.js b/Plan/common/src/main/resources/assets/plan/web/js/admin.js deleted file mode 100644 index ac115137d..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/admin.js +++ /dev/null @@ -1,463 +0,0 @@ -if (typeof jQuery === "undefined") { - throw new Error("jQuery plugins need to be before this file"); -} - -$.AdminBSB = {}; -$.AdminBSB.options = { - colors: { - red: '#F44336', - pink: '#E91E63', - purple: '#9C27B0', - deepPurple: '#673AB7', - indigo: '#3F51B5', - blue: '#2196F3', - lightBlue: '#03A9F4', - cyan: '#00BCD4', - teal: '#009688', - green: '#4CAF50', - lightGreen: '#8BC34A', - lime: '#CDDC39', - yellow: '#ffe821', - amber: '#FFC107', - orange: '#FF9800', - deepOrange: '#FF5722', - brown: '#795548', - grey: '#9E9E9E', - blueGrey: '#607D8B', - black: '#000000', - white: '#ffffff' - }, - leftSideBar: { - scrollColor: 'rgba(0,0,0,0.5)', - scrollWidth: '4px', - scrollAlwaysVisible: false, - scrollBorderRadius: '0', - scrollRailBorderRadius: '0', - scrollActiveItemWhenPageLoad: true, - breakpointWidth: 1170 - }, - dropdownMenu: { - effectIn: 'fadeIn', - effectOut: 'fadeOut' - } -}; - -/* Left Sidebar - Function ================================================================================================= -* You can manage the left sidebar menu options -* -*/ -$.AdminBSB.leftSideBar = { - activate: function () { - var _this = this; - var $body = $('body'); - var $overlay = $('.overlay'); - - //Close sidebar - $(window).click(function (e) { - var $target = $(e.target); - if (e.target.nodeName.toLowerCase() === 'i') { $target = $(e.target).parent(); } - - if (!$target.hasClass('bars') && _this.isOpen() && $target.parents('#leftsidebar').length === 0) { - if (!$target.hasClass('js-right-sidebar')) $overlay.fadeOut(); - $body.removeClass('overlay-open'); - } - }); - - $.each($('.menu-toggle.toggled'), function (i, val) { - $(val).next().slideToggle(0); - }); - - //When page load - $.each($('.menu .list li.active'), function (i, val) { - var $activeAnchors = $(val).find('a:eq(0)'); - - $activeAnchors.addClass('toggled'); - $activeAnchors.next().show(); - }); - - //Collapse or Expand Menu - $('.menu-toggle').on('click', function (e) { - var $this = $(this); - var $content = $this.next(); - - if ($($this.parents('ul')[0]).hasClass('list')) { - var $not = $(e.target).hasClass('menu-toggle') ? e.target : $(e.target).parents('.menu-toggle'); - - $.each($('.menu-toggle.toggled').not($not).next(), function (i, val) { - if ($(val).is(':visible')) { - $(val).prev().toggleClass('toggled'); - $(val).slideUp(); - } - }); - } - - $this.toggleClass('toggled'); - $content.slideToggle(320); - }); - - //Set menu height - _this.setMenuHeight(); - _this.checkStatuForResize(true); - $(window).resize(function () { - _this.setMenuHeight(); - _this.checkStatuForResize(false); - }); - - //Set Waves - Waves.attach('.menu .list a', ['waves-block']); - Waves.init(); - }, - setMenuHeight: function (isFirstTime) { - if (typeof $.fn.slimScroll != 'undefined') { - var configs = $.AdminBSB.options.leftSideBar; - var height = ($(window).height() - ($('.legal').outerHeight() + $('.user-info').outerHeight() + $('.navbar').innerHeight())); - var $el = $('.list'); - - $el.slimscroll({ - height: height + "px", - color: configs.scrollColor, - size: configs.scrollWidth, - alwaysVisible: configs.scrollAlwaysVisible, - borderRadius: configs.scrollBorderRadius, - railBorderRadius: configs.scrollRailBorderRadius - }); - - //Scroll active menu item when page load, if option set = true - if ($.AdminBSB.options.leftSideBar.scrollActiveItemWhenPageLoad) { - var activeItemOffsetTop = $('.menu .list li.active')[0].offsetTop; - if (activeItemOffsetTop > 150) $el.slimscroll({ scrollTo: activeItemOffsetTop + 'px' }); - } - } - }, - checkStatuForResize: function (firstTime) { - var $body = $('body'); - var $openCloseBar = $('.navbar .navbar-header .bars'); - var width = $body.width(); - - if (firstTime) { - $body.find('.content, .sidebar').addClass('no-animate').delay(1000).queue(function () { - $(this).removeClass('no-animate').dequeue(); - }); - } - - if (width < $.AdminBSB.options.leftSideBar.breakpointWidth) { - $body.addClass('ls-closed'); - $openCloseBar.fadeIn(); - } - else { - $body.removeClass('ls-closed'); - $openCloseBar.fadeOut(); - } - }, - isOpen: function () { - return $('body').hasClass('overlay-open'); - } -}; -//========================================================================================================================== - -/* Right Sidebar - Function ================================================================================================ -* You can manage the right sidebar menu options -* -*/ -$.AdminBSB.rightSideBar = { - activate: function () { - var _this = this; - var $sidebar = $('#rightsidebar'); - var $overlay = $('.overlay'); - - //Close sidebar - $(window).click(function (e) { - var $target = $(e.target); - if (e.target.nodeName.toLowerCase() === 'i') { $target = $(e.target).parent(); } - - if (!$target.hasClass('js-right-sidebar') && _this.isOpen() && $target.parents('#rightsidebar').length === 0) { - if (!$target.hasClass('bars')) $overlay.fadeOut(); - $sidebar.removeClass('open'); - } - }); - - $('.js-right-sidebar').on('click', function () { - $sidebar.toggleClass('open'); - if (_this.isOpen()) { $overlay.fadeIn(); } else { $overlay.fadeOut(); } - }); - }, - isOpen: function () { - return $('.right-sidebar').hasClass('open'); - } -}; -//========================================================================================================================== - -/* Searchbar - Function ================================================================================================ -* You can manage the search bar -* -*/ -var $searchBar = $('.search-bar'); -$.AdminBSB.search = { - activate: function () { - var _this = this; - - //Search button click event - $('.js-search').on('click', function () { - _this.showSearchBar(); - }); - - //Close search click event - $searchBar.find('.close-search').on('click', function () { - _this.hideSearchBar(); - }); - - //ESC key on pressed - $searchBar.find('input[type="text"]').on('keyup', function (e) { - if (e.keyCode == 27) { - _this.hideSearchBar(); - } - }); - }, - showSearchBar: function () { - $searchBar.addClass('open'); - $searchBar.find('input[type="text"]').focus(); - }, - hideSearchBar: function () { - $searchBar.removeClass('open'); - $searchBar.find('input[type="text"]').val(''); - } -}; -//========================================================================================================================== - -/* Navbar - Function ======================================================================================================= -* You can manage the navbar -* -*/ -$.AdminBSB.navbar = { - activate: function () { - var $body = $('body'); - var $overlay = $('.overlay'); - - //Open left sidebar panel - $('.bars').on('click', function () { - $body.toggleClass('overlay-open'); - if ($body.hasClass('overlay-open')) { $overlay.fadeIn(); } else { $overlay.fadeOut(); } - }); - - //Close collapse bar on click event - $('.nav [data-close="true"]').on('click', function () { - var isVisible = $('.navbar-toggle').is(':visible'); - var $navbarCollapse = $('.navbar-collapse'); - - if (isVisible) { - $navbarCollapse.slideUp(function () { - $navbarCollapse.removeClass('in').removeAttr('style'); - }); - } - }); - } -}; -//========================================================================================================================== - -/* Input - Function ======================================================================================================== -* You can manage the inputs(also textareas) with name of class 'form-control' -* -*/ -$.AdminBSB.input = { - activate: function () { - //On focus event - $('.form-control').focus(function () { - $(this).parent().addClass('focused'); - }); - - //On focusout event - $('.form-control').focusout(function () { - var $this = $(this); - if ($this.parents('.form-group').hasClass('form-float')) { - if ($this.val() == '') { $this.parents('.form-line').removeClass('focused'); } - } - else { - $this.parents('.form-line').removeClass('focused'); - } - }); - - //On label click - $('body').on('click', '.form-float .form-line .form-label', function () { - $(this).parent().find('input').focus(); - }); - - //Not blank form - $('.form-control').each(function () { - if ($(this).val() !== '') { - $(this).parents('.form-line').addClass('focused'); - } - }); - } -}; -//========================================================================================================================== - -/* Form - Select - Function ================================================================================================ -* You can manage the 'select' of form elements -* -*/ -$.AdminBSB.select = { - activate: function () { - if ($.fn.selectpicker) { $('select:not(.ms)').selectpicker(); } - } -}; -//========================================================================================================================== - -/* DropdownMenu - Function ================================================================================================= -* You can manage the dropdown menu -* -*/ - -$.AdminBSB.dropdownMenu = { - activate: function () { - var _this = this; - - $('.dropdown, .dropup, .btn-group').on({ - "show.bs.dropdown": function () { - var dropdown = _this.dropdownEffect(this); - _this.dropdownEffectStart(dropdown, dropdown.effectIn); - }, - "shown.bs.dropdown": function () { - var dropdown = _this.dropdownEffect(this); - if (dropdown.effectIn && dropdown.effectOut) { - _this.dropdownEffectEnd(dropdown, function () { }); - } - }, - "hide.bs.dropdown": function (e) { - var dropdown = _this.dropdownEffect(this); - if (dropdown.effectOut) { - e.preventDefault(); - _this.dropdownEffectStart(dropdown, dropdown.effectOut); - _this.dropdownEffectEnd(dropdown, function () { - dropdown.dropdown.removeClass('open'); - }); - } - } - }); - - //Set Waves - Waves.attach('.dropdown-menu li a', ['waves-block']); - Waves.init(); - }, - dropdownEffect: function (target) { - var effectIn = $.AdminBSB.options.dropdownMenu.effectIn, effectOut = $.AdminBSB.options.dropdownMenu.effectOut; - var dropdown = $(target), dropdownMenu = $('.dropdown-menu', target); - - if (dropdown.length > 0) { - var udEffectIn = dropdown.data('effect-in'); - var udEffectOut = dropdown.data('effect-out'); - if (udEffectIn !== undefined) { effectIn = udEffectIn; } - if (udEffectOut !== undefined) { effectOut = udEffectOut; } - } - - return { - target: target, - dropdown: dropdown, - dropdownMenu: dropdownMenu, - effectIn: effectIn, - effectOut: effectOut - }; - }, - dropdownEffectStart: function (data, effectToStart) { - if (effectToStart) { - data.dropdown.addClass('dropdown-animating'); - data.dropdownMenu.addClass('animated dropdown-animated'); - data.dropdownMenu.addClass(effectToStart); - } - }, - dropdownEffectEnd: function (data, callback) { - var animationEnd = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'; - data.dropdown.one(animationEnd, function () { - data.dropdown.removeClass('dropdown-animating'); - data.dropdownMenu.removeClass('animated dropdown-animated'); - data.dropdownMenu.removeClass(data.effectIn); - data.dropdownMenu.removeClass(data.effectOut); - - if (typeof callback == 'function') { - callback(); - } - }); - } -}; -//========================================================================================================================== - -/* Browser - Function ====================================================================================================== -* You can manage browser -* -*/ -var edge = 'Microsoft Edge'; -var ie10 = 'Internet Explorer 10'; -var ie11 = 'Internet Explorer 11'; -var opera = 'Opera'; -var firefox = 'Mozilla Firefox'; -var chrome = 'Google Chrome'; -var safari = 'Safari'; - -$.AdminBSB.browser = { - activate: function () { - var _this = this; - var className = _this.getClassName(); - - if (className !== '') $('html').addClass(_this.getClassName()); - }, - getBrowser: function () { - var userAgent = navigator.userAgent.toLowerCase(); - - if (/edge/i.test(userAgent)) { - return edge; - } else if (/rv:11/i.test(userAgent)) { - return ie11; - } else if (/msie 10/i.test(userAgent)) { - return ie10; - } else if (/opr/i.test(userAgent)) { - return opera; - } else if (/chrome/i.test(userAgent)) { - return chrome; - } else if (/firefox/i.test(userAgent)) { - return firefox; - } else if (!!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/)) { - return safari; - } - - return undefined; - }, - getClassName: function () { - var browser = this.getBrowser(); - - if (browser === edge) { - return 'edge'; - } else if (browser === ie11) { - return 'ie11'; - } else if (browser === ie10) { - return 'ie10'; - } else if (browser === opera) { - return 'opera'; - } else if (browser === chrome) { - return 'chrome'; - } else if (browser === firefox) { - return 'firefox'; - } else if (browser === safari) { - return 'safari'; - } else { - return ''; - } - } -}; -//========================================================================================================================== - -$(function () { - $.AdminBSB.browser.activate(); - $.AdminBSB.leftSideBar.activate(); - $.AdminBSB.rightSideBar.activate(); - $.AdminBSB.navbar.activate(); - $.AdminBSB.dropdownMenu.activate(); - $.AdminBSB.input.activate(); - $.AdminBSB.select.activate(); - $.AdminBSB.search.activate(); - - setTimeout(function () { $('.page-loader-wrapper').fadeOut(); }, 50); -}); - -$(function () { - //Popover (For Help texts) - $('[data-toggle="popover"]').popover(); -}); \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/activityPie.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/activityPie.js deleted file mode 100644 index 42f1682fc..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/activityPie.js +++ /dev/null @@ -1,25 +0,0 @@ -function activityPie(id, activitySeries) { - Highcharts.chart(id, { - chart: { - plotBackgroundColor: null, - plotBorderWidth: null, - plotShadow: false, - type: 'pie' - }, - title: {text: ''}, - tooltip: { - pointFormat: '{series.name}: {point.y}' - }, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - series: [activitySeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/diskGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/diskGraph.js deleted file mode 100644 index 93a9d27a6..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/diskGraph.js +++ /dev/null @@ -1,41 +0,0 @@ -function diskChart(id, series) { - Highcharts.stockChart(id, { - rangeSelector: { - selected: 2, - buttons: [{ - type: 'hour', - count: 12, - text: '12h' - }, { - type: 'hour', - count: 24, - text: '24h' - }, { - type: 'day', - count: 7, - text: '7d' - }, { - type: 'month', - count: 1, - text: '30d' - }, { - type: 'all', - text: 'All' - }] - }, - yAxis: { - labels: { - formatter: function () { - return this.value + ' Mb'; - } - }, - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - legend: { - enabled: true - }, - series: series - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/healthGauge.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/healthGauge.js deleted file mode 100644 index a060370bf..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/healthGauge.js +++ /dev/null @@ -1,101 +0,0 @@ -function healthGauge(id, healthData) { - var gaugeOptions = { - - chart: { - type: 'solidgauge' - }, - - title: null, - - pane: { - center: ['50%', '85%'], - size: '140%', - startAngle: -90, - endAngle: 90, - background: { - backgroundColor: (Highcharts.theme && Highcharts.theme.background2) || '#EEE', - innerRadius: '60%', - outerRadius: '100%', - shape: 'arc' - } - }, - - tooltip: { - enabled: false - }, - - // the value axis - yAxis: { - stops: [ - [0.1, '#DF5353'], // red - [0.5, '#DDDF0D'], // yellow - [0.9, '#55BF3B'] // green - ], - lineWidth: 0, - minorTickInterval: null, - tickAmount: 2, - title: { - y: -70 - }, - labels: { - y: 16 - } - }, - - plotOptions: { - solidgauge: { - dataLabels: { - y: 5, - borderWidth: 0, - useHTML: true - } - } - } - }; - - var chartSpeed = Highcharts.chart(id, Highcharts.merge(gaugeOptions, { - yAxis: { - min: 0, - max: 100, - title: { - text: 'Server Health' - }, - visible: false - }, - - credits: { - enabled: false - }, - - series: [{ - name: 'health', - data: healthData, - dataLabels: { - formatter: function () { - return '
    ' + (this.y).toFixed(2) + '
    ' + - '' + getLabel(this.y) + '
    '; - } - } - }] - - })); -} - -function getLabel(index) { - if (index >= 80) { - return 'Very Healthy'; - } - if (index >= 60) { - return 'Healthy'; - } - if (index >= 50) { - return 'Good'; - } - if (index >= 30) { - return 'OK'; - } - if (index >= 0) { - return 'Poor'; - } -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/horizontalBarGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/horizontalBarGraph.js deleted file mode 100644 index f4566f3e5..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/horizontalBarGraph.js +++ /dev/null @@ -1,40 +0,0 @@ -function horizontalBarChart(id, categories, series, text) { - Highcharts.chart(id, { - chart: { - type: 'bar' - }, - title: { - text: '' - }, - xAxis: { - categories: categories, - title: { - text: null - } - }, - yAxis: { - min: 0, - title: { - text: text, - align: 'high' - }, - labels: { - overflow: 'justify' - } - }, - legend: { - enabled: false - }, - plotOptions: { - bar: { - dataLabels: { - enabled: true - } - } - }, - credits: { - enabled: true - }, - series: series - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/lineGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/lineGraph.js deleted file mode 100644 index 3dc505205..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/lineGraph.js +++ /dev/null @@ -1,36 +0,0 @@ -function lineChart(id, series) { - Highcharts.stockChart(id, { - rangeSelector: { - selected: 2, - buttons: [{ - type: 'hour', - count: 12, - text: '12h' - }, { - type: 'hour', - count: 24, - text: '24h' - }, { - type: 'day', - count: 7, - text: '7d' - }, { - type: 'month', - count: 1, - text: '30d' - }, { - type: 'all', - text: 'All' - }] - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - legend: { - enabled: true - }, - series: series - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/onlineActivityCalendar.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/onlineActivityCalendar.js deleted file mode 100644 index 4d3c64074..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/onlineActivityCalendar.js +++ /dev/null @@ -1,28 +0,0 @@ -function onlineActivityCalendar(id, events, firstDay) { - $(id).fullCalendar({ - eventColor: '#2196F3', - firstDay: firstDay, - - eventRender: function (eventObj, $el) { - $el.popover({ - content: eventObj.title, - trigger: 'hover', - placement: 'top', - container: 'body' - }); - }, - - events: events, - - height: 'parent', - header: { - left: 'title', - center: '', - right: 'month prev,next' - } - }); - - setTimeout(function () { - $(id).fullCalendar('render') - }, 1000); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/performanceGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/performanceGraph.js deleted file mode 100644 index 4914a317a..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/performanceGraph.js +++ /dev/null @@ -1,72 +0,0 @@ -function performanceChart(id, playersOnlineSeries, tpsSeries, cpuSeries, ramSeries, entitySeries, chunkSeries) { - Highcharts.stockChart(id, { - rangeSelector: { - selected: 2, - buttons: [{ - type: 'hour', - count: 12, - text: '12h' - }, { - type: 'hour', - count: 24, - text: '24h' - }, { - type: 'day', - count: 7, - text: '7d' - }, { - type: 'month', - count: 1, - text: '30d' - }, { - type: 'all', - text: 'All' - }] - }, - title: {text: ''}, - yAxis: [{ - labels: { - formatter: function () { - return this.value + ' P'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + ' TPS'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + '%'; - } - } - }, { - labels: { - formatter: function () { - return this.value + ' MB'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + ' E'; - } - } - }, { - labels: { - formatter: function () { - return this.value + ' C'; - } - } - }], - legend: { - enabled: true - }, - series: [playersOnlineSeries, tpsSeries, cpuSeries, ramSeries, entitySeries, chunkSeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/playerGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/playerGraph.js deleted file mode 100644 index 13fff8f91..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/playerGraph.js +++ /dev/null @@ -1,38 +0,0 @@ -function playersChart(id, playersOnlineSeries, sel) { - Highcharts.stockChart(id, { - rangeSelector: { - selected: sel, - buttons: [{ - type: 'hour', - count: 12, - text: '12h' - },{ - type: 'hour', - count: 24, - text: '24h' - },{ - type: 'day', - count: 7, - text: '7d' - },{ - type: 'month', - count: 1, - text: '30d' - },{ - type: 'all', - text: 'All' - }] - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - series: [playersOnlineSeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/playerGraphNoNav.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/playerGraphNoNav.js deleted file mode 100644 index 2b08045d5..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/playerGraphNoNav.js +++ /dev/null @@ -1,41 +0,0 @@ -function playersChartNoNav(id, playersOnlineSeries) { - Highcharts.stockChart(id, { - rangeSelector: { - selected: 3, - buttons: [{ - type: 'hour', - count: 12, - text: '12h' - }, { - type: 'hour', - count: 24, - text: '24h' - }, { - type: 'day', - count: 7, - text: '7d' - }, { - type: 'month', - count: 1, - text: '30d' - }, { - type: 'all', - text: 'All' - }] - }, - navigator: { - enabled: false - }, - yAxis: { - softMax: 2, - softMin: 0 - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - series: [playersOnlineSeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/punchCard.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/punchCard.js deleted file mode 100644 index f7c3280f9..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/punchCard.js +++ /dev/null @@ -1,30 +0,0 @@ -function punchCard(id, punchcardSeries) { - Highcharts.chart(id, { - chart: { - defaultSeriesType: 'scatter' - }, - title: {text: ''}, - xAxis: { - type: 'datetime', - dateTimeLabelFormats: { - hour: '%I %P', - day: '%H %P' - }, - tickInterval: 3600000 - }, - time: { - timezoneOffset: 0 - }, - yAxis: { - title: { - text: "Day of the Week" - }, - reversed: true, - categories: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] - }, - tooltip: { - pointFormat: 'Activity: {point.z}' - }, - series: [punchcardSeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/resourceGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/resourceGraph.js deleted file mode 100644 index 723bb00f7..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/resourceGraph.js +++ /dev/null @@ -1,60 +0,0 @@ -function resourceChart(id, cpuSeries, ramSeries, playersOnlineSeries) { - Highcharts.stockChart(id, { - rangeSelector: { - selected: 1, - buttons: [{ - type: 'hour', - count: 12, - text: '12h' - }, { - type: 'hour', - count: 24, - text: '24h' - }, { - type: 'day', - count: 7, - text: '7d' - }, { - type: 'month', - count: 1, - text: '30d' - }, { - type: 'all', - text: 'All' - }] - }, - tooltip: { - split: true - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - yAxis: [{ - labels: { - formatter: function () { - return this.value + ' Players'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + '%'; - } - } - }, { - labels: { - formatter: function () { - return this.value + ' Mb'; - } - } - }], - legend: { - enabled: true - }, - series: [cpuSeries, ramSeries, playersOnlineSeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/serverPie.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/serverPie.js deleted file mode 100644 index f438a0c5c..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/serverPie.js +++ /dev/null @@ -1,53 +0,0 @@ -function serverPie(id, serverSeries) { - Highcharts.chart(id, { - chart: { - plotBackgroundColor: null, - plotBorderWidth: null, - plotShadow: false, - type: 'pie' - }, - title: {text: ''}, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - tooltip: { - formatter: function() { - return ''+this.point.name+': ' + formatTimeAmount(this.y) + ' ('+this.percentage.toFixed(2)+'%)'; - } - }, - series: [serverSeries] - }); -} - -function formatTimeAmount(ms) { - var out = ""; - - var seconds = Math.floor(ms / 1000); - - var dd = Math.floor(seconds / 86400); - seconds -= (dd * 86400); - var dh = Math.floor(seconds / 3600); - seconds -= (dh * 3600); - var dm = Math.floor(seconds / 60); - seconds -= (dm * 60); - seconds = Math.floor(seconds); - if (dd !== 0) { - out += dd.toString() + "d "; - } - if (dh !== 0) { - out += dh.toString() + "h "; - } - if (dm !== 0) { - out += dm.toString() + "m "; - } - out += seconds.toString() + "s "; - - return out; -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/sessionCalendar.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/sessionCalendar.js deleted file mode 100644 index d12c02d97..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/sessionCalendar.js +++ /dev/null @@ -1,30 +0,0 @@ -function sessionCalendar(id, events, firstDay) { - $(id).fullCalendar({ - eventColor: '#009688', - eventLimit: 4, - firstDay: firstDay, - - eventRender: function (eventObj, $el) { - $el.popover({ - content: eventObj.title, - trigger: 'hover', - placement: 'top', - container: 'body' - }); - }, - - events: events, - - navLinks: true, - height: 'parent', - header: { - left: 'title', - center: '', - right: 'month agendaWeek agendaDay today prev,next' - } - }); - - setTimeout(function () { - $(id).fullCalendar('render') - }, 1000); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/stackGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/stackGraph.js deleted file mode 100644 index 23efde59d..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/stackGraph.js +++ /dev/null @@ -1,40 +0,0 @@ -function stackChart(id, categories, series, label) { - Highcharts.chart(id, { - chart: { - type: 'area' - }, - title: { - text: '' - }, - xAxis: { - categories: categories, - tickmarkPlacement: 'on', - title: { - enabled: false - } - }, - yAxis: { - title: { - text: label - }, - labels: { - formatter: function () { - return this.value; - } - }, - softMax: 2, - softMin: 0 - }, - tooltip: { - split: true, - valueSuffix: ' ' + label - }, - plotOptions: { - area: { - stacking: 'normal', - lineWidth: 1 - } - }, - series: series - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/tpsGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/tpsGraph.js deleted file mode 100644 index 5985a1566..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/tpsGraph.js +++ /dev/null @@ -1,54 +0,0 @@ -function tpsChart(id, tpsSeries, playersOnlineSeries) { - Highcharts.stockChart(id, { - rangeSelector: { - selected: 1, - buttons: [{ - type: 'hour', - count: 12, - text: '12h' - }, { - type: 'hour', - count: 24, - text: '24h' - }, { - type: 'day', - count: 7, - text: '7d' - }, { - type: 'month', - count: 1, - text: '30d' - }, { - type: 'all', - text: 'All' - }] - }, - tooltip: { - split: true - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - yAxis: [{ - labels: { - formatter: function () { - return this.value + ' Players'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + ' TPS'; - } - } - }], - legend: { - enabled: true - }, - series: [tpsSeries, playersOnlineSeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/worldGraph.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/worldGraph.js deleted file mode 100644 index 32b85cc66..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/worldGraph.js +++ /dev/null @@ -1,60 +0,0 @@ -function worldChart(id, entitySeries, chunkSeries, playersOnlineSeries) { - Highcharts.stockChart(id, { - rangeSelector: { - selected: 1, - buttons: [{ - type: 'hour', - count: 12, - text: '12h' - }, { - type: 'hour', - count: 24, - text: '24h' - }, { - type: 'day', - count: 7, - text: '7d' - }, { - type: 'month', - count: 1, - text: '30d' - }, { - type: 'all', - text: 'All' - }] - }, - tooltip: { - split: true - }, - title: {text: ''}, - plotOptions: { - areaspline: { - fillOpacity: 0.4 - } - }, - yAxis: [{ - labels: { - formatter: function () { - return this.value + ' Players'; - } - } - }, { - opposite: true, - labels: { - formatter: function () { - return this.value + ' Entities'; - } - } - }, { - labels: { - formatter: function () { - return this.value + ' Chunks'; - } - } - }], - legend: { - enabled: true - }, - series: [entitySeries, chunkSeries, playersOnlineSeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/worldMap.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/worldMap.js deleted file mode 100644 index db66ebd4e..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/worldMap.js +++ /dev/null @@ -1,21 +0,0 @@ -function worldMap(id, colorMin, colorMax, mapSeries) { - Highcharts.mapChart(id, { - chart: { - animation: true - }, - title: {text: ''}, - - mapNavigation: { - enabled: true, - enableDoubleClickZoomTo: true - }, - - colorAxis: { - min: 1, - type: 'logarithmic', - minColor: colorMin, - maxColor: colorMax - }, - series: [mapSeries] - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/charts/worldPie.js b/Plan/common/src/main/resources/assets/plan/web/js/charts/worldPie.js deleted file mode 100644 index ccca32c8f..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/charts/worldPie.js +++ /dev/null @@ -1,70 +0,0 @@ -function worldPie(id, worldSeries, gmSeries) { - var defaultTitle = ''; - var defaultSubtitle = 'Click the slices to view used GameMode'; - - var chart = Highcharts.chart(id, { - chart: { - plotBackgroundColor: null, - plotBorderWidth: null, - plotShadow: false, - type: 'pie', - events: { - drilldown: function (e) { - chart.setTitle({text: '' + e.point.name}, {text: ''}); - }, - drillup: function (e) { - chart.setTitle({text: defaultTitle}, {text: defaultSubtitle}); - } - } - }, - title: {text: defaultTitle}, - subtitle: { - text: defaultSubtitle - }, - plotOptions: { - pie: { - allowPointSelect: true, - cursor: 'pointer', - dataLabels: { - enabled: false - }, - showInLegend: true - } - }, - tooltip: { - formatter: function() { - return ''+this.point.name+': ' + formatTimeAmount(this.y) + ' ('+this.percentage.toFixed(2)+'%)'; - } - }, - series: [worldSeries], - drilldown: { - series: gmSeries - } - }); -} - -function formatTimeAmount(ms) { - var out = ""; - - var seconds = Math.floor(ms / 1000); - - var dd = Math.floor(seconds / 86400); - seconds -= (dd * 86400); - var dh = Math.floor(seconds / 3600); - seconds -= (dh * 3600); - var dm = Math.floor(seconds / 60); - seconds -= (dm * 60); - seconds = Math.floor(seconds); - if (dd !== 0) { - out += dd.toString() + "d "; - } - if (dh !== 0) { - out += dh.toString() + "h "; - } - if (dm !== 0) { - out += dm.toString() + "m "; - } - out += seconds.toString() + "s "; - - return out; -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/color-selector.js b/Plan/common/src/main/resources/assets/plan/web/js/color-selector.js new file mode 100644 index 000000000..066bb1237 --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/color-selector.js @@ -0,0 +1,440 @@ +(function ($) { + var bgElements = ['.sidebar', '.btn']; + var textElements = []; + + var colors = ['plan', + 'red', 'pink', 'purple', 'deep-purple', + 'indigo', 'blue', 'light-blue', 'cyan', + 'teal', 'green', 'light-green', 'lime', + 'yellow', 'amber', 'orange', 'deep-orange', + 'brown', 'grey', 'blue-grey']; + + var selectedColor = window.localStorage.getItem('themeColor'); + var themeDefaultColor = 'plan'; + var currentColor = 'plan'; + + if (selectedColor === null) { + window.localStorage.setItem('themeColor', currentColor); + } + + // Function for changing color + function setColor(nextColor) { + if (!nextColor || nextColor == currentColor) { + return; + } + + for (i in bgElements) { + var element = bgElements[i]; + $(element + '.bg-' + currentColor + ":not(.color-chooser)") + .removeClass('bg-' + currentColor) + .addClass('bg-' + nextColor); + } + for (i in textElements) { + var element = textElements[i]; + $(element + '.col-' + currentColor) + .removeClass('col-' + currentColor) + .addClass('col-' + nextColor); + } + if (nextColor != 'night') { + window.localStorage.setItem('themeColor', nextColor); + } + currentColor = nextColor; + } + + // Set the color changing function for all color change buttons + function enableColorSetters() { + function colorSetter(i) { + return function () { + setColor(colors[i]); + } + } + + for (i in colors) { + var color = colors[i]; + var func = colorSetter(i); + $('#choose-' + color).on('click', func); + $('#choose-' + color).addClass('bg-' + color); + } + } + + enableColorSetters(); + + function disableColorSetters() { + for (i in colors) { + var color = colors[i]; + $('#choose-' + color).addClass('disabled').unbind('click'); + } + } + + // Change the color of the theme + setColor(selectedColor ? selectedColor : themeDefaultColor); + + var nightMode = window.localStorage.getItem('nightMode') == 'true'; + + var saturationReduction = 0.70; + + // From https://stackoverflow.com/a/3732187 + function withReducedSaturation(colorHex) { + // To RGB + var r = parseInt(colorHex.substr(1, 2), 16); // Grab the hex representation of red (chars 1-2) and convert to decimal (base 10). + var g = parseInt(colorHex.substr(3, 2), 16); + var b = parseInt(colorHex.substr(5, 2), 16); + + // To HSL + r /= 255; + g /= 255; + b /= 255; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, l = (max + min) / 2; + + if (max === min) { + h = s = 0; // achromatic + } else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0); + break; + case g: + h = (b - r) / d + 2; + break; + case b: + h = (r - g) / d + 4; + break; + } + h /= 6; + } + + // To css property + return 'hsl(' + h * 360 + ',' + s * 100 * saturationReduction + '%,' + l * 95 + '%)'; + } + + var nightModeColors = '.bg-red {background-color: ' + withReducedSaturation('#F44336') + ';color: #eee8d5;}' + + '.bg-pink {background-color: ' + withReducedSaturation('#E91E63') + ';color: #eee8d5;}' + + '.bg-purple {background-color: ' + withReducedSaturation('#9C27B0') + ';color: #eee8d5;}' + + '.bg-deep-purple {background-color: ' + withReducedSaturation('#673AB7') + ';color: #eee8d5;}' + + '.bg-indigo {background-color: ' + withReducedSaturation('#3F51B5') + ';color: #eee8d5;}' + + '.bg-blue {background-color: ' + withReducedSaturation('#2196F3') + ';color: #eee8d5;}' + + '.bg-light-blue {background-color: ' + withReducedSaturation('#03A9F4') + ';color: #eee8d5;}' + + '.bg-cyan {background-color: ' + withReducedSaturation('#00BCD4') + ';color: #eee8d5;}' + + '.bg-teal {background-color: ' + withReducedSaturation('#009688') + ';color: #eee8d5;}' + + '.bg-green {background-color: ' + withReducedSaturation('#4CAF50') + ';color: #eee8d5;}' + + '.bg-light-green {background-color: ' + withReducedSaturation('#8BC34A') + ';color: #eee8d5;}' + + '.bg-lime {background-color: ' + withReducedSaturation('#CDDC39') + ';color: #eee8d5;}' + + '.bg-yellow {background-color: ' + withReducedSaturation('#ffe821') + ';color: #eee8d5;}' + + '.bg-amber {background-color: ' + withReducedSaturation('#FFC107') + ';color: #eee8d5;}' + + '.badge-warning {background-color: ' + withReducedSaturation('#f6c23e') + ';color: #eee8d5;}' + + '.bg-orange {background-color: ' + withReducedSaturation('#FF9800') + ';color: #eee8d5;}' + + '.bg-deep-orange {background-color: ' + withReducedSaturation('#FF5722') + ';color: #eee8d5;}' + + '.badge-danger {background-color: ' + withReducedSaturation('#e74a3b') + ';color: #eee8d5;}' + + '.bg-brown {background-color: ' + withReducedSaturation('#795548') + ';color: #eee8d5;}' + + '.bg-grey {background-color: ' + withReducedSaturation('#9E9E9E') + ';color: #eee8d5;}' + + '.bg-blue-grey {background-color: ' + withReducedSaturation('#607D8B') + ';color: #eee8d5;}' + + '.bg-black {background-color: ' + withReducedSaturation('#555555') + ';color: #eee8d5;}' + + '.bg-plan {background-color: ' + withReducedSaturation('#368F17') + ';color: #eee8d5;}' + + '.badge-success {background-color: ' + withReducedSaturation('#1cc88a') + ';color: #eee8d5;}' + + '.bg-night {background-color: #44475a;color: #eee8d5;}' + + '.col-red {color: ' + withReducedSaturation('#F44336') + ';}' + + '.col-pink {color: ' + withReducedSaturation('#E91E63') + ';}' + + '.col-purple {color: ' + withReducedSaturation('#9C27B0') + ';}' + + '.col-deep-purple {color: ' + withReducedSaturation('#673AB7') + ';}' + + '.col-indigo {color: ' + withReducedSaturation('#3F51B5') + ';}' + + '.col-blue {color: ' + withReducedSaturation('#2196F3') + ';}' + + '.col-light-blue {color: ' + withReducedSaturation('#03A9F4') + ';}' + + '.col-cyan {color: ' + withReducedSaturation('#00BCD4') + ';}' + + '.col-teal {color: ' + withReducedSaturation('#009688') + ';}' + + '.col-green {color: ' + withReducedSaturation('#4CAF50') + ';}' + + '.col-light-green {color: ' + withReducedSaturation('#8BC34A') + ';}' + + '.col-lime {color: ' + withReducedSaturation('#CDDC39') + ';}' + + '.col-yellow {color: ' + withReducedSaturation('#ffe821') + ';}' + + '.col-amber {color: ' + withReducedSaturation('#FFC107') + ';}' + + '.text-warning {color: ' + withReducedSaturation('#f6c23e') + ';}' + + '.col-orange {color: ' + withReducedSaturation('#FF9800') + ';}' + + '.col-deep-orange {color: ' + withReducedSaturation('#FF5722') + ';}' + + '.text-danger {color: ' + withReducedSaturation('#e74a3b') + ';}' + + '.col-brown {color: ' + withReducedSaturation('#795548') + ';}' + + '.col-grey {color: ' + withReducedSaturation('#9E9E9E') + ';}' + + '.col-blue-grey {color: ' + withReducedSaturation('#607D8B') + ';}' + + '.col-plan {color: ' + withReducedSaturation('#368F17') + ';}' + + '.text-success {color: ' + withReducedSaturation('#1cc88a') + ';}'; + + function changeNightModeCSS() { + if (nightMode) { + // Background colors from dracula theme + $('head').append(''); + // Turn bright tables to dark + $('.table').addClass('table-dark'); + // Sidebar is grey when in night mode + disableColorSetters(); + setColor('night'); + } else { + // Remove night mode style sheet + $('#nightmode').remove(); + // Turn dark tables bright again + $('.table').removeClass('table-dark'); + // Sidebar is colorful + $('.color-chooser').removeClass('disabled'); + enableColorSetters(); + setColor(window.localStorage.getItem('themeColor')); + } + } + + function changeHighChartsNightMode() { + try { + Highcharts.theme = nightMode ? { + chart: { + backgroundColor: '#44475a', + plotBorderColor: '#606063' + }, + title: { + style: {color: '#eee8d5'} + }, + subtitle: { + style: {color: '#eee8d5'} + }, + xAxis: { + gridLineColor: '#707073', + labels: { + style: {color: '#eee8d5'} + }, + lineColor: '#707073', + minorGridLineColor: '#505053', + tickColor: '#707073', + title: { + style: {color: '#eee8d5'} + } + }, + yAxis: { + gridLineColor: '#707073', + labels: { + style: {color: '#eee8d5'} + }, + lineColor: '#707073', + minorGridLineColor: '#505053', + tickColor: '#707073', + tickWidth: 1, + title: { + style: {color: '#eee8d5'} + } + }, + tooltip: { + backgroundColor: '#44475a', + style: {color: '#eee8d5'} + }, + plotOptions: { + series: { + dataLabels: {color: '#B0B0B3'}, + marker: {lineColor: '#333'} + } + }, + legend: { + itemStyle: {color: '#eee8d5'}, + itemHoverStyle: {color: '#eee8d5'}, + itemHiddenStyle: {color: '#606063'} + }, + labels: { + style: {color: '#eee8d5'} + }, + drilldown: { + activeAxisLabelStyle: {color: '#eee8d5'}, + activeDataLabelStyle: {color: '#eee8d5'} + }, + navigation: { + buttonOptions: { + symbolStroke: '#eee8d5', + theme: {fill: '#44475a'} + } + }, + // scroll charts + rangeSelector: { + buttonTheme: { + fill: '#505053', + stroke: '#646e8c', + style: {color: '#CCC'}, + states: { + hover: { + fill: '#646e9d', + stroke: '#646e8c', + style: {color: 'white'} + }, + select: { + fill: '#646e9d', + stroke: '#646e8c', + style: {color: 'white'} + } + } + }, + inputBoxBorderColor: '#505053', + inputStyle: { + backgroundColor: '#333', + color: 'silver' + }, + labelStyle: {color: 'silver'} + }, + + navigator: { + handles: { + backgroundColor: '#666', + borderColor: '#AAA' + }, + outlineColor: '#CCC', + maskFill: 'rgba(255,255,255,0.1)', + series: {lineColor: '#A6C7ED'}, + xAxis: {gridLineColor: '#505053'} + }, + + scrollbar: { + barBackgroundColor: '#808083', + barBorderColor: '#808083', + buttonArrowColor: '#CCC', + buttonBackgroundColor: '#606063', + buttonBorderColor: '#606063', + rifleColor: '#FFF', + trackBackgroundColor: '#404043', + trackBorderColor: '#404043' + } + } : { // Defaults + chart: { + backgroundColor: '#fff', + plotBorderColor: '#cccccc' + }, + title: { + style: {color: '#3E576F'} + }, + subtitle: { + style: {color: '#3E576F'} + }, + xAxis: { + gridLineColor: '#E6E6E6', + labels: { + style: {color: '#333333'} + }, + lineColor: '#E6E6E6', + minorGridLineColor: '#505053', + tickColor: '#E6E6E6', + title: { + style: {color: '#333333'} + } + }, + yAxis: { + gridLineColor: '#E6E6E6', + labels: { + style: {color: '#333333'} + }, + lineColor: '#E6E6E6', + minorGridLineColor: '#505053', + tickColor: '#E6E6E6', + tickWidth: 1, + title: { + style: {color: '#333333'} + } + }, + tooltip: { + backgroundColor: 'rgba(247,247,247,0.85)', + style: {color: '#333333'} + }, + plotOptions: { + series: { + dataLabels: {color: undefined}, + marker: {lineColor: undefined} + } + }, + legend: { + itemStyle: {color: '#333333'}, + itemHoverStyle: {color: '#000000'}, + itemHiddenStyle: {color: '#cccccc'} + }, + labels: { + style: {color: '#333333'} + }, + drilldown: { + activeAxisLabelStyle: {color: '#333333'}, + activeDataLabelStyle: {color: '#333333'} + }, + navigation: { + buttonOptions: { + symbolStroke: '#333333', + theme: {fill: '#CCCCCC'} + } + }, + // scroll charts + rangeSelector: { + buttonTheme: { + fill: '#F7F7F7', + stroke: '#333', + style: {color: '#4B336A'}, + states: { + hover: { + fill: '#E6EBF5', + stroke: '#333', + style: {color: 'black'} + }, + select: { + fill: '#E6EBF5', + stroke: '#333', + style: {color: 'black'} + } + } + }, + inputBoxBorderColor: '#CCCCCC', + inputStyle: { + backgroundColor: '#333', + color: '#666666' + }, + labelStyle: {color: "#666666"} + }, + + navigator: { + handles: { + backgroundColor: '#f2f2f2', + borderColor: '#999999' + }, + outlineColor: '#cccccc', + maskFill: 'rgba(102,133,194,0.3)', + series: {lineColor: "#3FA0FF"}, + xAxis: {gridLineColor: '#e6e6e6'} + }, + + scrollbar: { + barBackgroundColor: '#cccccc', + barBorderColor: '#cccccc', + buttonArrowColor: '#333333', + buttonBackgroundColor: '#e6e6e6', + buttonBorderColor: '#cccccc', + rifleColor: '#333333', + trackBackgroundColor: '#f2f2f2', + trackBorderColor: '#f2f2f2' + } + }; + Highcharts.setOptions(Highcharts.theme); + updateGraphs(); + } catch (e) { + } + } + + changeNightModeCSS(); + changeHighChartsNightMode(); + + function toggleNightMode() { + nightMode = !nightMode; + setTimeout(function () { + window.localStorage.setItem('nightMode', nightMode); + changeNightModeCSS(); + changeHighChartsNightMode(); + }, 0); + } + + $('#night-mode-toggle').on('click', toggleNightMode); + +})(jQuery); \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/demo.js b/Plan/common/src/main/resources/assets/plan/web/js/demo.js deleted file mode 100644 index fbe4f062b..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/demo.js +++ /dev/null @@ -1,68 +0,0 @@ -$(function () { - skinChanger(); - - setSkin(); - - setSkinListHeightAndScroll(true); - $(window).resize(function () { - setSkinListHeightAndScroll(false); - }); -}); - -//Skin changer -function skinChanger() { - $('.right-sidebar .demo-choose-skin li').on('click', function () { - var $body = $('body'); - var $this = $(this); - - var existTheme = $('.right-sidebar .demo-choose-skin li.active').data('theme'); - $('.right-sidebar .demo-choose-skin li').removeClass('active'); - $body.removeClass('theme-' + existTheme); - $this.addClass('active'); - - var theme = $this.data('theme'); - $body.addClass('theme-' + theme); - localStorage.setItem("plan_skin", theme.toString()); - }); -} - -function setSkin() { - var theme = localStorage.getItem("plan_skin"); - if (theme === null) { - theme = '${defaultTheme}' - } - var body = document.getElementsByTagName('body')[0]; - - var classes = body.className.split(' '); - var themeClass; - for (i = 0; i < classes.length; i++) { - if (classes[i].startsWith('theme')) { - themeClass = classes[i]; - break; - } - } - - body.classList.remove(themeClass); - body.classList.add('theme-' + theme); - localStorage.setItem("plan_skin", theme); -} - -//Skin tab content set height and show scroll -function setSkinListHeightAndScroll(isFirstTime) { - var height = $(window).height() - ($('.navbar').innerHeight() + $('.right-sidebar .nav-tabs').outerHeight()); - var $el = $('.demo-choose-skin'); - - if (!isFirstTime) { - $el.slimScroll({destroy: true}).height('auto'); - $el.parent().find('.slimScrollBar, .slimScrollRail').remove(); - } - - $el.slimscroll({ - height: height + 'px', - color: 'rgba(0,0,0,0.5)', - size: '6px', - alwaysVisible: false, - borderRadius: '0', - railBorderRadius: '0' - }); -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/graphs.js b/Plan/common/src/main/resources/assets/plan/web/js/graphs.js new file mode 100644 index 000000000..d90e122d9 --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/graphs.js @@ -0,0 +1,641 @@ +var linegraphButtons = [{ + type: 'hour', + count: 12, + text: '12h' +}, { + type: 'hour', + count: 24, + text: '24h' +}, { + type: 'day', + count: 7, + text: '7d' +}, { + type: 'month', + count: 1, + text: '30d' +}, { + type: 'all', + text: 'All' +}]; + +var graphs = []; +var specialColors = {}; + +function activityPie(id, activitySeries) { + graphs.push(Highcharts.chart(id, { + chart: { + plotBackgroundColor: null, + plotBorderWidth: null, + plotShadow: false, + type: 'pie' + }, + title: {text: ''}, + tooltip: { + pointFormat: '{series.name}: {point.y}' + }, + plotOptions: { + pie: { + allowPointSelect: true, + cursor: 'pointer', + dataLabels: { + enabled: false + }, + showInLegend: true + } + }, + series: [activitySeries] + })); +} + +function diskChart(id, series) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + yAxis: { + labels: { + formatter: function () { + return this.value + ' MB'; + } + }, + softMax: 2, + softMin: 0 + }, + title: {text: ''}, + legend: { + enabled: true + }, + series: series + })); +} + +function horizontalBarChart(id, categories, series, text) { + graphs.push(Highcharts.chart(id, { + chart: { + type: 'bar' + }, + title: { + text: '' + }, + xAxis: { + categories: categories, + title: { + text: null + } + }, + yAxis: { + min: 0, + title: { + text: text, + align: 'high' + }, + labels: { + overflow: 'justify' + } + }, + legend: { + enabled: false + }, + plotOptions: { + bar: { + dataLabels: { + enabled: true + } + } + }, + credits: { + enabled: true + }, + series: series + })); +} + +function lineChart(id, series) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + yAxis: { + softMax: 2, + softMin: 0 + }, + title: {text: ''}, + legend: { + enabled: true + }, + series: series + })); +} + +function dayByDay(id, series) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + yAxis: { + softMax: 2, + softMin: 0 + }, + title: {text: ''}, + legend: { + enabled: true + }, + time: {timezoneOffset: 0}, + series: series + })); +} + +function onlineActivityCalendar(id, events, firstDay) { + $(id).fullCalendar({ + eventColor: '#2196F3', + firstDay: firstDay, + + eventRender: function (eventObj, $el) { + $el.popover({ + content: eventObj.title, + trigger: 'hover', + placement: 'top', + container: 'body' + }); + }, + + events: events, + + height: 800, + contentHeight: 795, + header: { + left: 'title', + center: '', + right: 'month prev,next' + } + }); + + $(id).fullCalendar('render') +} + +function performanceChart(id, playersOnlineSeries, tpsSeries, cpuSeries, ramSeries, entitySeries, chunkSeries) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: 2, + buttons: linegraphButtons + }, + title: {text: ''}, + yAxis: [{ + labels: { + formatter: function () { + return this.value + ' P'; + } + } + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + ' TPS'; + } + } + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + '%'; + } + } + }, { + labels: { + formatter: function () { + return this.value + ' MB'; + } + } + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + ' E'; + } + } + }, { + labels: { + formatter: function () { + return this.value + ' C'; + } + } + }], + legend: { + enabled: true + }, + series: [playersOnlineSeries, tpsSeries, cpuSeries, ramSeries, entitySeries, chunkSeries] + })); +} + +function playersChart(id, playersOnlineSeries, sel) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: sel, + buttons: linegraphButtons + }, + yAxis: { + softMax: 2, + softMin: 0 + }, + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + series: [playersOnlineSeries] + })); +} + +function playersChartNoNav(id, playersOnlineSeries) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: 3, + buttons: linegraphButtons + }, + navigator: { + enabled: false + }, + yAxis: { + softMax: 2, + softMin: 0 + }, + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + series: [playersOnlineSeries] + })); +} + +function punchCard(id, punchcardSeries) { + graphs.push(Highcharts.chart(id, { + chart: { + defaultSeriesType: 'scatter' + }, + title: {text: ''}, + xAxis: { + type: 'datetime', + dateTimeLabelFormats: { + hour: '%I %P', + day: '%H %P' + }, + tickInterval: 3600000 + }, + time: { + timezoneOffset: 0 + }, + yAxis: { + title: { + text: "Day of the Week" + }, + reversed: true, + categories: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] + }, + tooltip: { + pointFormat: 'Activity: {point.z}' + }, + series: [punchcardSeries] + })); +} + +function resourceChart(id, cpuSeries, ramSeries, playersOnlineSeries) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: 1, + buttons: linegraphButtons + }, + tooltip: { + split: true + }, + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + yAxis: [{ + labels: { + formatter: function () { + return this.value + ' Players'; + } + } + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + '%'; + } + } + }, { + labels: { + formatter: function () { + return this.value + ' MB'; + } + } + }], + legend: { + enabled: true + }, + series: [cpuSeries, ramSeries, playersOnlineSeries] + })); +} + +function serverPie(id, serverSeries) { + graphs.push(Highcharts.chart(id, { + chart: { + plotBackgroundColor: null, + plotBorderWidth: null, + plotShadow: false, + type: 'pie' + }, + title: {text: ''}, + plotOptions: { + pie: { + allowPointSelect: true, + cursor: 'pointer', + dataLabels: { + enabled: false + }, + showInLegend: true + } + }, + tooltip: { + formatter: function () { + return '' + this.point.name + ': ' + formatTimeAmount(this.y) + ' (' + this.percentage.toFixed(2) + '%)'; + } + }, + series: [serverSeries] + })); +} + +function formatTimeAmount(ms) { + var out = ""; + + var seconds = Math.floor(ms / 1000); + + var dd = Math.floor(seconds / 86400); + seconds -= (dd * 86400); + var dh = Math.floor(seconds / 3600); + seconds -= (dh * 3600); + var dm = Math.floor(seconds / 60); + seconds -= (dm * 60); + seconds = Math.floor(seconds); + if (dd !== 0) { + out += dd.toString() + "d "; + } + if (dh !== 0) { + out += dh.toString() + "h "; + } + if (dm !== 0) { + out += dm.toString() + "m "; + } + out += seconds.toString() + "s "; + + return out; +} + +function sessionCalendar(id, events, firstDay) { + $(id).fullCalendar({ + eventColor: '#009688', + eventLimit: 4, + firstDay: firstDay, + + eventRender: function (eventObj, $el) { + $el.popover({ + content: eventObj.title, + trigger: 'hover', + placement: 'top', + container: 'body' + }); + }, + + events: events, + + navLinks: true, + height: 450, + contentHeight: 445, + header: { + left: 'title', + center: '', + right: 'month agendaWeek agendaDay today prev,next' + } + }); + + setTimeout(function () { + $(id).fullCalendar('render') + }, 1000); +} + +function stackChart(id, categories, series, label) { + graphs.push(Highcharts.chart(id, { + chart: { + type: 'area' + }, + title: { + text: '' + }, + xAxis: { + categories: categories, + tickmarkPlacement: 'on', + title: { + enabled: false + } + }, + yAxis: { + title: { + text: label + }, + labels: { + formatter: function () { + return this.value; + } + }, + softMax: 2, + softMin: 0 + }, + tooltip: { + split: true, + valueSuffix: ' ' + label + }, + plotOptions: { + area: { + stacking: 'normal', + lineWidth: 1 + } + }, + series: series + })); +} + +function tpsChart(id, tpsSeries, playersOnlineSeries) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: 1, + buttons: linegraphButtons + }, + tooltip: { + split: true + }, + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + yAxis: [{ + labels: { + formatter: function () { + return this.value + ' Players'; + } + } + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + ' TPS'; + } + } + }], + legend: { + enabled: true + }, + series: [tpsSeries, playersOnlineSeries] + })); +} + +function worldChart(id, entitySeries, chunkSeries, playersOnlineSeries) { + graphs.push(Highcharts.stockChart(id, { + rangeSelector: { + selected: 1, + buttons: linegraphButtons + }, + tooltip: { + split: true + }, + title: {text: ''}, + plotOptions: { + areaspline: { + fillOpacity: 0.4 + } + }, + yAxis: [{ + labels: { + formatter: function () { + return this.value + ' Players'; + } + } + }, { + opposite: true, + labels: { + formatter: function () { + return this.value + ' Entities'; + } + } + }, { + labels: { + formatter: function () { + return this.value + ' Chunks'; + } + } + }], + legend: { + enabled: true + }, + series: [entitySeries, chunkSeries, playersOnlineSeries] + })); +} + +function worldMap(id, colorMin, colorMax, mapSeries) { + graphs.push(Highcharts.mapChart(id, { + chart: { + animation: true + }, + title: {text: ''}, + + mapNavigation: { + enabled: true, + enableDoubleClickZoomTo: true + }, + + colorAxis: { + min: 1, + type: 'logarithmic', + minColor: colorMin, + maxColor: colorMax + }, + series: [mapSeries] + })); +} + +function worldPie(id, worldSeries, gmSeries, bgColor) { + var defaultTitle = ''; + var defaultSubtitle = 'Click to expand'; + var nightMode = window.localStorage.getItem('nightMode') == 'true'; + var chart = Highcharts.chart(id, { + chart: { + backgroundColor: nightMode ? (bgColor ? bgColor : '#44475a') : '#fff', + plotBackgroundColor: null, + plotBorderWidth: null, + plotShadow: false, + type: 'pie', + events: { + drilldown: function (e) { + chart.setTitle({text: '' + e.point.name}, {text: ''}); + }, + drillup: function (e) { + chart.setTitle({text: defaultTitle}, {text: defaultSubtitle}); + } + } + }, + title: {text: defaultTitle}, + subtitle: { + text: defaultSubtitle + }, + plotOptions: { + pie: { + allowPointSelect: true, + cursor: 'pointer', + dataLabels: { + enabled: false + }, + showInLegend: true + } + }, + tooltip: { + formatter: function () { + return '' + this.point.name + ': ' + formatTimeAmount(this.y) + ' (' + this.percentage.toFixed(2) + '%)'; + } + }, + series: [worldSeries], + drilldown: { + series: gmSeries.map(function (d) { + return {name: d.name, id: d.id, colors: gmPieColors, data: d.data} + }) + } + }); + if (bgColor) { + specialColors[graphs.length] = bgColor; + } + graphs.push(chart); +} + +function updateGraphs() { + var nightMode = window.localStorage.getItem('nightMode') == 'true'; + for (var i = 0; i < graphs.length; i++) { + graphs[i].update(Highcharts.theme); + if (nightMode && specialColors[i]) { + graphs[i].update({ + chart: { + backgroundColor: specialColors[i] + } + }); + } + } +} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/helpers.js b/Plan/common/src/main/resources/assets/plan/web/js/helpers.js deleted file mode 100644 index 37e3d3230..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/js/helpers.js +++ /dev/null @@ -1,13 +0,0 @@ -function hexToRgb(hexCode) { - var patt = /^#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/; - var matches = patt.exec(hexCode); - var rgb = "rgb(" + parseInt(matches[1], 16) + "," + parseInt(matches[2], 16) + "," + parseInt(matches[3], 16) + ")"; - return rgb; -} - -function hexToRgba(hexCode, opacity) { - var patt = /^#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})$/; - var matches = patt.exec(hexCode); - var rgb = "rgba(" + parseInt(matches[1], 16) + "," + parseInt(matches[2], 16) + "," + parseInt(matches[3], 16) + "," + opacity + ")"; - return rgb; -} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/network-values.js b/Plan/common/src/main/resources/assets/plan/web/js/network-values.js new file mode 100644 index 000000000..f5b16ca6a --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/network-values.js @@ -0,0 +1,305 @@ +var trend_up_good = " "; +var trend_up_bad = " "; +var trend_down_bad = " "; +var trend_down_good = " "; +var trend_same = " "; + +var trend_end = ""; + +function trend(trend) { + if (!trend) { + return trend_same + '?' + trend_end; + } + switch (trend.direction) { + case '+': + return (trend.reversed ? trend_up_bad : trend_up_good) + trend.text + trend_end; + case '-': + return (trend.reversed ? trend_down_good : trend_down_bad) + trend.text + trend_end; + default: + return trend_same + trend.text + trend_end; + } +} + +function smallTrend(trend) { + if (!trend) { + return ' '; + } + switch (trend.direction) { + case '+': + trend_color = trend.reversed ? 'text-danger' : 'text-success'; + return ' '; + case '-': + trend_color = trend.reversed ? 'text-success' : 'text-danger'; + return ' '; + default: + return ' '; + } +} + +function displayError(element, error) { + element.find('.d-sm-flex').after('') +} + +/* This function loads Network Overview tab */ +function loadNetworkOverviewValues(json, error) { + tab = $('#network-overview'); + if (error) { + displayError(tab, error); + return; + } + + // Last 7 days + data = json.players; + element = $(tab).find('#data_players'); + + $(element).find('#data_unique_players_1d').text(data.unique_players_1d); + $(element).find('#data_unique_players_7d').text(data.unique_players_7d); + $(element).find('#data_unique_players_30d').text(data.unique_players_30d); + $(element).find('#data_new_players_1d').text(data.new_players_1d); + $(element).find('#data_new_players_7d').text(data.new_players_7d); + $(element).find('#data_new_players_30d').text(data.new_players_30d); + + // Server As Numbers + data = json.numbers; + element = $(tab).find('#data_numbers'); + + $(element).find('#data_total').text(data.total_players); + $(element).find('#data_regular').text(data.regular_players); + $(element).find('#data_online').text(data.online_players); + + $(element).find('#data_last_peak_date').text(data.last_peak_date); + $(element).find('#data_last_peak_players').text(data.last_peak_players); + $(element).find('#data_best_peak_date').text(data.best_peak_date); + $(element).find('#data_best_peak_players').text(data.best_peak_players); + + $(element).find('#data_playtime').text(data.playtime); + $(element).find('#data_player_playtime').text(data.player_playtime); + $(element).find('#data_session_length_avg').text(data.session_length_avg); + $(element).find('#data_sessions').text(data.sessions); + + // Week Comparison + data = json.weeks; + element = $(tab).find('#data_weeks'); + + $(element).find('#data_start').text(data.start); + $(element).find('#data_midpoint').text(data.midpoint); + $(element).find('#data_midpoint2').text(data.midpoint); + $(element).find('#data_end').text(data.end); + + $(element).find('#data_unique_before').text(data.unique_before); + $(element).find('#data_unique_after').text(data.unique_after); + $(element).find('#data_unique_trend').replaceWith(trend(data.unique_trend)); + $(element).find('#data_new_before').text(data.new_before); + $(element).find('#data_new_after').text(data.new_after); + $(element).find('#data_new_trend').replaceWith(trend(data.new_trend)); + $(element).find('#data_regular_before').text(data.regular_before); + $(element).find('#data_regular_after').text(data.regular_after); + $(element).find('#data_regular_trend').replaceWith(trend(data.regular_trend)); + + $(element).find('#data_average_playtime_before').text(data.average_playtime_before); + $(element).find('#data_average_playtime_after').text(data.average_playtime_after); + $(element).find('#data_average_playtime_trend').replaceWith(trend(data.average_playtime_trend)); + $(element).find('#data_sessions_before').text(data.sessions_before); + $(element).find('#data_sessions_after').text(data.sessions_after); + $(element).find('#data_sessions_trend').replaceWith(trend(data.sessions_trend)); + $(element).find('#data_session_length_average_before').text(data.session_length_average_before); + $(element).find('#data_session_length_average_after').text(data.session_length_average_after); + $(element).find('#data_session_length_average_trend').replaceWith(trend(data.session_length_average_trend)); + +} + +/* This function loads Online Activity Overview tab */ +function loadOnlineActivityOverviewValues(json, error) { + tab = $('#online-activity-overview'); + if (error) { + displayError(tab, error); + return; + } + + // Online Activity as Numbers + data = json.numbers; + element = $(tab).find('#data_numbers'); + + $(element).find('#data_unique_players_30d').replaceWith('
    ' + data.unique_players_30d + smallTrend(data.unique_players_30d_trend) + '' + data.unique_players_30d_avg + smallTrend(data.unique_players_30d_avg_trend) + '' + data.new_players_30d + smallTrend(data.new_players_30d_trend) + '' + data.new_players_30d_avg + smallTrend(data.new_players_30d_avg_trend) + '(' + data.new_players_retention_24h + '/' + data.new_players_24h + ') ' + data.new_players_retention_24h_perc + ' ' + data.playtime_30d + smallTrend(data.playtime_30d_trend) + '' + data.playtime_30d_avg + smallTrend(data.playtime_30d_avg_trend) + '' + data.session_length_30d_avg + smallTrend(data.session_length_30d_trend) + '' + data.sessions_30d + smallTrend(data.sessions_30d_trend) + '
    Error: ' + error + '---
    No Data---
    ' + entry.country + + '' + entry.avg_ping + + '' + entry.min_ping + + '' + entry.max_ping + + '
    No Nicknames--
    ' + nickname.nickname + '' + nickname.server + '' + nickname.date + '
    No Data-
    ' + connection.geolocation + '' + connection.date + '
    No Data---
    ' + + '
    ' + + '
    ' + + (server.operator ? '

    Operator

    ' : '') + + (server.banned ? '

    Banned

    ' : '') + + (server.operator || server.banned ? '
    ' : '') + + '

    Sessions' + server.session_count + '

    ' + + '

    Playtime' + server.playtime + '

    ' + + '

    AFK Time' + server.afk_time + '

    ' + + '

    Longest Session' + server.longest_session_length + '

    ' + + '

    Session Median' + server.session_median + '

    ' + + '
    ' + + '

    Player Kills' + server.player_kills + '

    ' + + '

    Mob Kills' + server.mob_kills + '

    ' + + '

    Deaths' + server.deaths + '

    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + data.unique_players_30d + smallTrend(data.unique_players_30d_trend) + '' + data.unique_players_30d_avg + smallTrend(data.unique_players_30d_avg_trend) + '' + data.new_players_30d + smallTrend(data.new_players_30d_trend) + '' + data.new_players_30d_avg + smallTrend(data.new_players_30d_avg_trend) + '(' + data.new_players_retention_24h + '/' + data.new_players_24h + ') ' + data.new_players_retention_24h_perc + ' ' + data.playtime_30d + smallTrend(data.playtime_30d_trend) + '' + data.playtime_30d_avg + smallTrend(data.playtime_30d_avg_trend) + '' + data.session_length_30d_avg + smallTrend(data.session_length_30d_trend) + '' + data.sessions_30d + smallTrend(data.sessions_30d_trend) + '
    Error: ' + error + '---
    No Data---
    ' + + '
    ' + + '
    ' + + '

    Ended' + session.end + '

    ' + + '

    Length' + session.length + '

    ' + + '

    AFK Time' + session.afk_time + '

    ' + + '

    Server' + session.server_name + '


    ' + + '

    Player Kills' + session.player_kills.length + '

    ' + + '

    Mob Kills' + session.mob_kills + '

    ' + + '

    Deaths' + session.deaths + '


    ' + + createKillsTable(session.player_kills) + + '
    ' + + '
    ' + + ' Player Page' + + (session.network_server ? ' Server Analysis' : '') + + '
    ' + + '
    '; + + if (player_kills.length === 0) { + table += '' + } + + for (var i = 0; i < player_kills.length; i++) { + var kill = player_kills[i]; + table += '' + + '' + + '' + } + + table += '
    None--
    ' + kill.date + '' + kill.killer + + (kill.killer === kill.victim ? '' : '') + + kill.victim + '' + kill.weapon + '
    '; + return table; +} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/js/xmlhttprequests.js b/Plan/common/src/main/resources/assets/plan/web/js/xmlhttprequests.js new file mode 100644 index 000000000..12537a730 --- /dev/null +++ b/Plan/common/src/main/resources/assets/plan/web/js/xmlhttprequests.js @@ -0,0 +1,36 @@ +/** + * Make an XMLHttpRequest for JSON data. + * @param address Address to request from + * @param callback function with (json, error) parameters to call after the request. + */ +function jsonRequest(address, callback) { + setTimeout(function () { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function () { + if (this.readyState === 4) { + try { + if (this.status === 200 || (this.status === 0 && this.responseText)) { + var json = JSON.parse(this.responseText); + setTimeout(function () { + callback(json, null) + }, 0); + } else if (this.status === 404 || this.status === 403 || this.status === 500) { + callback(null, "HTTP " + this.status + " (See " + address + ")") + } else if (this.status === 400) { + callback(null, this.responseText + " (See " + address + ")") + } else if (this.status === 0) { + callback(null, "Request was blocked. (Adblocker maybe?)") + } + } catch (e) { + callback(null, e.message + " (See " + address + ")") + } + } + }; + xhttp.timeout = 20000; + xhttp.ontimeout = function () { + callback(null, "Timed out after 20 seconds. (" + address + ")") + }; + xhttp.open("GET", address, true); + xhttp.send(); + }, 0); +} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/network.html b/Plan/common/src/main/resources/assets/plan/web/network.html index b505ffa37..9fa1eff74 100644 --- a/Plan/common/src/main/resources/assets/plan/web/network.html +++ b/Plan/common/src/main/resources/assets/plan/web/network.html @@ -1,656 +1,984 @@ - - + + - - - + + + + + + + Plan | Network - - - - - + + + - - - - - - - - - - + + - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Please wait...

    -
    + + +
    + +

    Please wait..

    - - -
    - - - - - - - -
    - - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Network Online Activity

    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    NETWORK INFORMATION
    -
      -
    • - Total Players - ${playersTotal} -
    • -
    • - Players Online - ${playersOnline} -
    • -
    • -
    • Last Peak: ${lastPeakTime}${playersLastPeak} Players
    • -
    • All Time Peak: ${bestPeakTime}${playersBestPeak} Players
    • -
    -
    -
    -
    -
    -
    PLAYERS
    -
      -
    • - Unique | 24h - ${playersDay} -
    • -
    • - New | 24h - ${playersNewDay} -
    • -
    • -
    • - Unique | 7d - ${playersWeek} -
    • -
    • - New | 7d - ${playersNewWeek} -
    • -
    • -
    • - Unique | 30d - ${playersMonth} -
    • -
    • - New | 30d - ${playersNewMonth} -
    • -
    -
    -
    -
    - -
    - -
    - ${tabContentServers} -
    - -
    -
    - -
    -
    -
    -
    -
    -

    Health Estimate

    -
    - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    -

    Last 30 Days

    -
    - -
    -
    -
    - ${healthNotes} -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -

    Playerbase Development

    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Current Playerbase

    -
    - -
    -
    -
    -
    -
    -
    -
    + +
    + + + + + + +
    + + + +
    + +
    +
    + +
    +

    ${networkDisplayName} + · Network Overview

    +
    + +
    + +
    +
    + +
    +
    +
    +
    -
    - help_outline +
    +
    -
    -
    +
    + + + +
    +
    +
    +
    Players
    +
    +
    +

    Last 24 hours

    +

    Unique Players

    +

    New Players

    +

    Last 7 days

    +

    Unique Players

    +

    New Players

    +

    Last 30 days

    +

    Unique Players

    +

    New Players

    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + Network as Numbers
    +
    +
    +

    Total Players

    +

    Regular Players

    +

    Players Online

    +
    +

    Last Peak: + Players

    +

    Best Peak: + Players

    +
    +

    Total Playtime

    +

    Average Playtime / Player

    +

    Average Session Length

    +

    Sessions

    +
    +
    +
    + + +
    +
    +
    +
    + Week Comparison
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Comparing 7 days + - - Trend
    Unique Players
    New Players
    Regular Players
    Average Playtime / + Player +
    Average Session Length
    Sessions
    +
    +
    +
    +
    +
    + +
    +
    + +
    +

    ${networkDisplayName} + · Servers

    +
    + +
    +
    +
    +
    +
    +
    +
    +
      + Online Activity (30 days)
    +
    +
    +
    + +
    +
    +
      + as Numbers
    +
    +
    +

    Last Peak: Players +

    +

    Best Peak: Players +

    +
    +

    Last 7 days

    +

    Unique Players

    +

    New Players

    +

    +

    Average TPS

    +

    Low TPS Spikes

    +

    Downtime

    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +

    ${networkDisplayName} + · Sessions

    +
    + +
    + +
    +
    +
    +
    + Recent Sessions + Click to expand
    +
    + + + + + + + + + + + +
    Player Session Started Length Server
    +
    +
    +
    + +
    +
    +
    + Server Playtime for 30 days
    +
    +
    +
    + +
    +
    +
    + Insights for 30 days
    +
    +
    +

    Playtime

    +

    AFK Time ()

    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +

    ${networkDisplayName} + · Playerbase Overview

    +
    + +
    + +
    +
    +
    +
    + Playerbase development
    +
    +
    +
    +
    + + +
    +
    +
    +
    + Current Playerbase
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + Trends for 30 days
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    +
    +
    + Insights for 30 days
    +
    +
    +

    New Regular

    +

    Regular Inactive

    +

    + Comparing 30d ago to Now +

    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +

    ${networkDisplayName} + · Geolocations

    +
    + +
    + +
    +
    +
    +
    + Geolocations
    +
    +
    -
    +
    -
    +
    -
    - -
    - +
    + +
    +
    +
    +
    + Connection Information
    +
    +
    + + + + + + + + + + +
    Country Average Ping Best Ping Worst Ping
    +
    +
    +
    +
    +
    +
    + ${tabsPlugins} +
    +
    +
    +
    + + + + -
    - - + + - - + + - - +
    + - - - - - - - + + + + - - + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + \ No newline at end of file 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 49cb5369b..ead72ecbe 100644 --- a/Plan/common/src/main/resources/assets/plan/web/player.html +++ b/Plan/common/src/main/resources/assets/plan/web/player.html @@ -1,932 +1,814 @@ - - + + - - - + + + + + + + Plan | ${playerName} - - - - - + + + - - + + + - - - - - - - - - - - - - - - - - - - - - - + - -
    -
    -
    -
    -
    -
    -
    -
    -
    +
    + + Please wait.. +
    + + +
    + + +
    -

    Please wait...

    -
    -
    - - -
    - - - - - - - -
    - - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    ${playerName}

    -
    - +
    + +
    +
    +
    +
    + ${playerName}
    -
    -
    -
    +
    - ${playerStatus} -

    Times Kicked: ${kickCount} -

    +

    +

    +

    Times Kicked:

    - +
    -

    Player Kills: - ${playerKillCount} -

    -

    Mob Kills: - ${mobKillCount}

    -

    Deaths: ${deathCount}

    +

    Player Kills

    +

    Mob Kills

    +

    Deaths

    -
    -
    +
    -

    Sessions ${sessionCount}

    Total Playtime ${playtimeTotal}

    + class="float-right" id="data_playtime">

    Total Active ${activeTotal}

    + class="float-right" id="data_active_playtime">

    Total AFK ${afkTotal}

    + class="float-right" id="data_afk_time">

    +
    +

    Sessions

    Longest Session ${sessionLengthLongest} -

    -

    Session Median - ${sessionLengthMedian} + class="float-right" id="data_longest_session_length">

    +

    Session Median

    +
    +

    + Registered

    -

    Activity Index ${activityIndexNumber}

    -

     ${activityIndex} -

    -

      -

    -

    - Favorite Server - ${favoriteServer} -

    -

      -

    -

    - Average Ping - ${avgPing} -

    -

    - Best Ping - ${minPing} -

    -

    - Worst Ping - ${maxPing} -

    +

    Activity Index ()

    +

    Favorite Server

    +

     

    +
    +

    Average Ping

    +

    Best Ping

    +

    Worst Ping

    +
    +

    Last Seen

    +
    +
    +
    + Seen Nicknames
    +
    +
    + + + + + + + + + +
    Nickname Server Last Seen
    +
    +
    +
    +
    +
    + Connection information
    +
    +
    + + + + + + + + +
    Country Last Connected
    +
    +
    -
    - -
    -
    -
    - person_add -
    -
    -
    REGISTERED
    -
    ${registered}
    -
    -
    -
    -
    -
    -
    - event -
    -
    -
    LAST SEEN
    -
    ${lastSeen}
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Punchcard

    -
    - + +
    +
    +
    +
    + Punchcard
    +
    +
    -
    -
    -
    -
    -
    -
    - -
    - - -
    -
    -
    -
    -
    -

    Seen Nicknames

    -
    +
    +
    +
    + Online Activity
    -
    -
    - - - - - - - +
    Nickname Server Last Seen
    + + + + - ${tableBodyNicknames} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Last 30 daysLast 7 days
    Playtime
    Active Playtime
    AFK Time
    Average Session Length +
    Sessions
    Player Kills
    Mob Kills
    Deaths
    - - -
    -
    -
    -
    -
    -

    Connection Information

    -
    - +
    +
    + +
    +
    + +
    +

    ${playerName} + · Sessions

    + ${backButton} +
    + +
    +
    + +
    +
    +
    + Session Calendar
    +
    +
    +
    + + +
    +
    +
    + Recent Sessions + Click to expand
    +
    +
    + + + + + + + + + + +
    Server Session Started Length Most played World
    -
    - - + +
    + +
    +
    +
    + World Playtime
    +
    +
    +
    +
    + + + + +
    +
    + +
    +

    ${playerName} + · PvP & PvE

    + ${backButton} +
    + +
    + +
    +
    +
    +
    + PvP & PvE as Numbers
    +
    +
    + - - - + + + + - ${tableBodyIPs} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    IP-address Geolocation Last ConnectedAll TimeLast 30 daysLast 7 days
    KDR
    Player Kills
    Player Caused Deaths
    Mob KDR
    Mob Kills
    Mob Caused Deaths
    Deaths
    -
    -
    - - -
    -
    - -
    -
    - -
    -
    -
    -
    -
    -

    Session Calendar

    -
    - +
    + +
    +
    +
    +
    + Insights
    +
    +
    +

    Deadliest PvP Weapon +

    +

    2nd PvP Weapon +

    +

    3rd PvP Weapon +

    -
    -
    +
    +
    + +
    + +
    +
    +
    +
    + Recent PvP Kills
    +
    +
    -
    -
    -
    -
    -

    Most Recent Sessions

    -
    - + +
    +
    +
    +
    + Recent PvP Deaths
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +

    ${playerName} + · Servers Overview

    + ${backButton} +
    + +
    +
    + +
    +
    +
    + Servers + Click to expand
    +
    +
    + + + + + + + + + + +
    Server Playtime Registered Last Seen
    - ${accordionSessions} -
    -
    - -
    - -
    -
    -
    -
    -

    World Playtime

    -
    - +
    +
    + +
    +
    +
    + Server Playtime
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -

    Server Preference

    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    - -
    -
    - - -
    -
    -
    -
    LAST 30 DAYS
    -
      -
    • Sessions ${sessionCountMonth}
    • -
    • Total Playtime ${playtimeMonth}
    • -
    • AFK ${afkMonth}
    • -
    • Longest Session ${sessionLongestMonth} -
    • -
    • Session Median ${sessionMedianMonth} -
    • -
    +
    - - -
    -
    -
    -
    LAST 7 DAYS
    -
      -
    • Sessions ${sessionCountWeek}
    • -
    • Total Playtime ${playtimeWeek}
    • -
    • AFK ${afkWeek}
    • -
    • Longest Session ${sessionLongestWeek} -
    • -
    • Session Median ${sessionMedianWeek} -
    • -
    -
    -
    -
    - - -
    -
    -
    -
    LAST 24 HOURS
    -
      -
    • Sessions ${sessionCountDay}
    • -
    • Total Playtime ${playtimeDay}
    • -
    • AFK ${afkDay}
    • -
    • Longest Session ${sessionLongestDay} -
    • -
    • Session Median ${sessionMedianDay} -
    • -
    -
    -
    -
    - - -
    -
    - -
    -
    -
    -
    -
    -

    Servers

    -
    - -
    -
    - ${accordionServers} -
    -
    - -
    -
    - -
    -
    - - -
    -
    -
    -
    OVERVIEW
    -
      -
    • KDR ${KDR}
    • -
    • Player Kills ${playerKillCount}
    • -
    • Player caused Deaths ${playerDeathCount}
    • -
    • -
    • Mob KDR ${mobKDR}
    • -
    • Mob Kills ${mobKillCount}
    • -
    • Mob caused Deaths ${mobDeathCount}
    • -
    • -
    • Deaths ${deathCount}
    • -
    -
    -
    -
    - - -
    -
    -
    -
    LAST 30 DAYS
    -
      -
    • KDR ${KDRMonth}
    • -
    • Player Kills ${playerKillCountMonth}
    • -
    • Player caused Deaths ${playerDeathCountMonth}
    • -
    • -
    • Mob KDR ${mobKDRMonth}
    • -
    • Mob Kills ${mobKillCountMonth}
    • -
    • Mob caused Deaths ${mobDeathCountMonth}
    • -
    • -
    • Deaths ${deathCountMonth}
    • -
    -
    -
    -
    - - -
    -
    -
    -
    LAST 7 DAYS
    -
      -
    • KDR ${KDRWeek}
    • -
    • Player Kills ${playerKillCountWeek}
    • -
    • Player caused Deaths ${playerDeathCountWeek}
    • -
    • -
    • Mob KDR ${mobKDRWeek}
    • -
    • Mob Kills ${mobKillCountWeek}
    • -
    • Mob caused Deaths ${mobDeathCountWeek}
    • -
    • -
    • Deaths ${deathCountWeek}
    • -
    -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    -
    -

    Player Kills

    -
    -
    -
    -
    - ${tablePlayerKills} -
    -
    -
    - - -
    -
    -
    -
    -
    -

    Player caused Deaths

    -
    - -
    -
    -
    - ${tablePlayerDeaths} -
    -
    -
    - - -
    -
    - +
    +
    ${pluginsTabs} -
    +
    +
    +
    + + + + -
    - - + + - - + + - - +
    + - - - + + + + - - - - - - + + + - - - - + + - - + + + + + + + + + + - - - - - + + + + + + + + + - - - - - - - + \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/players.html b/Plan/common/src/main/resources/assets/plan/web/players.html index a6d631e45..06db87740 100644 --- a/Plan/common/src/main/resources/assets/plan/web/players.html +++ b/Plan/common/src/main/resources/assets/plan/web/players.html @@ -1,351 +1,305 @@  - + - - - + + + + + + + Plan | Players - - - - - + + + - - - - - - - - - - + + - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -

    Please wait...

    -
    -
    - - -
    - - - - - - - -
    - - - - - - -
    + -
    -
    -
    -
    - -
    -
    -
    -
    -
    -

    Player List

    + +
    + + + + + + +
    + + + +
    + +
    +
    + +
    +

    ${networkName} | Players

    +
    + +
    + +
    +
    +
    +
    + Player List
    - -
    -
    - ${playersTable} + + + + +
    Loading..
    +
    +
    +
    +
    + + + + -
    - - + + - - + + - - +
    + - - + + + + - - - + + + + + - - + + + + + + + + - - - - + \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/animate-css/animate.css b/Plan/common/src/main/resources/assets/plan/web/plugins/animate-css/animate.css deleted file mode 100644 index 188e78f66..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/animate-css/animate.css +++ /dev/null @@ -1,3340 +0,0 @@ -@charset "UTF-8"; - -/*! - * animate.css -http://daneden.me/animate - * Version - 3.5.0 - * Licensed under the MIT license - http://opensource.org/licenses/MIT - * - * Copyright (c) 2016 Daniel Eden - */ - -.animated { - -webkit-animation-duration: 1s; - animation-duration: 1s; - -webkit-animation-fill-mode: both; - animation-fill-mode: both; -} - -.animated.infinite { - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; -} - -.animated.hinge { - -webkit-animation-duration: 2s; - animation-duration: 2s; -} - -.animated.flipOutX, -.animated.flipOutY, -.animated.bounceIn, -.animated.bounceOut { - -webkit-animation-duration: .75s; - animation-duration: .75s; -} - -@-webkit-keyframes bounce { - from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } -} - -@keyframes bounce { - from, 20%, 53%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - -webkit-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); - } - - 40%, 43% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -30px, 0); - transform: translate3d(0, -30px, 0); - } - - 70% { - -webkit-animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - animation-timing-function: cubic-bezier(0.755, 0.050, 0.855, 0.060); - -webkit-transform: translate3d(0, -15px, 0); - transform: translate3d(0, -15px, 0); - } - - 90% { - -webkit-transform: translate3d(0,-4px,0); - transform: translate3d(0,-4px,0); - } -} - -.bounce { - -webkit-animation-name: bounce; - animation-name: bounce; - -webkit-transform-origin: center bottom; - transform-origin: center bottom; -} - -@-webkit-keyframes flash { - from, 50%, to { - opacity: 1; - } - - 25%, 75% { - opacity: 0; - } -} - -@keyframes flash { - from, 50%, to { - opacity: 1; - } - - 25%, 75% { - opacity: 0; - } -} - -.flash { - -webkit-animation-name: flash; - animation-name: flash; -} - -/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ - -@-webkit-keyframes pulse { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 50% { - -webkit-transform: scale3d(1.05, 1.05, 1.05); - transform: scale3d(1.05, 1.05, 1.05); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes pulse { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 50% { - -webkit-transform: scale3d(1.05, 1.05, 1.05); - transform: scale3d(1.05, 1.05, 1.05); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -.pulse { - -webkit-animation-name: pulse; - animation-name: pulse; -} - -@-webkit-keyframes rubberBand { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 30% { - -webkit-transform: scale3d(1.25, 0.75, 1); - transform: scale3d(1.25, 0.75, 1); - } - - 40% { - -webkit-transform: scale3d(0.75, 1.25, 1); - transform: scale3d(0.75, 1.25, 1); - } - - 50% { - -webkit-transform: scale3d(1.15, 0.85, 1); - transform: scale3d(1.15, 0.85, 1); - } - - 65% { - -webkit-transform: scale3d(.95, 1.05, 1); - transform: scale3d(.95, 1.05, 1); - } - - 75% { - -webkit-transform: scale3d(1.05, .95, 1); - transform: scale3d(1.05, .95, 1); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes rubberBand { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 30% { - -webkit-transform: scale3d(1.25, 0.75, 1); - transform: scale3d(1.25, 0.75, 1); - } - - 40% { - -webkit-transform: scale3d(0.75, 1.25, 1); - transform: scale3d(0.75, 1.25, 1); - } - - 50% { - -webkit-transform: scale3d(1.15, 0.85, 1); - transform: scale3d(1.15, 0.85, 1); - } - - 65% { - -webkit-transform: scale3d(.95, 1.05, 1); - transform: scale3d(.95, 1.05, 1); - } - - 75% { - -webkit-transform: scale3d(1.05, .95, 1); - transform: scale3d(1.05, .95, 1); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -.rubberBand { - -webkit-animation-name: rubberBand; - animation-name: rubberBand; -} - -@-webkit-keyframes shake { - from, to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 10%, 30%, 50%, 70%, 90% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 20%, 40%, 60%, 80% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } -} - -@keyframes shake { - from, to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - 10%, 30%, 50%, 70%, 90% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 20%, 40%, 60%, 80% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } -} - -.shake { - -webkit-animation-name: shake; - animation-name: shake; -} - -@-webkit-keyframes headShake { - 0% { - -webkit-transform: translateX(0); - transform: translateX(0); - } - - 6.5% { - -webkit-transform: translateX(-6px) rotateY(-9deg); - transform: translateX(-6px) rotateY(-9deg); - } - - 18.5% { - -webkit-transform: translateX(5px) rotateY(7deg); - transform: translateX(5px) rotateY(7deg); - } - - 31.5% { - -webkit-transform: translateX(-3px) rotateY(-5deg); - transform: translateX(-3px) rotateY(-5deg); - } - - 43.5% { - -webkit-transform: translateX(2px) rotateY(3deg); - transform: translateX(2px) rotateY(3deg); - } - - 50% { - -webkit-transform: translateX(0); - transform: translateX(0); - } -} - -@keyframes headShake { - 0% { - -webkit-transform: translateX(0); - transform: translateX(0); - } - - 6.5% { - -webkit-transform: translateX(-6px) rotateY(-9deg); - transform: translateX(-6px) rotateY(-9deg); - } - - 18.5% { - -webkit-transform: translateX(5px) rotateY(7deg); - transform: translateX(5px) rotateY(7deg); - } - - 31.5% { - -webkit-transform: translateX(-3px) rotateY(-5deg); - transform: translateX(-3px) rotateY(-5deg); - } - - 43.5% { - -webkit-transform: translateX(2px) rotateY(3deg); - transform: translateX(2px) rotateY(3deg); - } - - 50% { - -webkit-transform: translateX(0); - transform: translateX(0); - } -} - -.headShake { - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - -webkit-animation-name: headShake; - animation-name: headShake; -} - -@-webkit-keyframes swing { - 20% { - -webkit-transform: rotate3d(0, 0, 1, 15deg); - transform: rotate3d(0, 0, 1, 15deg); - } - - 40% { - -webkit-transform: rotate3d(0, 0, 1, -10deg); - transform: rotate3d(0, 0, 1, -10deg); - } - - 60% { - -webkit-transform: rotate3d(0, 0, 1, 5deg); - transform: rotate3d(0, 0, 1, 5deg); - } - - 80% { - -webkit-transform: rotate3d(0, 0, 1, -5deg); - transform: rotate3d(0, 0, 1, -5deg); - } - - to { - -webkit-transform: rotate3d(0, 0, 1, 0deg); - transform: rotate3d(0, 0, 1, 0deg); - } -} - -@keyframes swing { - 20% { - -webkit-transform: rotate3d(0, 0, 1, 15deg); - transform: rotate3d(0, 0, 1, 15deg); - } - - 40% { - -webkit-transform: rotate3d(0, 0, 1, -10deg); - transform: rotate3d(0, 0, 1, -10deg); - } - - 60% { - -webkit-transform: rotate3d(0, 0, 1, 5deg); - transform: rotate3d(0, 0, 1, 5deg); - } - - 80% { - -webkit-transform: rotate3d(0, 0, 1, -5deg); - transform: rotate3d(0, 0, 1, -5deg); - } - - to { - -webkit-transform: rotate3d(0, 0, 1, 0deg); - transform: rotate3d(0, 0, 1, 0deg); - } -} - -.swing { - -webkit-transform-origin: top center; - transform-origin: top center; - -webkit-animation-name: swing; - animation-name: swing; -} - -@-webkit-keyframes tada { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 10%, 20% { - -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); - transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); - } - - 30%, 50%, 70%, 90% { - -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - } - - 40%, 60%, 80% { - -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes tada { - from { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } - - 10%, 20% { - -webkit-transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); - transform: scale3d(.9, .9, .9) rotate3d(0, 0, 1, -3deg); - } - - 30%, 50%, 70%, 90% { - -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); - } - - 40%, 60%, 80% { - -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); - } - - to { - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -.tada { - -webkit-animation-name: tada; - animation-name: tada; -} - -/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ - -@-webkit-keyframes wobble { - from { - -webkit-transform: none; - transform: none; - } - - 15% { - -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - } - - 30% { - -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - } - - 45% { - -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - } - - 60% { - -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - } - - 75% { - -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -@keyframes wobble { - from { - -webkit-transform: none; - transform: none; - } - - 15% { - -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg); - } - - 30% { - -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg); - } - - 45% { - -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg); - } - - 60% { - -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg); - } - - 75% { - -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -.wobble { - -webkit-animation-name: wobble; - animation-name: wobble; -} - -@-webkit-keyframes jello { - from, 11.1%, to { - -webkit-transform: none; - transform: none; - } - - 22.2% { - -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); - transform: skewX(-12.5deg) skewY(-12.5deg); - } - - 33.3% { - -webkit-transform: skewX(6.25deg) skewY(6.25deg); - transform: skewX(6.25deg) skewY(6.25deg); - } - - 44.4% { - -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); - transform: skewX(-3.125deg) skewY(-3.125deg); - } - - 55.5% { - -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); - transform: skewX(1.5625deg) skewY(1.5625deg); - } - - 66.6% { - -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); - transform: skewX(-0.78125deg) skewY(-0.78125deg); - } - - 77.7% { - -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); - transform: skewX(0.390625deg) skewY(0.390625deg); - } - - 88.8% { - -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - } -} - -@keyframes jello { - from, 11.1%, to { - -webkit-transform: none; - transform: none; - } - - 22.2% { - -webkit-transform: skewX(-12.5deg) skewY(-12.5deg); - transform: skewX(-12.5deg) skewY(-12.5deg); - } - - 33.3% { - -webkit-transform: skewX(6.25deg) skewY(6.25deg); - transform: skewX(6.25deg) skewY(6.25deg); - } - - 44.4% { - -webkit-transform: skewX(-3.125deg) skewY(-3.125deg); - transform: skewX(-3.125deg) skewY(-3.125deg); - } - - 55.5% { - -webkit-transform: skewX(1.5625deg) skewY(1.5625deg); - transform: skewX(1.5625deg) skewY(1.5625deg); - } - - 66.6% { - -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg); - transform: skewX(-0.78125deg) skewY(-0.78125deg); - } - - 77.7% { - -webkit-transform: skewX(0.390625deg) skewY(0.390625deg); - transform: skewX(0.390625deg) skewY(0.390625deg); - } - - 88.8% { - -webkit-transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - transform: skewX(-0.1953125deg) skewY(-0.1953125deg); - } -} - -.jello { - -webkit-animation-name: jello; - animation-name: jello; - -webkit-transform-origin: center; - transform-origin: center; -} - -@-webkit-keyframes bounceIn { - from, 20%, 40%, 60%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - 20% { - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - 40% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(1.03, 1.03, 1.03); - transform: scale3d(1.03, 1.03, 1.03); - } - - 80% { - -webkit-transform: scale3d(.97, .97, .97); - transform: scale3d(.97, .97, .97); - } - - to { - opacity: 1; - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes bounceIn { - from, 20%, 40%, 60%, 80%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - 20% { - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - 40% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(1.03, 1.03, 1.03); - transform: scale3d(1.03, 1.03, 1.03); - } - - 80% { - -webkit-transform: scale3d(.97, .97, .97); - transform: scale3d(.97, .97, .97); - } - - to { - opacity: 1; - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -.bounceIn { - -webkit-animation-name: bounceIn; - animation-name: bounceIn; -} - -@-webkit-keyframes bounceInDown { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: translate3d(0, -3000px, 0); - transform: translate3d(0, -3000px, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(0, 25px, 0); - transform: translate3d(0, 25px, 0); - } - - 75% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 90% { - -webkit-transform: translate3d(0, 5px, 0); - transform: translate3d(0, 5px, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -@keyframes bounceInDown { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: translate3d(0, -3000px, 0); - transform: translate3d(0, -3000px, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(0, 25px, 0); - transform: translate3d(0, 25px, 0); - } - - 75% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 90% { - -webkit-transform: translate3d(0, 5px, 0); - transform: translate3d(0, 5px, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -.bounceInDown { - -webkit-animation-name: bounceInDown; - animation-name: bounceInDown; -} - -@-webkit-keyframes bounceInLeft { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: translate3d(-3000px, 0, 0); - transform: translate3d(-3000px, 0, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(25px, 0, 0); - transform: translate3d(25px, 0, 0); - } - - 75% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 90% { - -webkit-transform: translate3d(5px, 0, 0); - transform: translate3d(5px, 0, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -@keyframes bounceInLeft { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 0; - -webkit-transform: translate3d(-3000px, 0, 0); - transform: translate3d(-3000px, 0, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(25px, 0, 0); - transform: translate3d(25px, 0, 0); - } - - 75% { - -webkit-transform: translate3d(-10px, 0, 0); - transform: translate3d(-10px, 0, 0); - } - - 90% { - -webkit-transform: translate3d(5px, 0, 0); - transform: translate3d(5px, 0, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -.bounceInLeft { - -webkit-animation-name: bounceInLeft; - animation-name: bounceInLeft; -} - -@-webkit-keyframes bounceInRight { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - from { - opacity: 0; - -webkit-transform: translate3d(3000px, 0, 0); - transform: translate3d(3000px, 0, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(-25px, 0, 0); - transform: translate3d(-25px, 0, 0); - } - - 75% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } - - 90% { - -webkit-transform: translate3d(-5px, 0, 0); - transform: translate3d(-5px, 0, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -@keyframes bounceInRight { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - from { - opacity: 0; - -webkit-transform: translate3d(3000px, 0, 0); - transform: translate3d(3000px, 0, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(-25px, 0, 0); - transform: translate3d(-25px, 0, 0); - } - - 75% { - -webkit-transform: translate3d(10px, 0, 0); - transform: translate3d(10px, 0, 0); - } - - 90% { - -webkit-transform: translate3d(-5px, 0, 0); - transform: translate3d(-5px, 0, 0); - } - - to { - -webkit-transform: none; - transform: none; - } -} - -.bounceInRight { - -webkit-animation-name: bounceInRight; - animation-name: bounceInRight; -} - -@-webkit-keyframes bounceInUp { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - from { - opacity: 0; - -webkit-transform: translate3d(0, 3000px, 0); - transform: translate3d(0, 3000px, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(0, -20px, 0); - transform: translate3d(0, -20px, 0); - } - - 75% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } - - 90% { - -webkit-transform: translate3d(0, -5px, 0); - transform: translate3d(0, -5px, 0); - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes bounceInUp { - from, 60%, 75%, 90%, to { - -webkit-animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - from { - opacity: 0; - -webkit-transform: translate3d(0, 3000px, 0); - transform: translate3d(0, 3000px, 0); - } - - 60% { - opacity: 1; - -webkit-transform: translate3d(0, -20px, 0); - transform: translate3d(0, -20px, 0); - } - - 75% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } - - 90% { - -webkit-transform: translate3d(0, -5px, 0); - transform: translate3d(0, -5px, 0); - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.bounceInUp { - -webkit-animation-name: bounceInUp; - animation-name: bounceInUp; -} - -@-webkit-keyframes bounceOut { - 20% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 50%, 55% { - opacity: 1; - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } -} - -@keyframes bounceOut { - 20% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 50%, 55% { - opacity: 1; - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } -} - -.bounceOut { - -webkit-animation-name: bounceOut; - animation-name: bounceOut; -} - -@-webkit-keyframes bounceOutDown { - 20% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } - - 40%, 45% { - opacity: 1; - -webkit-transform: translate3d(0, -20px, 0); - transform: translate3d(0, -20px, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } -} - -@keyframes bounceOutDown { - 20% { - -webkit-transform: translate3d(0, 10px, 0); - transform: translate3d(0, 10px, 0); - } - - 40%, 45% { - opacity: 1; - -webkit-transform: translate3d(0, -20px, 0); - transform: translate3d(0, -20px, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } -} - -.bounceOutDown { - -webkit-animation-name: bounceOutDown; - animation-name: bounceOutDown; -} - -@-webkit-keyframes bounceOutLeft { - 20% { - opacity: 1; - -webkit-transform: translate3d(20px, 0, 0); - transform: translate3d(20px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } -} - -@keyframes bounceOutLeft { - 20% { - opacity: 1; - -webkit-transform: translate3d(20px, 0, 0); - transform: translate3d(20px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } -} - -.bounceOutLeft { - -webkit-animation-name: bounceOutLeft; - animation-name: bounceOutLeft; -} - -@-webkit-keyframes bounceOutRight { - 20% { - opacity: 1; - -webkit-transform: translate3d(-20px, 0, 0); - transform: translate3d(-20px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } -} - -@keyframes bounceOutRight { - 20% { - opacity: 1; - -webkit-transform: translate3d(-20px, 0, 0); - transform: translate3d(-20px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } -} - -.bounceOutRight { - -webkit-animation-name: bounceOutRight; - animation-name: bounceOutRight; -} - -@-webkit-keyframes bounceOutUp { - 20% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 40%, 45% { - opacity: 1; - -webkit-transform: translate3d(0, 20px, 0); - transform: translate3d(0, 20px, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } -} - -@keyframes bounceOutUp { - 20% { - -webkit-transform: translate3d(0, -10px, 0); - transform: translate3d(0, -10px, 0); - } - - 40%, 45% { - opacity: 1; - -webkit-transform: translate3d(0, 20px, 0); - transform: translate3d(0, 20px, 0); - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } -} - -.bounceOutUp { - -webkit-animation-name: bounceOutUp; - animation-name: bounceOutUp; -} - -@-webkit-keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -.fadeIn { - -webkit-animation-name: fadeIn; - animation-name: fadeIn; -} - -@-webkit-keyframes fadeInDown { - from { - opacity: 0; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInDown { - from { - opacity: 0; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInDown { - -webkit-animation-name: fadeInDown; - animation-name: fadeInDown; -} - -@-webkit-keyframes fadeInDownBig { - from { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInDownBig { - from { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInDownBig { - -webkit-animation-name: fadeInDownBig; - animation-name: fadeInDownBig; -} - -@-webkit-keyframes fadeInLeft { - from { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInLeft { - from { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInLeft { - -webkit-animation-name: fadeInLeft; - animation-name: fadeInLeft; -} - -@-webkit-keyframes fadeInLeftBig { - from { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInLeftBig { - from { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInLeftBig { - -webkit-animation-name: fadeInLeftBig; - animation-name: fadeInLeftBig; -} - -@-webkit-keyframes fadeInRight { - from { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInRight { - from { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInRight { - -webkit-animation-name: fadeInRight; - animation-name: fadeInRight; -} - -@-webkit-keyframes fadeInRightBig { - from { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInRightBig { - from { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInRightBig { - -webkit-animation-name: fadeInRightBig; - animation-name: fadeInRightBig; -} - -@-webkit-keyframes fadeInUp { - from { - opacity: 0; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInUp { - from { - opacity: 0; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInUp { - -webkit-animation-name: fadeInUp; - animation-name: fadeInUp; -} - -@-webkit-keyframes fadeInUpBig { - from { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes fadeInUpBig { - from { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.fadeInUpBig { - -webkit-animation-name: fadeInUpBig; - animation-name: fadeInUpBig; -} - -@-webkit-keyframes fadeOut { - from { - opacity: 1; - } - - to { - opacity: 0; - } -} - -@keyframes fadeOut { - from { - opacity: 1; - } - - to { - opacity: 0; - } -} - -.fadeOut { - -webkit-animation-name: fadeOut; - animation-name: fadeOut; -} - -@-webkit-keyframes fadeOutDown { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } -} - -@keyframes fadeOutDown { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } -} - -.fadeOutDown { - -webkit-animation-name: fadeOutDown; - animation-name: fadeOutDown; -} - -@-webkit-keyframes fadeOutDownBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } -} - -@keyframes fadeOutDownBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, 2000px, 0); - transform: translate3d(0, 2000px, 0); - } -} - -.fadeOutDownBig { - -webkit-animation-name: fadeOutDownBig; - animation-name: fadeOutDownBig; -} - -@-webkit-keyframes fadeOutLeft { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -@keyframes fadeOutLeft { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -.fadeOutLeft { - -webkit-animation-name: fadeOutLeft; - animation-name: fadeOutLeft; -} - -@-webkit-keyframes fadeOutLeftBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } -} - -@keyframes fadeOutLeftBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(-2000px, 0, 0); - transform: translate3d(-2000px, 0, 0); - } -} - -.fadeOutLeftBig { - -webkit-animation-name: fadeOutLeftBig; - animation-name: fadeOutLeftBig; -} - -@-webkit-keyframes fadeOutRight { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -@keyframes fadeOutRight { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -.fadeOutRight { - -webkit-animation-name: fadeOutRight; - animation-name: fadeOutRight; -} - -@-webkit-keyframes fadeOutRightBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } -} - -@keyframes fadeOutRightBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(2000px, 0, 0); - transform: translate3d(2000px, 0, 0); - } -} - -.fadeOutRightBig { - -webkit-animation-name: fadeOutRightBig; - animation-name: fadeOutRightBig; -} - -@-webkit-keyframes fadeOutUp { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } -} - -@keyframes fadeOutUp { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } -} - -.fadeOutUp { - -webkit-animation-name: fadeOutUp; - animation-name: fadeOutUp; -} - -@-webkit-keyframes fadeOutUpBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } -} - -@keyframes fadeOutUpBig { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(0, -2000px, 0); - transform: translate3d(0, -2000px, 0); - } -} - -.fadeOutUpBig { - -webkit-animation-name: fadeOutUpBig; - animation-name: fadeOutUpBig; -} - -@-webkit-keyframes flip { - from { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); - transform: perspective(400px) rotate3d(0, 1, 0, -360deg); - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; - } - - 40% { - -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); - transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; - } - - 50% { - -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); - transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 80% { - -webkit-transform: perspective(400px) scale3d(.95, .95, .95); - transform: perspective(400px) scale3d(.95, .95, .95); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } -} - -@keyframes flip { - from { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg); - transform: perspective(400px) rotate3d(0, 1, 0, -360deg); - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; - } - - 40% { - -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); - transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg); - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; - } - - 50% { - -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); - transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 80% { - -webkit-transform: perspective(400px) scale3d(.95, .95, .95); - transform: perspective(400px) scale3d(.95, .95, .95); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } -} - -.animated.flip { - -webkit-backface-visibility: visible; - backface-visibility: visible; - -webkit-animation-name: flip; - animation-name: flip; -} - -@-webkit-keyframes flipInX { - from { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - opacity: 0; - } - - 40% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 60% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - opacity: 1; - } - - 80% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } -} - -@keyframes flipInX { - from { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - opacity: 0; - } - - 40% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 60% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - transform: perspective(400px) rotate3d(1, 0, 0, 10deg); - opacity: 1; - } - - 80% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - transform: perspective(400px) rotate3d(1, 0, 0, -5deg); - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } -} - -.flipInX { - -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; - -webkit-animation-name: flipInX; - animation-name: flipInX; -} - -@-webkit-keyframes flipInY { - from { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - opacity: 0; - } - - 40% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 60% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - opacity: 1; - } - - 80% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } -} - -@keyframes flipInY { - from { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - opacity: 0; - } - - 40% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - transform: perspective(400px) rotate3d(0, 1, 0, -20deg); - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; - } - - 60% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - transform: perspective(400px) rotate3d(0, 1, 0, 10deg); - opacity: 1; - } - - 80% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - transform: perspective(400px) rotate3d(0, 1, 0, -5deg); - } - - to { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } -} - -.flipInY { - -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; - -webkit-animation-name: flipInY; - animation-name: flipInY; -} - -@-webkit-keyframes flipOutX { - from { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } - - 30% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - opacity: 1; - } - - to { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - opacity: 0; - } -} - -@keyframes flipOutX { - from { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } - - 30% { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - transform: perspective(400px) rotate3d(1, 0, 0, -20deg); - opacity: 1; - } - - to { - -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - transform: perspective(400px) rotate3d(1, 0, 0, 90deg); - opacity: 0; - } -} - -.flipOutX { - -webkit-animation-name: flipOutX; - animation-name: flipOutX; - -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; -} - -@-webkit-keyframes flipOutY { - from { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } - - 30% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); - transform: perspective(400px) rotate3d(0, 1, 0, -15deg); - opacity: 1; - } - - to { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - opacity: 0; - } -} - -@keyframes flipOutY { - from { - -webkit-transform: perspective(400px); - transform: perspective(400px); - } - - 30% { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg); - transform: perspective(400px) rotate3d(0, 1, 0, -15deg); - opacity: 1; - } - - to { - -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - transform: perspective(400px) rotate3d(0, 1, 0, 90deg); - opacity: 0; - } -} - -.flipOutY { - -webkit-backface-visibility: visible !important; - backface-visibility: visible !important; - -webkit-animation-name: flipOutY; - animation-name: flipOutY; -} - -@-webkit-keyframes lightSpeedIn { - from { - -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); - transform: translate3d(100%, 0, 0) skewX(-30deg); - opacity: 0; - } - - 60% { - -webkit-transform: skewX(20deg); - transform: skewX(20deg); - opacity: 1; - } - - 80% { - -webkit-transform: skewX(-5deg); - transform: skewX(-5deg); - opacity: 1; - } - - to { - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes lightSpeedIn { - from { - -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg); - transform: translate3d(100%, 0, 0) skewX(-30deg); - opacity: 0; - } - - 60% { - -webkit-transform: skewX(20deg); - transform: skewX(20deg); - opacity: 1; - } - - 80% { - -webkit-transform: skewX(-5deg); - transform: skewX(-5deg); - opacity: 1; - } - - to { - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.lightSpeedIn { - -webkit-animation-name: lightSpeedIn; - animation-name: lightSpeedIn; - -webkit-animation-timing-function: ease-out; - animation-timing-function: ease-out; -} - -@-webkit-keyframes lightSpeedOut { - from { - opacity: 1; - } - - to { - -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); - transform: translate3d(100%, 0, 0) skewX(30deg); - opacity: 0; - } -} - -@keyframes lightSpeedOut { - from { - opacity: 1; - } - - to { - -webkit-transform: translate3d(100%, 0, 0) skewX(30deg); - transform: translate3d(100%, 0, 0) skewX(30deg); - opacity: 0; - } -} - -.lightSpeedOut { - -webkit-animation-name: lightSpeedOut; - animation-name: lightSpeedOut; - -webkit-animation-timing-function: ease-in; - animation-timing-function: ease-in; -} - -@-webkit-keyframes rotateIn { - from { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: rotate3d(0, 0, 1, -200deg); - transform: rotate3d(0, 0, 1, -200deg); - opacity: 0; - } - - to { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateIn { - from { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: rotate3d(0, 0, 1, -200deg); - transform: rotate3d(0, 0, 1, -200deg); - opacity: 0; - } - - to { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateIn { - -webkit-animation-name: rotateIn; - animation-name: rotateIn; -} - -@-webkit-keyframes rotateInDownLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateInDownLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateInDownLeft { - -webkit-animation-name: rotateInDownLeft; - animation-name: rotateInDownLeft; -} - -@-webkit-keyframes rotateInDownRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateInDownRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateInDownRight { - -webkit-animation-name: rotateInDownRight; - animation-name: rotateInDownRight; -} - -@-webkit-keyframes rotateInUpLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateInUpLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateInUpLeft { - -webkit-animation-name: rotateInUpLeft; - animation-name: rotateInUpLeft; -} - -@-webkit-keyframes rotateInUpRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, -90deg); - transform: rotate3d(0, 0, 1, -90deg); - opacity: 0; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -@keyframes rotateInUpRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, -90deg); - transform: rotate3d(0, 0, 1, -90deg); - opacity: 0; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: none; - transform: none; - opacity: 1; - } -} - -.rotateInUpRight { - -webkit-animation-name: rotateInUpRight; - animation-name: rotateInUpRight; -} - -@-webkit-keyframes rotateOut { - from { - -webkit-transform-origin: center; - transform-origin: center; - opacity: 1; - } - - to { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: rotate3d(0, 0, 1, 200deg); - transform: rotate3d(0, 0, 1, 200deg); - opacity: 0; - } -} - -@keyframes rotateOut { - from { - -webkit-transform-origin: center; - transform-origin: center; - opacity: 1; - } - - to { - -webkit-transform-origin: center; - transform-origin: center; - -webkit-transform: rotate3d(0, 0, 1, 200deg); - transform: rotate3d(0, 0, 1, 200deg); - opacity: 0; - } -} - -.rotateOut { - -webkit-animation-name: rotateOut; - animation-name: rotateOut; -} - -@-webkit-keyframes rotateOutDownLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } -} - -@keyframes rotateOutDownLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, 45deg); - transform: rotate3d(0, 0, 1, 45deg); - opacity: 0; - } -} - -.rotateOutDownLeft { - -webkit-animation-name: rotateOutDownLeft; - animation-name: rotateOutDownLeft; -} - -@-webkit-keyframes rotateOutDownRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } -} - -@keyframes rotateOutDownRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } -} - -.rotateOutDownRight { - -webkit-animation-name: rotateOutDownRight; - animation-name: rotateOutDownRight; -} - -@-webkit-keyframes rotateOutUpLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } -} - -@keyframes rotateOutUpLeft { - from { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: left bottom; - transform-origin: left bottom; - -webkit-transform: rotate3d(0, 0, 1, -45deg); - transform: rotate3d(0, 0, 1, -45deg); - opacity: 0; - } -} - -.rotateOutUpLeft { - -webkit-animation-name: rotateOutUpLeft; - animation-name: rotateOutUpLeft; -} - -@-webkit-keyframes rotateOutUpRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, 90deg); - transform: rotate3d(0, 0, 1, 90deg); - opacity: 0; - } -} - -@keyframes rotateOutUpRight { - from { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - opacity: 1; - } - - to { - -webkit-transform-origin: right bottom; - transform-origin: right bottom; - -webkit-transform: rotate3d(0, 0, 1, 90deg); - transform: rotate3d(0, 0, 1, 90deg); - opacity: 0; - } -} - -.rotateOutUpRight { - -webkit-animation-name: rotateOutUpRight; - animation-name: rotateOutUpRight; -} - -@-webkit-keyframes hinge { - 0% { - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - } - - 20%, 60% { - -webkit-transform: rotate3d(0, 0, 1, 80deg); - transform: rotate3d(0, 0, 1, 80deg); - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - } - - 40%, 80% { - -webkit-transform: rotate3d(0, 0, 1, 60deg); - transform: rotate3d(0, 0, 1, 60deg); - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - opacity: 1; - } - - to { - -webkit-transform: translate3d(0, 700px, 0); - transform: translate3d(0, 700px, 0); - opacity: 0; - } -} - -@keyframes hinge { - 0% { - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - } - - 20%, 60% { - -webkit-transform: rotate3d(0, 0, 1, 80deg); - transform: rotate3d(0, 0, 1, 80deg); - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - } - - 40%, 80% { - -webkit-transform: rotate3d(0, 0, 1, 60deg); - transform: rotate3d(0, 0, 1, 60deg); - -webkit-transform-origin: top left; - transform-origin: top left; - -webkit-animation-timing-function: ease-in-out; - animation-timing-function: ease-in-out; - opacity: 1; - } - - to { - -webkit-transform: translate3d(0, 700px, 0); - transform: translate3d(0, 700px, 0); - opacity: 0; - } -} - -.hinge { - -webkit-animation-name: hinge; - animation-name: hinge; -} - -/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ - -@-webkit-keyframes rollIn { - from { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -@keyframes rollIn { - from { - opacity: 0; - -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg); - } - - to { - opacity: 1; - -webkit-transform: none; - transform: none; - } -} - -.rollIn { - -webkit-animation-name: rollIn; - animation-name: rollIn; -} - -/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */ - -@-webkit-keyframes rollOut { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - } -} - -@keyframes rollOut { - from { - opacity: 1; - } - - to { - opacity: 0; - -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg); - } -} - -.rollOut { - -webkit-animation-name: rollOut; - animation-name: rollOut; -} - -@-webkit-keyframes zoomIn { - from { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - 50% { - opacity: 1; - } -} - -@keyframes zoomIn { - from { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - 50% { - opacity: 1; - } -} - -.zoomIn { - -webkit-animation-name: zoomIn; - animation-name: zoomIn; -} - -@-webkit-keyframes zoomInDown { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomInDown { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, -1000px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomInDown { - -webkit-animation-name: zoomInDown; - animation-name: zoomInDown; -} - -@-webkit-keyframes zoomInLeft { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); - transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomInLeft { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); - transform: scale3d(.1, .1, .1) translate3d(-1000px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(10px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomInLeft { - -webkit-animation-name: zoomInLeft; - animation-name: zoomInLeft; -} - -@-webkit-keyframes zoomInRight { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); - transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomInRight { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); - transform: scale3d(.1, .1, .1) translate3d(1000px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(-10px, 0, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomInRight { - -webkit-animation-name: zoomInRight; - animation-name: zoomInRight; -} - -@-webkit-keyframes zoomInUp { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomInUp { - from { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, 1000px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomInUp { - -webkit-animation-name: zoomInUp; - animation-name: zoomInUp; -} - -@-webkit-keyframes zoomOut { - from { - opacity: 1; - } - - 50% { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - to { - opacity: 0; - } -} - -@keyframes zoomOut { - from { - opacity: 1; - } - - 50% { - opacity: 0; - -webkit-transform: scale3d(.3, .3, .3); - transform: scale3d(.3, .3, .3); - } - - to { - opacity: 0; - } -} - -.zoomOut { - -webkit-animation-name: zoomOut; - animation-name: zoomOut; -} - -@-webkit-keyframes zoomOutDown { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); - -webkit-transform-origin: center bottom; - transform-origin: center bottom; - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomOutDown { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, -60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, 2000px, 0); - -webkit-transform-origin: center bottom; - transform-origin: center bottom; - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomOutDown { - -webkit-animation-name: zoomOutDown; - animation-name: zoomOutDown; -} - -@-webkit-keyframes zoomOutLeft { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); - transform: scale(.1) translate3d(-2000px, 0, 0); - -webkit-transform-origin: left center; - transform-origin: left center; - } -} - -@keyframes zoomOutLeft { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(42px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: scale(.1) translate3d(-2000px, 0, 0); - transform: scale(.1) translate3d(-2000px, 0, 0); - -webkit-transform-origin: left center; - transform-origin: left center; - } -} - -.zoomOutLeft { - -webkit-animation-name: zoomOutLeft; - animation-name: zoomOutLeft; -} - -@-webkit-keyframes zoomOutRight { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: scale(.1) translate3d(2000px, 0, 0); - transform: scale(.1) translate3d(2000px, 0, 0); - -webkit-transform-origin: right center; - transform-origin: right center; - } -} - -@keyframes zoomOutRight { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); - transform: scale3d(.475, .475, .475) translate3d(-42px, 0, 0); - } - - to { - opacity: 0; - -webkit-transform: scale(.1) translate3d(2000px, 0, 0); - transform: scale(.1) translate3d(2000px, 0, 0); - -webkit-transform-origin: right center; - transform-origin: right center; - } -} - -.zoomOutRight { - -webkit-animation-name: zoomOutRight; - animation-name: zoomOutRight; -} - -@-webkit-keyframes zoomOutUp { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); - -webkit-transform-origin: center bottom; - transform-origin: center bottom; - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -@keyframes zoomOutUp { - 40% { - opacity: 1; - -webkit-transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - transform: scale3d(.475, .475, .475) translate3d(0, 60px, 0); - -webkit-animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - animation-timing-function: cubic-bezier(0.550, 0.055, 0.675, 0.190); - } - - to { - opacity: 0; - -webkit-transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); - transform: scale3d(.1, .1, .1) translate3d(0, -2000px, 0); - -webkit-transform-origin: center bottom; - transform-origin: center bottom; - -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - animation-timing-function: cubic-bezier(0.175, 0.885, 0.320, 1); - } -} - -.zoomOutUp { - -webkit-animation-name: zoomOutUp; - animation-name: zoomOutUp; -} - -@-webkit-keyframes slideInDown { - from { - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes slideInDown { - from { - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.slideInDown { - -webkit-animation-name: slideInDown; - animation-name: slideInDown; -} - -@-webkit-keyframes slideInLeft { - from { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes slideInLeft { - from { - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.slideInLeft { - -webkit-animation-name: slideInLeft; - animation-name: slideInLeft; -} - -@-webkit-keyframes slideInRight { - from { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes slideInRight { - from { - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.slideInRight { - -webkit-animation-name: slideInRight; - animation-name: slideInRight; -} - -@-webkit-keyframes slideInUp { - from { - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -@keyframes slideInUp { - from { - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - visibility: visible; - } - - to { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} - -.slideInUp { - -webkit-animation-name: slideInUp; - animation-name: slideInUp; -} - -@-webkit-keyframes slideOutDown { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } -} - -@keyframes slideOutDown { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(0, 100%, 0); - transform: translate3d(0, 100%, 0); - } -} - -.slideOutDown { - -webkit-animation-name: slideOutDown; - animation-name: slideOutDown; -} - -@-webkit-keyframes slideOutLeft { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -@keyframes slideOutLeft { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } -} - -.slideOutLeft { - -webkit-animation-name: slideOutLeft; - animation-name: slideOutLeft; -} - -@-webkit-keyframes slideOutRight { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -@keyframes slideOutRight { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } -} - -.slideOutRight { - -webkit-animation-name: slideOutRight; - animation-name: slideOutRight; -} - -@-webkit-keyframes slideOutUp { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } -} - -@keyframes slideOutUp { - from { - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } - - to { - visibility: hidden; - -webkit-transform: translate3d(0, -100%, 0); - transform: translate3d(0, -100%, 0); - } -} - -.slideOutUp { - -webkit-animation-name: slideOutUp; - animation-name: slideOutUp; -} diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/animate-css/animate.min.css b/Plan/common/src/main/resources/assets/plan/web/plugins/animate-css/animate.min.css deleted file mode 100644 index 995954196..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/animate-css/animate.min.css +++ /dev/null @@ -1,11 +0,0 @@ -@charset "UTF-8"; - -/*! - * animate.css -http://daneden.me/animate - * Version - 3.5.0 - * Licensed under the MIT license - http://opensource.org/licenses/MIT - * - * Copyright (c) 2016 Daniel Eden - */ - -.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}.animated.bounceIn,.animated.bounceOut,.animated.flipOutX,.animated.flipOutY{-webkit-animation-duration:.75s;animation-duration:.75s}@-webkit-keyframes bounce{0%,20%,53%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translateZ(0);transform:translateZ(0)}40%,43%{-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}40%,43%,70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06)}70%{-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{0%,20%,53%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translateZ(0);transform:translateZ(0)}40%,43%{-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0)}40%,43%,70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06)}70%{-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,50%,to{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,50%,to{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes pulse{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes rubberBand{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px) rotateY(-9deg);transform:translateX(-6px) rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px) rotateY(7deg);transform:translateX(5px) rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px) rotateY(-5deg);transform:translateX(-3px) rotateY(-5deg)}43.5%{-webkit-transform:translateX(2px) rotateY(3deg);transform:translateX(2px) rotateY(3deg)}50%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px) rotateY(-9deg);transform:translateX(-6px) rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px) rotateY(7deg);transform:translateX(5px) rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px) rotateY(-5deg);transform:translateX(-3px) rotateY(-5deg)}43.5%{-webkit-transform:translateX(2px) rotateY(3deg);transform:translateX(2px) rotateY(3deg)}50%{-webkit-transform:translateX(0);transform:translateX(0)}}.headShake{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-name:headShake;animation-name:headShake}@-webkit-keyframes swing{20%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes swing{20%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}.swing{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate(-3deg);transform:scale3d(.9,.9,.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(3deg);transform:scale3d(1.1,1.1,1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(-3deg);transform:scale3d(1.1,1.1,1.1) rotate(-3deg)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes tada{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate(-3deg);transform:scale3d(.9,.9,.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(3deg);transform:scale3d(1.1,1.1,1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(-3deg);transform:scale3d(1.1,1.1,1.1) rotate(-3deg)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate(-5deg);transform:translate3d(-25%,0,0) rotate(-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate(3deg);transform:translate3d(20%,0,0) rotate(3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate(-3deg);transform:translate3d(-15%,0,0) rotate(-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate(2deg);transform:translate3d(10%,0,0) rotate(2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate(-1deg);transform:translate3d(-5%,0,0) rotate(-1deg)}to{-webkit-transform:none;transform:none}}@keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate(-5deg);transform:translate3d(-25%,0,0) rotate(-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate(3deg);transform:translate3d(20%,0,0) rotate(3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate(-3deg);transform:translate3d(-15%,0,0) rotate(-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate(2deg);transform:translate3d(10%,0,0) rotate(2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate(-1deg);transform:translate3d(-5%,0,0) rotate(-1deg)}to{-webkit-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes jello{0%,11.1%,to{-webkit-transform:none;transform:none}22.2%{-webkit-transform:skewX(-12.5deg) skewY(-12.5deg);transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg) skewY(6.25deg);transform:skewX(6.25deg) skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg) skewY(-3.125deg);transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg) skewY(1.5625deg);transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg) skewY(-.78125deg);transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg) skewY(.390625deg);transform:skewX(.390625deg) skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg) skewY(-.1953125deg);transform:skewX(-.1953125deg) skewY(-.1953125deg)}}@keyframes jello{0%,11.1%,to{-webkit-transform:none;transform:none}22.2%{-webkit-transform:skewX(-12.5deg) skewY(-12.5deg);transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg) skewY(6.25deg);transform:skewX(6.25deg) skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg) skewY(-3.125deg);transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg) skewY(1.5625deg);transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg) skewY(-.78125deg);transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg) skewY(.390625deg);transform:skewX(.390625deg) skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg) skewY(-.1953125deg);transform:skewX(-.1953125deg) skewY(-.1953125deg)}}.jello{-webkit-animation-name:jello;animation-name:jello;-webkit-transform-origin:center;transform-origin:center}@-webkit-keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}.bounceIn{-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}to{-webkit-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}to{-webkit-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}@keyframes bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}to{-webkit-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.bounceOut{-webkit-animation-name:bounceOut;animation-name:bounceOut}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) rotateY(-1turn);transform:perspective(400px) rotateY(-1turn)}0%,40%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translateZ(150px) rotateY(-190deg);transform:perspective(400px) translateZ(150px) rotateY(-190deg)}50%{-webkit-transform:perspective(400px) translateZ(150px) rotateY(-170deg);transform:perspective(400px) translateZ(150px) rotateY(-170deg)}50%,80%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95)}to{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) rotateY(-1turn);transform:perspective(400px) rotateY(-1turn)}0%,40%{-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translateZ(150px) rotateY(-190deg);transform:perspective(400px) translateZ(150px) rotateY(-190deg)}50%{-webkit-transform:perspective(400px) translateZ(150px) rotateY(-170deg);transform:perspective(400px) translateZ(150px) rotateY(-170deg)}50%,80%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95);transform:perspective(400px) scale3d(.95,.95,.95)}to{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}0%,40%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg)}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}0%,40%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg)}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{0%{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}0%,40%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotateY(-20deg);transform:perspective(400px) rotateY(-20deg)}60%{-webkit-transform:perspective(400px) rotateY(10deg);transform:perspective(400px) rotateY(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateY(-5deg);transform:perspective(400px) rotateY(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}0%,40%{-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotateY(-20deg);transform:perspective(400px) rotateY(-20deg)}60%{-webkit-transform:perspective(400px) rotateY(10deg);transform:perspective(400px) rotateY(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateY(-5deg);transform:perspective(400px) rotateY(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}.flipOutX{-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateY(-15deg);transform:perspective(400px) rotateY(-15deg);opacity:1}to{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateY(-15deg);transform:perspective(400px) rotateY(-15deg);opacity:1}to{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}}.flipOutY{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY}@-webkit-keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg)}60%,80%{opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}to{-webkit-transform:none;transform:none;opacity:1}}@keyframes lightSpeedIn{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg)}60%,80%{opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}to{-webkit-transform:none;transform:none;opacity:1}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{0%{opacity:1}to{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}@keyframes lightSpeedOut{0%{opacity:1}to{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{transform-origin:center;-webkit-transform:rotate(-200deg);transform:rotate(-200deg);opacity:0}0%,to{-webkit-transform-origin:center}to{transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateIn{0%{transform-origin:center;-webkit-transform:rotate(-200deg);transform:rotate(-200deg);opacity:0}0%,to{-webkit-transform-origin:center}to{transform-origin:center;-webkit-transform:none;transform:none;opacity:1}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{0%{transform-origin:left bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownLeft{0%{transform-origin:left bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{0%{transform-origin:right bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInDownRight{0%{transform-origin:right bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{0%{transform-origin:left bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpLeft{0%{transform-origin:left bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{0%{transform-origin:right bottom;-webkit-transform:rotate(-90deg);transform:rotate(-90deg);opacity:0}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}@keyframes rotateInUpRight{0%{transform-origin:right bottom;-webkit-transform:rotate(-90deg);transform:rotate(-90deg);opacity:0}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:none;transform:none;opacity:1}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{0%{transform-origin:center;opacity:1}0%,to{-webkit-transform-origin:center}to{transform-origin:center;-webkit-transform:rotate(200deg);transform:rotate(200deg);opacity:0}}@keyframes rotateOut{0%{transform-origin:center;opacity:1}0%,to{-webkit-transform-origin:center}to{transform-origin:center;-webkit-transform:rotate(200deg);transform:rotate(200deg);opacity:0}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{0%{transform-origin:left bottom;opacity:1}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}}@keyframes rotateOutDownLeft{0%{transform-origin:left bottom;opacity:1}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{0%{transform-origin:right bottom;opacity:1}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}@keyframes rotateOutDownRight{0%{transform-origin:right bottom;opacity:1}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{0%{transform-origin:left bottom;opacity:1}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}@keyframes rotateOutUpLeft{0%{transform-origin:left bottom;opacity:1}0%,to{-webkit-transform-origin:left bottom}to{transform-origin:left bottom;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{0%{transform-origin:right bottom;opacity:1}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:rotate(90deg);transform:rotate(90deg);opacity:0}}@keyframes rotateOutUpRight{0%{transform-origin:right bottom;opacity:1}0%,to{-webkit-transform-origin:right bottom}to{transform-origin:right bottom;-webkit-transform:rotate(90deg);transform:rotate(90deg);opacity:0}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{transform-origin:top left}0%,20%,60%{-webkit-transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate(80deg);transform:rotate(80deg);transform-origin:top left}40%,80%{-webkit-transform:rotate(60deg);transform:rotate(60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{transform-origin:top left}0%,20%,60%{-webkit-transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate(80deg);transform:rotate(80deg);transform-origin:top left}40%,80%{-webkit-transform:rotate(60deg);transform:rotate(60deg);-webkit-transform-origin:top left;transform-origin:top left;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate(-120deg);transform:translate3d(-100%,0,0) rotate(-120deg)}to{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate(-120deg);transform:translate3d(-100%,0,0) rotate(-120deg)}to{opacity:1;-webkit-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate(120deg);transform:translate3d(100%,0,0) rotate(120deg)}}@keyframes rollOut{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate(120deg);transform:translate3d(100%,0,0) rotate(120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%,to{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%,to{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-transform-origin:center bottom;transform-origin:center bottom;-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp}@-webkit-keyframes slideInDown{0%{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInDown{0%{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.slideInDown{-webkit-animation-name:slideInDown;animation-name:slideInDown}@-webkit-keyframes slideInLeft{0%{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInLeft{0%{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.slideInLeft{-webkit-animation-name:slideInLeft;animation-name:slideInLeft}@-webkit-keyframes slideInRight{0%{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInRight{0%{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.slideInRight{-webkit-animation-name:slideInRight;animation-name:slideInRight}@-webkit-keyframes slideInUp{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInUp{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.slideInUp{-webkit-animation-name:slideInUp;animation-name:slideInUp}@-webkit-keyframes slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.slideOutDown{-webkit-animation-name:slideOutDown;animation-name:slideOutDown}@-webkit-keyframes slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.slideOutLeft{-webkit-animation-name:slideOutLeft;animation-name:slideOutLeft}@-webkit-keyframes slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.slideOutRight{-webkit-animation-name:slideOutRight;animation-name:slideOutRight}@-webkit-keyframes slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.slideOutUp{-webkit-animation-name:slideOutUp;animation-name:slideOutUp} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/autosize/autosize.js b/Plan/common/src/main/resources/assets/plan/web/plugins/autosize/autosize.js deleted file mode 100644 index 49b8b0b95..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/autosize/autosize.js +++ /dev/null @@ -1,262 +0,0 @@ -/*! - Autosize 3.0.17 - license: MIT - http://www.jacklmoore.com/autosize -*/ -(function (global, factory) { - if (typeof define === 'function' && define.amd) { - define(['exports', 'module'], factory); - } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { - factory(exports, module); - } else { - var mod = { - exports: {} - }; - factory(mod.exports, mod); - global.autosize = mod.exports; - } -})(this, function (exports, module) { - 'use strict'; - - var set = typeof Set === 'function' ? new Set() : (function () { - var list = []; - - return { - has: function has(key) { - return Boolean(list.indexOf(key) > -1); - }, - add: function add(key) { - list.push(key); - }, - 'delete': function _delete(key) { - list.splice(list.indexOf(key), 1); - } }; - })(); - - var createEvent = function createEvent(name) { - return new Event(name); - }; - try { - new Event('test'); - } catch (e) { - // IE does not support `new Event()` - createEvent = function (name) { - var evt = document.createEvent('Event'); - evt.initEvent(name, true, false); - return evt; - }; - } - - function assign(ta) { - if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || set.has(ta)) return; - - var heightOffset = null; - var clientWidth = ta.clientWidth; - var cachedHeight = null; - - function init() { - var style = window.getComputedStyle(ta, null); - - if (style.resize === 'vertical') { - ta.style.resize = 'none'; - } else if (style.resize === 'both') { - ta.style.resize = 'horizontal'; - } - - if (style.boxSizing === 'content-box') { - heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom)); - } else { - heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth); - } - // Fix when a textarea is not on document body and heightOffset is Not a Number - if (isNaN(heightOffset)) { - heightOffset = 0; - } - - update(); - } - - function changeOverflow(value) { - { - // Chrome/Safari-specific fix: - // When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space - // made available by removing the scrollbar. The following forces the necessary text reflow. - var width = ta.style.width; - ta.style.width = '0px'; - // Force reflow: - /* jshint ignore:start */ - ta.offsetWidth; - /* jshint ignore:end */ - ta.style.width = width; - } - - ta.style.overflowY = value; - - resize(); - } - - function getParentOverflows(el) { - var arr = []; - - while (el && el.parentNode && el.parentNode instanceof Element) { - if (el.parentNode.scrollTop) { - arr.push({ - node: el.parentNode, - scrollTop: el.parentNode.scrollTop }); - } - el = el.parentNode; - } - - return arr; - } - - function resize() { - var originalHeight = ta.style.height; - var overflows = getParentOverflows(ta); - var docTop = document.documentElement && document.documentElement.scrollTop; // Needed for Mobile IE (ticket #240) - - ta.style.height = 'auto'; - - var endHeight = ta.scrollHeight + heightOffset; - - if (ta.scrollHeight === 0) { - // If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM. - ta.style.height = originalHeight; - return; - } - - ta.style.height = endHeight + 'px'; - - // used to check if an update is actually necessary on window.resize - clientWidth = ta.clientWidth; - - // prevents scroll-position jumping - overflows.forEach(function (el) { - el.node.scrollTop = el.scrollTop; - }); - - if (docTop) { - document.documentElement.scrollTop = docTop; - } - } - - function update() { - resize(); - - var computed = window.getComputedStyle(ta, null); - var computedHeight = Math.round(parseFloat(computed.height)); - var styleHeight = Math.round(parseFloat(ta.style.height)); - - // The computed height not matching the height set via resize indicates that - // the max-height has been exceeded, in which case the overflow should be set to visible. - if (computedHeight !== styleHeight) { - if (computed.overflowY !== 'visible') { - changeOverflow('visible'); - } - } else { - // Normally keep overflow set to hidden, to avoid flash of scrollbar as the textarea expands. - if (computed.overflowY !== 'hidden') { - changeOverflow('hidden'); - } - } - - if (cachedHeight !== computedHeight) { - cachedHeight = computedHeight; - var evt = createEvent('autosize:resized'); - ta.dispatchEvent(evt); - } - } - - var pageResize = function pageResize() { - if (ta.clientWidth !== clientWidth) { - update(); - } - }; - - var destroy = (function (style) { - window.removeEventListener('resize', pageResize, false); - ta.removeEventListener('input', update, false); - ta.removeEventListener('keyup', update, false); - ta.removeEventListener('autosize:destroy', destroy, false); - ta.removeEventListener('autosize:update', update, false); - set['delete'](ta); - - Object.keys(style).forEach(function (key) { - ta.style[key] = style[key]; - }); - }).bind(ta, { - height: ta.style.height, - resize: ta.style.resize, - overflowY: ta.style.overflowY, - overflowX: ta.style.overflowX, - wordWrap: ta.style.wordWrap }); - - ta.addEventListener('autosize:destroy', destroy, false); - - // IE9 does not fire onpropertychange or oninput for deletions, - // so binding to onkeyup to catch most of those events. - // There is no way that I know of to detect something like 'cut' in IE9. - if ('onpropertychange' in ta && 'oninput' in ta) { - ta.addEventListener('keyup', update, false); - } - - window.addEventListener('resize', pageResize, false); - ta.addEventListener('input', update, false); - ta.addEventListener('autosize:update', update, false); - set.add(ta); - ta.style.overflowX = 'hidden'; - ta.style.wordWrap = 'break-word'; - - init(); - } - - function destroy(ta) { - if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return; - var evt = createEvent('autosize:destroy'); - ta.dispatchEvent(evt); - } - - function update(ta) { - if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return; - var evt = createEvent('autosize:update'); - ta.dispatchEvent(evt); - } - - var autosize = null; - - // Do nothing in Node.js environment and IE8 (or lower) - if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') { - autosize = function (el) { - return el; - }; - autosize.destroy = function (el) { - return el; - }; - autosize.update = function (el) { - return el; - }; - } else { - autosize = function (el, options) { - if (el) { - Array.prototype.forEach.call(el.length ? el : [el], function (x) { - return assign(x, options); - }); - } - return el; - }; - autosize.destroy = function (el) { - if (el) { - Array.prototype.forEach.call(el.length ? el : [el], destroy); - } - return el; - }; - autosize.update = function (el) { - if (el) { - Array.prototype.forEach.call(el.length ? el : [el], update); - } - return el; - }; - } - - module.exports = autosize; -}); \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/autosize/autosize.min.js b/Plan/common/src/main/resources/assets/plan/web/plugins/autosize/autosize.min.js deleted file mode 100644 index 2108e9033..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/autosize/autosize.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - Autosize 3.0.17 - license: MIT - http://www.jacklmoore.com/autosize -*/ -!function(e,t){if("function"==typeof define&&define.amd)define(["exports","module"],t);else if("undefined"!=typeof exports&&"undefined"!=typeof module)t(exports,module);else{var n={exports:{}};t(n.exports,n),e.autosize=n.exports}}(this,function(e,t){"use strict";function n(e){function t(){var t=window.getComputedStyle(e,null);"vertical"===t.resize?e.style.resize="none":"both"===t.resize&&(e.style.resize="horizontal"),l="content-box"===t.boxSizing?-(parseFloat(t.paddingTop)+parseFloat(t.paddingBottom)):parseFloat(t.borderTopWidth)+parseFloat(t.borderBottomWidth),isNaN(l)&&(l=0),a()}function n(t){var n=e.style.width;e.style.width="0px",e.offsetWidth,e.style.width=n,e.style.overflowY=t,r()}function o(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push({node:e.parentNode,scrollTop:e.parentNode.scrollTop}),e=e.parentNode;return t}function r(){var t=e.style.height,n=o(e),r=document.documentElement&&document.documentElement.scrollTop;e.style.height="auto";var i=e.scrollHeight+l;return 0===e.scrollHeight?void(e.style.height=t):(e.style.height=i+"px",s=e.clientWidth,n.forEach(function(e){e.node.scrollTop=e.scrollTop}),void(r&&(document.documentElement.scrollTop=r)))}function a(){r();var t=window.getComputedStyle(e,null),o=Math.round(parseFloat(t.height)),i=Math.round(parseFloat(e.style.height));if(o!==i?"visible"!==t.overflowY&&n("visible"):"hidden"!==t.overflowY&&n("hidden"),u!==o){u=o;var a=d("autosize:resized");e.dispatchEvent(a)}}if(e&&e.nodeName&&"TEXTAREA"===e.nodeName&&!i.has(e)){var l=null,s=e.clientWidth,u=null,c=function(){e.clientWidth!==s&&a()},p=function(t){window.removeEventListener("resize",c,!1),e.removeEventListener("input",a,!1),e.removeEventListener("keyup",a,!1),e.removeEventListener("autosize:destroy",p,!1),e.removeEventListener("autosize:update",a,!1),i["delete"](e),Object.keys(t).forEach(function(n){e.style[n]=t[n]})}.bind(e,{height:e.style.height,resize:e.style.resize,overflowY:e.style.overflowY,overflowX:e.style.overflowX,wordWrap:e.style.wordWrap});e.addEventListener("autosize:destroy",p,!1),"onpropertychange"in e&&"oninput"in e&&e.addEventListener("keyup",a,!1),window.addEventListener("resize",c,!1),e.addEventListener("input",a,!1),e.addEventListener("autosize:update",a,!1),i.add(e),e.style.overflowX="hidden",e.style.wordWrap="break-word",t()}}function o(e){if(e&&e.nodeName&&"TEXTAREA"===e.nodeName){var t=d("autosize:destroy");e.dispatchEvent(t)}}function r(e){if(e&&e.nodeName&&"TEXTAREA"===e.nodeName){var t=d("autosize:update");e.dispatchEvent(t)}}var i="function"==typeof Set?new Set:function(){var e=[];return{has:function(t){return Boolean(e.indexOf(t)>-1)},add:function(t){e.push(t)},"delete":function(t){e.splice(e.indexOf(t),1)}}}(),d=function(e){return new Event(e)};try{new Event("test")}catch(a){d=function(e){var t=document.createEvent("Event");return t.initEvent(e,!0,!1),t}}var l=null;"undefined"==typeof window||"function"!=typeof window.getComputedStyle?(l=function(e){return e},l.destroy=function(e){return e},l.update=function(e){return e}):(l=function(e,t){return e&&Array.prototype.forEach.call(e.length?e:[e],function(e){return n(e,t)}),e},l.destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e},l.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],r),e}),t.exports=l}); \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.css b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.css deleted file mode 100644 index 0f47dd40f..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.css +++ /dev/null @@ -1,222 +0,0 @@ -/*! - * Bootstrap Colorpicker v2.3.3 - * http://mjolnic.github.io/bootstrap-colorpicker/ - * - * Originally written by (c) 2012 Stefan Petre - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0.txt - * - */ -.colorpicker-saturation { - width: 100px; - height: 100px; - background-image: url("../img/bootstrap-colorpicker/saturation.png"); - cursor: crosshair; - float: left; -} -.colorpicker-saturation i { - display: block; - height: 5px; - width: 5px; - border: 1px solid #000; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; - position: absolute; - top: 0; - left: 0; - margin: -4px 0 0 -4px; -} -.colorpicker-saturation i b { - display: block; - height: 5px; - width: 5px; - border: 1px solid #fff; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} -.colorpicker-hue, -.colorpicker-alpha { - width: 15px; - height: 100px; - float: left; - cursor: row-resize; - margin-left: 4px; - margin-bottom: 4px; -} -.colorpicker-hue i, -.colorpicker-alpha i { - display: block; - height: 1px; - background: #000; - border-top: 1px solid #fff; - position: absolute; - top: 0; - left: 0; - width: 100%; - margin-top: -1px; -} -.colorpicker-hue { - background-image: url("../img/bootstrap-colorpicker/hue.png"); -} -.colorpicker-alpha { - background-image: url("../img/bootstrap-colorpicker/alpha.png"); - display: none; -} -.colorpicker-saturation, -.colorpicker-hue, -.colorpicker-alpha { - background-size: contain; -} -.colorpicker { - padding: 4px; - min-width: 130px; - margin-top: 1px; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - z-index: 2500; -} -.colorpicker:before, -.colorpicker:after { - display: table; - content: ""; - line-height: 0; -} -.colorpicker:after { - clear: both; -} -.colorpicker:before { - content: ''; - display: inline-block; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-bottom: 7px solid #ccc; - border-bottom-color: rgba(0, 0, 0, 0.2); - position: absolute; - top: -7px; - left: 6px; -} -.colorpicker:after { - content: ''; - display: inline-block; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-bottom: 6px solid #ffffff; - position: absolute; - top: -6px; - left: 7px; -} -.colorpicker div { - position: relative; -} -.colorpicker.colorpicker-with-alpha { - min-width: 140px; -} -.colorpicker.colorpicker-with-alpha .colorpicker-alpha { - display: block; -} -.colorpicker-color { - height: 10px; - margin-top: 5px; - clear: both; - background-image: url("../img/bootstrap-colorpicker/alpha.png"); - background-position: 0 100%; -} -.colorpicker-color div { - height: 10px; -} -.colorpicker-selectors { - display: none; - height: 10px; - margin-top: 5px; - clear: both; -} -.colorpicker-selectors i { - cursor: pointer; - float: left; - height: 10px; - width: 10px; -} -.colorpicker-selectors i + i { - margin-left: 3px; -} -.colorpicker-element .input-group-addon i, -.colorpicker-element .add-on i { - display: inline-block; - cursor: pointer; - height: 16px; - vertical-align: text-top; - width: 16px; -} -.colorpicker.colorpicker-inline { - position: relative; - display: inline-block; - float: none; - z-index: auto; -} -.colorpicker.colorpicker-horizontal { - width: 110px; - min-width: 110px; - height: auto; -} -.colorpicker.colorpicker-horizontal .colorpicker-saturation { - margin-bottom: 4px; -} -.colorpicker.colorpicker-horizontal .colorpicker-color { - width: 100px; -} -.colorpicker.colorpicker-horizontal .colorpicker-hue, -.colorpicker.colorpicker-horizontal .colorpicker-alpha { - width: 100px; - height: 15px; - float: left; - cursor: col-resize; - margin-left: 0; - margin-bottom: 4px; -} -.colorpicker.colorpicker-horizontal .colorpicker-hue i, -.colorpicker.colorpicker-horizontal .colorpicker-alpha i { - display: block; - height: 15px; - background: #ffffff; - position: absolute; - top: 0; - left: 0; - width: 1px; - border: none; - margin-top: 0; -} -.colorpicker.colorpicker-horizontal .colorpicker-hue { - background-image: url("../img/bootstrap-colorpicker/hue-horizontal.png"); -} -.colorpicker.colorpicker-horizontal .colorpicker-alpha { - background-image: url("../img/bootstrap-colorpicker/alpha-horizontal.png"); -} -.colorpicker.colorpicker-hidden { - display: none; -} -.colorpicker.colorpicker-visible { - display: block; -} -.colorpicker-inline.colorpicker-visible { - display: inline-block; -} -.colorpicker-right:before { - left: auto; - right: 6px; -} -.colorpicker-right:after { - left: auto; - right: 7px; -} -.colorpicker-no-arrow:before { - border-right: 0; - border-left: 0; -} -.colorpicker-no-arrow:after { - border-right: 0; - border-left: 0; -} -/*# sourceMappingURL=bootstrap-colorpicker.css.map */ \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.css.map b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.css.map deleted file mode 100644 index e61413ad6..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["src/less/colorpicker.less"],"names":[],"mappings":";;;;;;;;;AAqBA;EACE,YAAA;EACA,aAAA;EAXA,sBAAsB,8CAAtB;EAaA,iBAAA;EACA,WAAA;;AALF,uBAME;EACE,cAAA;EACA,WAAA;EACA,UAAA;EACA,sBAAA;EAfF,0BAAA;EACA,uBAAA;EACA,kBAAA;EAeE,kBAAA;EACA,MAAA;EACA,OAAA;EACA,qBAAA;;AAfJ,uBAME,EAUE;EACE,cAAA;EACA,WAAA;EACA,UAAA;EACA,sBAAA;EAzBJ,0BAAA;EACA,uBAAA;EACA,kBAAA;;AA6BF;AACA;EACE,WAAA;EACA,aAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,kBAAA;;AAGF,gBAAiB;AACjB,kBAAmB;EACjB,cAAA;EACA,WAAA;EACA,gBAAA;EACA,0BAAA;EACA,kBAAA;EACA,MAAA;EACA,OAAA;EACA,WAAA;EACA,gBAAA;;AAGF;EA1DE,sBAAsB,uCAAtB;;AA8DF;EA9DE,sBAAsB,yCAAtB;EAgEA,aAAA;;AAGF;AACA;AACA;EACE,wBAAA;;AAGF;EACE,YAAA;EACA,gBAAA;EACA,eAAA;EAxEA,0BAAA;EACA,uBAAA;EACA,kBAAA;EAwEA,aAAA;;AAGF,YAAY;AACZ,YAAY;EACV,cAAA;EACA,SAAS,EAAT;EACA,cAAA;;AAGF,YAAY;EACV,WAAA;;AAGF,YAAY;EACV,SAAS,EAAT;EACA,qBAAA;EACA,kCAAA;EACA,mCAAA;EACA,6BAAA;EACA,uCAAA;EACA,kBAAA;EACA,SAAA;EACA,SAAA;;AAGF,YAAY;EACV,SAAS,EAAT;EACA,qBAAA;EACA,kCAAA;EACA,mCAAA;EACA,gCAAA;EACA,kBAAA;EACA,SAAA;EACA,SAAA;;AAGF,YAAa;EACX,kBAAA;;AAGF,YAAY;EACV,gBAAA;;AAGF,YAAY,uBAAwB;EAClC,cAAA;;AAGF;EACE,YAAA;EACA,eAAA;EACA,WAAA;EAlIA,sBAAsB,yCAAtB;EAoIA,2BAAA;;AAGF,kBAAmB;EACjB,YAAA;;AAGF;EACE,aAAA;EACA,YAAA;EACA,eAAA;EACA,WAAA;;AAGF,sBAAuB;EACrB,eAAA;EACA,WAAA;EACA,YAAA;EACA,WAAA;;AAGF,sBAAuB,EAAE;EACvB,gBAAA;;AAGF,oBAAqB,mBAAmB;AACxC,oBAAqB,QAAQ;EAC3B,qBAAA;EACA,eAAA;EACA,YAAA;EACA,wBAAA;EACA,WAAA;;AAGF,YAAY;EACV,kBAAA;EACA,qBAAA;EACA,WAAA;EACA,aAAA;;AAGF,YAAY;EACV,YAAA;EACA,gBAAA;EACA,YAAA;;AAGF,YAAY,uBAAwB;EAClC,kBAAA;;AAGF,YAAY,uBAAwB;EAClC,YAAA;;AAGF,YAAY,uBAAwB;AACpC,YAAY,uBAAwB;EAClC,YAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,kBAAA;;AAGF,YAAY,uBAAwB,iBAAiB;AACrD,YAAY,uBAAwB,mBAAmB;EACrD,cAAA;EACA,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,MAAA;EACA,OAAA;EACA,UAAA;EACA,YAAA;EACA,eAAA;;AAGF,YAAY,uBAAwB;EAlNlC,sBAAsB,kDAAtB;;AAsNF,YAAY,uBAAwB;EAtNlC,sBAAsB,oDAAtB;;AA0NF,YAAY;EACV,aAAA;;AAGF,YAAY;EACV,cAAA;;AAGF,mBAAmB;EACjB,qBAAA;;AAGF,kBAAkB;EAChB,UAAA;EACA,UAAA;;AAGF,kBAAkB;EAChB,UAAA;EACA,UAAA;;AAGF,qBAAqB;EACnB,eAAA;EACA,cAAA;;AAGF,qBAAqB;EACnB,eAAA;EACA,cAAA","sourcesContent":["/*!\n * Bootstrap Colorpicker v2.3.3\n * http://mjolnic.github.io/bootstrap-colorpicker/\n *\n * Originally written by (c) 2012 Stefan Petre\n * Licensed under the Apache License v2.0\n * http://www.apache.org/licenses/LICENSE-2.0.txt\n *\n */\n@imgPath: \"../img/bootstrap-colorpicker/\";\n\n.bgImg(@imgFilename) {\n background-image: url(\"@{imgPath}@{imgFilename}\");\n}\n\n.borderRadius(@size) {\n -webkit-border-radius: @size;\n -moz-border-radius: @size;\n border-radius: @size;\n}\n\n.colorpicker-saturation {\n width: 100px;\n height: 100px;\n .bgImg('saturation.png');\n cursor: crosshair;\n float: left;\n i {\n display: block;\n height: 5px;\n width: 5px;\n border: 1px solid #000;\n .borderRadius(5px);\n position: absolute;\n top: 0;\n left: 0;\n margin: -4px 0 0 -4px;\n b {\n display: block;\n height: 5px;\n width: 5px;\n border: 1px solid #fff;\n .borderRadius(5px);\n }\n }\n}\n\n.colorpicker-hue,\n.colorpicker-alpha {\n width: 15px;\n height: 100px;\n float: left;\n cursor: row-resize;\n margin-left: 4px;\n margin-bottom: 4px;\n}\n\n.colorpicker-hue i,\n.colorpicker-alpha i {\n display: block;\n height: 1px;\n background: #000;\n border-top: 1px solid #fff;\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n margin-top: -1px;\n}\n\n.colorpicker-hue {\n .bgImg('hue.png');\n}\n\n.colorpicker-alpha {\n .bgImg('alpha.png');\n display: none;\n}\n\n.colorpicker-saturation,\n.colorpicker-hue,\n.colorpicker-alpha {\n background-size: contain;\n}\n\n.colorpicker {\n padding: 4px;\n min-width: 130px;\n margin-top: 1px;\n .borderRadius(4px);\n z-index: 2500;\n}\n\n.colorpicker:before,\n.colorpicker:after {\n display: table;\n content: \"\";\n line-height: 0;\n}\n\n.colorpicker:after {\n clear: both;\n}\n\n.colorpicker:before {\n content: '';\n display: inline-block;\n border-left: 7px solid transparent;\n border-right: 7px solid transparent;\n border-bottom: 7px solid #ccc;\n border-bottom-color: rgba(0, 0, 0, 0.2);\n position: absolute;\n top: -7px;\n left: 6px;\n}\n\n.colorpicker:after {\n content: '';\n display: inline-block;\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-bottom: 6px solid #ffffff;\n position: absolute;\n top: -6px;\n left: 7px;\n}\n\n.colorpicker div {\n position: relative;\n}\n\n.colorpicker.colorpicker-with-alpha {\n min-width: 140px;\n}\n\n.colorpicker.colorpicker-with-alpha .colorpicker-alpha {\n display: block;\n}\n\n.colorpicker-color {\n height: 10px;\n margin-top: 5px;\n clear: both;\n .bgImg('alpha.png');\n background-position: 0 100%;\n}\n\n.colorpicker-color div {\n height: 10px;\n}\n\n.colorpicker-selectors {\n display: none;\n height: 10px;\n margin-top: 5px;\n clear: both;\n}\n\n.colorpicker-selectors i {\n cursor: pointer;\n float: left;\n height: 10px;\n width: 10px;\n}\n\n.colorpicker-selectors i + i {\n margin-left: 3px;\n}\n\n.colorpicker-element .input-group-addon i,\n.colorpicker-element .add-on i {\n display: inline-block;\n cursor: pointer;\n height: 16px;\n vertical-align: text-top;\n width: 16px;\n}\n\n.colorpicker.colorpicker-inline {\n position: relative;\n display: inline-block;\n float: none;\n z-index: auto;\n}\n\n.colorpicker.colorpicker-horizontal {\n width: 110px;\n min-width: 110px;\n height: auto;\n}\n\n.colorpicker.colorpicker-horizontal .colorpicker-saturation {\n margin-bottom: 4px;\n}\n\n.colorpicker.colorpicker-horizontal .colorpicker-color {\n width: 100px;\n}\n\n.colorpicker.colorpicker-horizontal .colorpicker-hue,\n.colorpicker.colorpicker-horizontal .colorpicker-alpha {\n width: 100px;\n height: 15px;\n float: left;\n cursor: col-resize;\n margin-left: 0px;\n margin-bottom: 4px;\n}\n\n.colorpicker.colorpicker-horizontal .colorpicker-hue i,\n.colorpicker.colorpicker-horizontal .colorpicker-alpha i {\n display: block;\n height: 15px;\n background: #ffffff;\n position: absolute;\n top: 0;\n left: 0;\n width: 1px;\n border: none;\n margin-top: 0px;\n}\n\n.colorpicker.colorpicker-horizontal .colorpicker-hue {\n .bgImg('hue-horizontal.png');\n}\n\n.colorpicker.colorpicker-horizontal .colorpicker-alpha {\n .bgImg('alpha-horizontal.png');\n}\n\n.colorpicker.colorpicker-hidden {\n display: none;\n}\n\n.colorpicker.colorpicker-visible {\n display: block;\n}\n\n.colorpicker-inline.colorpicker-visible {\n display: inline-block;\n}\n\n.colorpicker-right:before {\n left: auto;\n right: 6px;\n}\n\n.colorpicker-right:after {\n left: auto;\n right: 7px;\n}\n\n.colorpicker-no-arrow:before {\n border-right: 0;\n border-left: 0;\n}\n\n.colorpicker-no-arrow:after {\n border-right: 0;\n border-left: 0;\n}\n"]} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css deleted file mode 100644 index 22e5c37ae..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css +++ /dev/null @@ -1,10 +0,0 @@ -/*! - * Bootstrap Colorpicker v2.3.3 - * http://mjolnic.github.io/bootstrap-colorpicker/ - * - * Originally written by (c) 2012 Stefan Petre - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0.txt - * - */.colorpicker-saturation{width:100px;height:100px;background-image:url(../img/bootstrap-colorpicker/saturation.png);cursor:crosshair;float:left}.colorpicker-saturation i{display:block;height:5px;width:5px;border:1px solid #000;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;position:absolute;top:0;left:0;margin:-4px 0 0 -4px}.colorpicker-saturation i b{display:block;height:5px;width:5px;border:1px solid #fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.colorpicker-alpha,.colorpicker-hue{width:15px;height:100px;float:left;cursor:row-resize;margin-left:4px;margin-bottom:4px}.colorpicker-alpha i,.colorpicker-hue i{display:block;height:1px;background:#000;border-top:1px solid #fff;position:absolute;top:0;left:0;width:100%;margin-top:-1px}.colorpicker-hue{background-image:url(../img/bootstrap-colorpicker/hue.png)}.colorpicker-alpha{background-image:url(../img/bootstrap-colorpicker/alpha.png);display:none}.colorpicker-alpha,.colorpicker-hue,.colorpicker-saturation{background-size:contain}.colorpicker{padding:4px;min-width:130px;margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;z-index:2500}.colorpicker:after,.colorpicker:before{display:table;content:"";line-height:0}.colorpicker:after{clear:both}.colorpicker:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,.2);position:absolute;top:-7px;left:6px}.colorpicker:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:7px}.colorpicker div{position:relative}.colorpicker.colorpicker-with-alpha{min-width:140px}.colorpicker.colorpicker-with-alpha .colorpicker-alpha{display:block}.colorpicker-color{height:10px;margin-top:5px;clear:both;background-image:url(../img/bootstrap-colorpicker/alpha.png);background-position:0 100%}.colorpicker-color div{height:10px}.colorpicker-selectors{display:none;height:10px;margin-top:5px;clear:both}.colorpicker-selectors i{cursor:pointer;float:left;height:10px;width:10px}.colorpicker-selectors i+i{margin-left:3px}.colorpicker-element .add-on i,.colorpicker-element .input-group-addon i{display:inline-block;cursor:pointer;height:16px;vertical-align:text-top;width:16px}.colorpicker.colorpicker-inline{position:relative;display:inline-block;float:none;z-index:auto}.colorpicker.colorpicker-horizontal{width:110px;min-width:110px;height:auto}.colorpicker.colorpicker-horizontal .colorpicker-saturation{margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-color{width:100px}.colorpicker.colorpicker-horizontal .colorpicker-alpha,.colorpicker.colorpicker-horizontal .colorpicker-hue{width:100px;height:15px;float:left;cursor:col-resize;margin-left:0;margin-bottom:4px}.colorpicker.colorpicker-horizontal .colorpicker-alpha i,.colorpicker.colorpicker-horizontal .colorpicker-hue i{display:block;height:15px;background:#fff;position:absolute;top:0;left:0;width:1px;border:none;margin-top:0}.colorpicker.colorpicker-horizontal .colorpicker-hue{background-image:url(../img/bootstrap-colorpicker/hue-horizontal.png)}.colorpicker.colorpicker-horizontal .colorpicker-alpha{background-image:url(../img/bootstrap-colorpicker/alpha-horizontal.png)}.colorpicker.colorpicker-hidden{display:none}.colorpicker.colorpicker-visible{display:block}.colorpicker-inline.colorpicker-visible{display:inline-block}.colorpicker-right:before{left:auto;right:6px}.colorpicker-right:after{left:auto;right:7px}.colorpicker-no-arrow:before{border-right:0;border-left:0}.colorpicker-no-arrow:after{border-right:0;border-left:0} -/*# sourceMappingURL=bootstrap-colorpicker.min.css.map */ \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css.map b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css.map deleted file mode 100644 index f61ccb3e2..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/css/bootstrap-colorpicker.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["src/less/colorpicker.less"],"names":[],"mappings":";;;;;;;;AAqBA,wBACE,MAAA,MACA,OAAA,MAXA,iBAAsB,iDAatB,OAAA,UACA,MAAA,KACA,0BACE,QAAA,MACA,OAAA,IACA,MAAA,IACA,OAAA,IAAA,MAAA,KAfF,sBAAA,IACA,mBAAA,IACA,cAAA,IAeE,SAAA,SACA,IAAA,EACA,KAAA,EACA,OAAA,KAAA,EAAA,EAAA,KACA,4BACE,QAAA,MACA,OAAA,IACA,MAAA,IACA,OAAA,IAAA,MAAA,KAzBJ,sBAAA,IACA,mBAAA,IACA,cAAA,IA8BF,mBADA,iBAEE,MAAA,KACA,OAAA,MACA,MAAA,KACA,OAAA,WACA,YAAA,IACA,cAAA,IAIiB,qBADF,mBAEf,QAAA,MACA,OAAA,IACA,WAAA,KACA,WAAA,IAAA,MAAA,KACA,SAAA,SACA,IAAA,EACA,KAAA,EACA,MAAA,KACA,WAAA,KAGF,iBA1DE,iBAAsB,0CA8DxB,mBA9DE,iBAAsB,4CAgEtB,QAAA,KAKF,mBADA,iBADA,wBAGE,gBAAA,QAGF,aACE,QAAA,IACA,UAAA,MACA,WAAA,IAxEA,sBAAA,IACA,mBAAA,IACA,cAAA,IAwEA,QAAA,KAIU,mBADA,oBAEV,QAAA,MACA,QAAA,GACA,YAAA,EAGU,mBACV,MAAA,KAGU,oBACV,QAAA,GACA,QAAA,aACA,YAAA,IAAA,MAAA,YACA,aAAA,IAAA,MAAA,YACA,cAAA,IAAA,MAAA,KACA,oBAAA,eACA,SAAA,SACA,IAAA,KACA,KAAA,IAGU,mBACV,QAAA,GACA,QAAA,aACA,YAAA,IAAA,MAAA,YACA,aAAA,IAAA,MAAA,YACA,cAAA,IAAA,MAAA,KACA,SAAA,SACA,IAAA,KACA,KAAA,IAGW,iBACX,SAAA,SAGU,oCACV,UAAA,MAGkC,uDAClC,QAAA,MAGF,mBACE,OAAA,KACA,WAAA,IACA,MAAA,KAlIA,iBAAsB,4CAoItB,oBAAA,EAAA,KAGiB,uBACjB,OAAA,KAGF,uBACE,QAAA,KACA,OAAA,KACA,WAAA,IACA,MAAA,KAGqB,yBACrB,OAAA,QACA,MAAA,KACA,OAAA,KACA,MAAA,KAGuB,2BACvB,YAAA,IAI2B,+BADW,0CAEtC,QAAA,aACA,OAAA,QACA,OAAA,KACA,eAAA,SACA,MAAA,KAGU,gCACV,SAAA,SACA,QAAA,aACA,MAAA,KACA,QAAA,KAGU,oCACV,MAAA,MACA,UAAA,MACA,OAAA,KAGkC,4DAClC,cAAA,IAGkC,uDAClC,MAAA,MAIkC,uDADA,qDAElC,MAAA,MACA,OAAA,KACA,MAAA,KACA,OAAA,WACA,YAAA,EACA,cAAA,IAIqD,yDADF,uDAEnD,QAAA,MACA,OAAA,KACA,WAAA,KACA,SAAA,SACA,IAAA,EACA,KAAA,EACA,MAAA,IACA,OAAA,KACA,WAAA,EAGkC,qDAlNlC,iBAAsB,qDAsNY,uDAtNlC,iBAAsB,uDA0NZ,gCACV,QAAA,KAGU,iCACV,QAAA,MAGiB,wCACjB,QAAA,aAGgB,0BAChB,KAAA,KACA,MAAA,IAGgB,yBAChB,KAAA,KACA,MAAA,IAGmB,6BACnB,aAAA,EACA,YAAA,EAGmB,4BACnB,aAAA,EACA,YAAA"} \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha-horizontal.png b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha-horizontal.png deleted file mode 100644 index f83188951..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha-horizontal.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha.png b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha.png deleted file mode 100644 index 2e53a30e7..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/alpha.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/hue-horizontal.png b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/hue-horizontal.png deleted file mode 100644 index 3dcd5946a..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/hue-horizontal.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/hue.png b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/hue.png deleted file mode 100644 index 6f5ec2e50..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/hue.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/saturation.png b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/saturation.png deleted file mode 100644 index 170841cba..000000000 Binary files a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/img/bootstrap-colorpicker/saturation.png and /dev/null differ diff --git a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/js/bootstrap-colorpicker.js b/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/js/bootstrap-colorpicker.js deleted file mode 100644 index 758ebc381..000000000 --- a/Plan/common/src/main/resources/assets/plan/web/plugins/bootstrap-colorpicker/js/bootstrap-colorpicker.js +++ /dev/null @@ -1,1106 +0,0 @@ -/*! - * Bootstrap Colorpicker v2.3.3 - * http://mjolnic.github.io/bootstrap-colorpicker/ - * - * Originally written by (c) 2012 Stefan Petre - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0.txt - * - */ - -(function(factory) { - "use strict"; - if (typeof exports === 'object') { - module.exports = factory(window.jQuery); - } else if (typeof define === 'function' && define.amd) { - define(['jquery'], factory); - } else if (window.jQuery && !window.jQuery.fn.colorpicker) { - factory(window.jQuery); - } -}(function($) { - 'use strict'; - - /** - * Color manipulation helper class - * - * @param {Object|String} val - * @param {Object} predefinedColors - * @constructor - */ - var Color = function(val, predefinedColors) { - this.value = { - h: 0, - s: 0, - b: 0, - a: 1 - }; - this.origFormat = null; // original string format - if (predefinedColors) { - $.extend(this.colors, predefinedColors); - } - if (val) { - if (val.toLowerCase !== undefined) { - // cast to string - val = val + ''; - this.setColor(val); - } else if (val.h !== undefined) { - this.value = val; - } - } - }; - - Color.prototype = { - constructor: Color, - // 140 predefined colors from the HTML Colors spec - colors: { - "aliceblue": "#f0f8ff", - "antiquewhite": "#faebd7", - "aqua": "#00ffff", - "aquamarine": "#7fffd4", - "azure": "#f0ffff", - "beige": "#f5f5dc", - "bisque": "#ffe4c4", - "black": "#000000", - "blanchedalmond": "#ffebcd", - "blue": "#0000ff", - "blueviolet": "#8a2be2", - "brown": "#a52a2a", - "burlywood": "#deb887", - "cadetblue": "#5f9ea0", - "chartreuse": "#7fff00", - "chocolate": "#d2691e", - "coral": "#ff7f50", - "cornflowerblue": "#6495ed", - "cornsilk": "#fff8dc", - "crimson": "#dc143c", - "cyan": "#00ffff", - "darkblue": "#00008b", - "darkcyan": "#008b8b", - "darkgoldenrod": "#b8860b", - "darkgray": "#a9a9a9", - "darkgreen": "#006400", - "darkkhaki": "#bdb76b", - "darkmagenta": "#8b008b", - "darkolivegreen": "#556b2f", - "darkorange": "#ff8c00", - "darkorchid": "#9932cc", - "darkred": "#8b0000", - "darksalmon": "#e9967a", - "darkseagreen": "#8fbc8f", - "darkslateblue": "#483d8b", - "darkslategray": "#2f4f4f", - "darkturquoise": "#00ced1", - "darkviolet": "#9400d3", - "deeppink": "#ff1493", - "deepskyblue": "#00bfff", - "dimgray": "#696969", - "dodgerblue": "#1e90ff", - "firebrick": "#b22222", - "floralwhite": "#fffaf0", - "forestgreen": "#228b22", - "fuchsia": "#ff00ff", - "gainsboro": "#dcdcdc", - "ghostwhite": "#f8f8ff", - "gold": "#ffd700", - "goldenrod": "#daa520", - "gray": "#808080", - "green": "#008000", - "greenyellow": "#adff2f", - "honeydew": "#f0fff0", - "hotpink": "#ff69b4", - "indianred": "#cd5c5c", - "indigo": "#4b0082", - "ivory": "#fffff0", - "khaki": "#f0e68c", - "lavender": "#e6e6fa", - "lavenderblush": "#fff0f5", - "lawngreen": "#7cfc00", - "lemonchiffon": "#fffacd", - "lightblue": "#add8e6", - "lightcoral": "#f08080", - "lightcyan": "#e0ffff", - "lightgoldenrodyellow": "#fafad2", - "lightgrey": "#d3d3d3", - "lightgreen": "#90ee90", - "lightpink": "#ffb6c1", - "lightsalmon": "#ffa07a", - "lightseagreen": "#20b2aa", - "lightskyblue": "#87cefa", - "lightslategray": "#778899", - "lightsteelblue": "#b0c4de", - "lightyellow": "#ffffe0", - "lime": "#00ff00", - "limegreen": "#32cd32", - "linen": "#faf0e6", - "magenta": "#ff00ff", - "maroon": "#800000", - "mediumaquamarine": "#66cdaa", - "mediumblue": "#0000cd", - "mediumorchid": "#ba55d3", - "mediumpurple": "#9370d8", - "mediumseagreen": "#3cb371", - "mediumslateblue": "#7b68ee", - "mediumspringgreen": "#00fa9a", - "mediumturquoise": "#48d1cc", - "mediumvioletred": "#c71585", - "midnightblue": "#191970", - "mintcream": "#f5fffa", - "mistyrose": "#ffe4e1", - "moccasin": "#ffe4b5", - "navajowhite": "#ffdead", - "navy": "#000080", - "oldlace": "#fdf5e6", - "olive": "#808000", - "olivedrab": "#6b8e23", - "orange": "#ffa500", - "orangered": "#ff4500", - "orchid": "#da70d6", - "palegoldenrod": "#eee8aa", - "palegreen": "#98fb98", - "paleturquoise": "#afeeee", - "palevioletred": "#d87093", - "papayawhip": "#ffefd5", - "peachpuff": "#ffdab9", - "peru": "#cd853f", - "pink": "#ffc0cb", - "plum": "#dda0dd", - "powderblue": "#b0e0e6", - "purple": "#800080", - "red": "#ff0000", - "rosybrown": "#bc8f8f", - "royalblue": "#4169e1", - "saddlebrown": "#8b4513", - "salmon": "#fa8072", - "sandybrown": "#f4a460", - "seagreen": "#2e8b57", - "seashell": "#fff5ee", - "sienna": "#a0522d", - "silver": "#c0c0c0", - "skyblue": "#87ceeb", - "slateblue": "#6a5acd", - "slategray": "#708090", - "snow": "#fffafa", - "springgreen": "#00ff7f", - "steelblue": "#4682b4", - "tan": "#d2b48c", - "teal": "#008080", - "thistle": "#d8bfd8", - "tomato": "#ff6347", - "turquoise": "#40e0d0", - "violet": "#ee82ee", - "wheat": "#f5deb3", - "white": "#ffffff", - "whitesmoke": "#f5f5f5", - "yellow": "#ffff00", - "yellowgreen": "#9acd32", - "transparent": "transparent" - }, - _sanitizeNumber: function(val) { - if (typeof val === 'number') { - return val; - } - if (isNaN(val) || (val === null) || (val === '') || (val === undefined)) { - return 1; - } - if (val === '') { - return 0; - } - if (val.toLowerCase !== undefined) { - if (val.match(/^\./)) { - val = "0" + val; - } - return Math.ceil(parseFloat(val) * 100) / 100; - } - return 1; - }, - isTransparent: function(strVal) { - if (!strVal) { - return false; - } - strVal = strVal.toLowerCase().trim(); - return (strVal === 'transparent') || (strVal.match(/#?00000000/)) || (strVal.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/)); - }, - rgbaIsTransparent: function(rgba) { - return ((rgba.r === 0) && (rgba.g === 0) && (rgba.b === 0) && (rgba.a === 0)); - }, - //parse a string to HSB - setColor: function(strVal) { - strVal = strVal.toLowerCase().trim(); - if (strVal) { - if (this.isTransparent(strVal)) { - this.value = { - h: 0, - s: 0, - b: 0, - a: 0 - }; - } else { - this.value = this.stringToHSB(strVal) || { - h: 0, - s: 0, - b: 0, - a: 1 - }; // if parser fails, defaults to black - } - } - }, - stringToHSB: function(strVal) { - strVal = strVal.toLowerCase(); - var alias; - if (typeof this.colors[strVal] !== 'undefined') { - strVal = this.colors[strVal]; - alias = 'alias'; - } - var that = this, - result = false; - $.each(this.stringParsers, function(i, parser) { - var match = parser.re.exec(strVal), - values = match && parser.parse.apply(that, [match]), - format = alias || parser.format || 'rgba'; - if (values) { - if (format.match(/hsla?/)) { - result = that.RGBtoHSB.apply(that, that.HSLtoRGB.apply(that, values)); - } else { - result = that.RGBtoHSB.apply(that, values); - } - that.origFormat = format; - return false; - } - return true; - }); - return result; - }, - setHue: function(h) { - this.value.h = 1 - h; - }, - setSaturation: function(s) { - this.value.s = s; - }, - setBrightness: function(b) { - this.value.b = 1 - b; - }, - setAlpha: function(a) { - this.value.a = Math.round((parseInt((1 - a) * 100, 10) / 100) * 100) / 100; - }, - toRGB: function(h, s, b, a) { - if (!h) { - h = this.value.h; - s = this.value.s; - b = this.value.b; - } - h *= 360; - var R, G, B, X, C; - h = (h % 360) / 60; - C = b * s; - X = C * (1 - Math.abs(h % 2 - 1)); - R = G = B = b - C; - - h = ~~h; - R += [C, X, 0, 0, X, C][h]; - G += [X, C, C, X, 0, 0][h]; - B += [0, 0, X, C, C, X][h]; - return { - r: Math.round(R * 255), - g: Math.round(G * 255), - b: Math.round(B * 255), - a: a || this.value.a - }; - }, - toHex: function(h, s, b, a) { - var rgb = this.toRGB(h, s, b, a); - if (this.rgbaIsTransparent(rgb)) { - return 'transparent'; - } - return '#' + ((1 << 24) | (parseInt(rgb.r) << 16) | (parseInt(rgb.g) << 8) | parseInt(rgb.b)).toString(16).substr(1); - }, - toHSL: function(h, s, b, a) { - h = h || this.value.h; - s = s || this.value.s; - b = b || this.value.b; - a = a || this.value.a; - - var H = h, - L = (2 - s) * b, - S = s * b; - if (L > 0 && L <= 1) { - S /= L; - } else { - S /= 2 - L; - } - L /= 2; - if (S > 1) { - S = 1; - } - return { - h: isNaN(H) ? 0 : H, - s: isNaN(S) ? 0 : S, - l: isNaN(L) ? 0 : L, - a: isNaN(a) ? 0 : a - }; - }, - toAlias: function(r, g, b, a) { - var rgb = this.toHex(r, g, b, a); - for (var alias in this.colors) { - if (this.colors[alias] === rgb) { - return alias; - } - } - return false; - }, - RGBtoHSB: function(r, g, b, a) { - r /= 255; - g /= 255; - b /= 255; - - var H, S, V, C; - V = Math.max(r, g, b); - C = V - Math.min(r, g, b); - H = (C === 0 ? null : - V === r ? (g - b) / C : - V === g ? (b - r) / C + 2 : - (r - g) / C + 4 - ); - H = ((H + 360) % 6) * 60 / 360; - S = C === 0 ? 0 : C / V; - return { - h: this._sanitizeNumber(H), - s: S, - b: V, - a: this._sanitizeNumber(a) - }; - }, - HueToRGB: function(p, q, h) { - if (h < 0) { - h += 1; - } else if (h > 1) { - h -= 1; - } - if ((h * 6) < 1) { - return p + (q - p) * h * 6; - } else if ((h * 2) < 1) { - return q; - } else if ((h * 3) < 2) { - return p + (q - p) * ((2 / 3) - h) * 6; - } else { - return p; - } - }, - HSLtoRGB: function(h, s, l, a) { - if (s < 0) { - s = 0; - } - var q; - if (l <= 0.5) { - q = l * (1 + s); - } else { - q = l + s - (l * s); - } - - var p = 2 * l - q; - - var tr = h + (1 / 3); - var tg = h; - var tb = h - (1 / 3); - - var r = Math.round(this.HueToRGB(p, q, tr) * 255); - var g = Math.round(this.HueToRGB(p, q, tg) * 255); - var b = Math.round(this.HueToRGB(p, q, tb) * 255); - return [r, g, b, this._sanitizeNumber(a)]; - }, - toString: function(format) { - format = format || 'rgba'; - var c = false; - switch (format) { - case 'rgb': - { - c = this.toRGB(); - if (this.rgbaIsTransparent(c)) { - return 'transparent'; - } - return 'rgb(' + c.r + ',' + c.g + ',' + c.b + ')'; - } - break; - case 'rgba': - { - c = this.toRGB(); - return 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' + c.a + ')'; - } - break; - case 'hsl': - { - c = this.toHSL(); - return 'hsl(' + Math.round(c.h * 360) + ',' + Math.round(c.s * 100) + '%,' + Math.round(c.l * 100) + '%)'; - } - break; - case 'hsla': - { - c = this.toHSL(); - return 'hsla(' + Math.round(c.h * 360) + ',' + Math.round(c.s * 100) + '%,' + Math.round(c.l * 100) + '%,' + c.a + ')'; - } - break; - case 'hex': - { - return this.toHex(); - } - break; - case 'alias': - return this.toAlias() || this.toHex(); - default: - { - return c; - } - break; - } - }, - // a set of RE's that can match strings and generate color tuples. - // from John Resig color plugin - // https://github.com/jquery/jquery-color/ - stringParsers: [{ - re: /rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/, - format: 'rgb', - parse: function(execResult) { - return [ - execResult[1], - execResult[2], - execResult[3], - 1 - ]; - } - }, { - re: /rgb\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/, - format: 'rgb', - parse: function(execResult) { - return [ - 2.55 * execResult[1], - 2.55 * execResult[2], - 2.55 * execResult[3], - 1 - ]; - } - }, { - re: /rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/, - format: 'rgba', - parse: function(execResult) { - return [ - execResult[1], - execResult[2], - execResult[3], - execResult[4] - ]; - } - }, { - re: /rgba\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/, - format: 'rgba', - parse: function(execResult) { - return [ - 2.55 * execResult[1], - 2.55 * execResult[2], - 2.55 * execResult[3], - execResult[4] - ]; - } - }, { - re: /hsl\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/, - format: 'hsl', - parse: function(execResult) { - return [ - execResult[1] / 360, - execResult[2] / 100, - execResult[3] / 100, - execResult[4] - ]; - } - }, { - re: /hsla\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/, - format: 'hsla', - parse: function(execResult) { - return [ - execResult[1] / 360, - execResult[2] / 100, - execResult[3] / 100, - execResult[4] - ]; - } - }, { - re: /#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/, - format: 'hex', - parse: function(execResult) { - return [ - parseInt(execResult[1], 16), - parseInt(execResult[2], 16), - parseInt(execResult[3], 16), - 1 - ]; - } - }, { - re: /#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/, - format: 'hex', - parse: function(execResult) { - return [ - parseInt(execResult[1] + execResult[1], 16), - parseInt(execResult[2] + execResult[2], 16), - parseInt(execResult[3] + execResult[3], 16), - 1 - ]; - } - }], - colorNameToHex: function(name) { - if (typeof this.colors[name.toLowerCase()] !== 'undefined') { - return this.colors[name.toLowerCase()]; - } - return false; - } - }; - - /* - * Default plugin options - */ - var defaults = { - horizontal: false, // horizontal mode layout ? - inline: false, //forces to show the colorpicker as an inline element - color: false, //forces a color - format: false, //forces a format - input: 'input', // children input selector - container: false, // container selector - component: '.add-on, .input-group-addon', // children component selector - sliders: { - saturation: { - maxLeft: 100, - maxTop: 100, - callLeft: 'setSaturation', - callTop: 'setBrightness' - }, - hue: { - maxLeft: 0, - maxTop: 100, - callLeft: false, - callTop: 'setHue' - }, - alpha: { - maxLeft: 0, - maxTop: 100, - callLeft: false, - callTop: 'setAlpha' - } - }, - slidersHorz: { - saturation: { - maxLeft: 100, - maxTop: 100, - callLeft: 'setSaturation', - callTop: 'setBrightness' - }, - hue: { - maxLeft: 100, - maxTop: 0, - callLeft: 'setHue', - callTop: false - }, - alpha: { - maxLeft: 100, - maxTop: 0, - callLeft: 'setAlpha', - callTop: false - } - }, - template: '