From f4a3bfed97b86b9377f8fd0a3f17690f9a55558d Mon Sep 17 00:00:00 2001 From: delvh Date: Sun, 16 Aug 2020 17:14:41 +0200 Subject: [PATCH] Added option to autocreate bug issues on client and server side Additionally cleaned up a few classes a bit --- client/.classpath | 1 - client/src/main/java/envoy/client/Main.java | 11 ++- .../client/ui/controller/SettingsScene.java | 17 ++-- .../client/ui/settings/BugReportPane.java | 76 ++++++++++++++++ .../ui/settings/OnlyIfOnlineSettingsPane.java | 51 +++++++++++ .../client/ui/settings/UserSettingsPane.java | 18 +--- .../envoy/client/util/ReflectionUtil.java | 1 - .../main/resources/fxml/SettingsScene.fxml | 2 +- .../main/java/envoy/event/IssueProposal.java | 57 ++++++++++++ .../src/main/java/envoy/server/Startup.java | 7 +- .../processors/IssueProposalProcessor.java | 88 +++++++++++++++++++ 11 files changed, 299 insertions(+), 30 deletions(-) create mode 100644 client/src/main/java/envoy/client/ui/settings/BugReportPane.java create mode 100644 client/src/main/java/envoy/client/ui/settings/OnlyIfOnlineSettingsPane.java create mode 100644 common/src/main/java/envoy/event/IssueProposal.java create mode 100644 server/src/main/java/envoy/server/processors/IssueProposalProcessor.java diff --git a/client/.classpath b/client/.classpath index 99d4cc0..4328dab 100644 --- a/client/.classpath +++ b/client/.classpath @@ -20,7 +20,6 @@ - diff --git a/client/src/main/java/envoy/client/Main.java b/client/src/main/java/envoy/client/Main.java index 57f2d6d..189d1f3 100644 --- a/client/src/main/java/envoy/client/Main.java +++ b/client/src/main/java/envoy/client/Main.java @@ -19,6 +19,8 @@ import envoy.client.ui.Startup; */ public class Main { + private static final boolean DEBUG = false; + /** * Starts the application. * @@ -26,5 +28,12 @@ public class Main { * client configuration * @since Envoy Client v0.1-beta */ - public static void main(String[] args) { Application.launch(Startup.class, args); } + public static void main(String[] args) { + if (DEBUG) { + // Put testing code here + System.out.println(); + System.exit(0); + } + Application.launch(Startup.class, args); + } } diff --git a/client/src/main/java/envoy/client/ui/controller/SettingsScene.java b/client/src/main/java/envoy/client/ui/controller/SettingsScene.java index 5656588..e800574 100644 --- a/client/src/main/java/envoy/client/ui/controller/SettingsScene.java +++ b/client/src/main/java/envoy/client/ui/controller/SettingsScene.java @@ -1,10 +1,13 @@ package envoy.client.ui.controller; import javafx.fxml.FXML; -import javafx.scene.control.*; +import javafx.scene.control.Label; +import javafx.scene.control.ListView; +import javafx.scene.control.TitledPane; import envoy.client.net.Client; import envoy.client.ui.SceneContext; +import envoy.client.ui.listcell.AbstractListCell; import envoy.client.ui.settings.*; /** @@ -33,20 +36,20 @@ public class SettingsScene { */ public void initializeData(SceneContext sceneContext, Client client) { this.sceneContext = sceneContext; + final var user = client.getSender(); + final var online = client.isOnline(); settingsList.getItems().add(new GeneralSettingsPane()); - settingsList.getItems().add(new UserSettingsPane(sceneContext, client.getSender(), client.isOnline())); + settingsList.getItems().add(new UserSettingsPane(sceneContext, user, online)); settingsList.getItems().add(new DownloadSettingsPane(sceneContext)); + settingsList.getItems().add(new BugReportPane( user, online)); } @FXML private void initialize() { - settingsList.setCellFactory(listView -> new ListCell<>() { + settingsList.setCellFactory(listView -> new AbstractListCell<>(listView) { @Override - protected void updateItem(SettingsPane item, boolean empty) { - super.updateItem(item, empty); - if (!empty && item != null) setGraphic(new Label(item.getTitle())); - } + protected Label renderItem(SettingsPane item) { return new Label(item.getTitle()); } }); } diff --git a/client/src/main/java/envoy/client/ui/settings/BugReportPane.java b/client/src/main/java/envoy/client/ui/settings/BugReportPane.java new file mode 100644 index 0000000..bbc8295 --- /dev/null +++ b/client/src/main/java/envoy/client/ui/settings/BugReportPane.java @@ -0,0 +1,76 @@ +package envoy.client.ui.settings; + +import javafx.event.EventHandler; +import javafx.scene.control.*; +import javafx.scene.input.InputEvent; + +import envoy.client.event.SendEvent; +import envoy.data.User; +import envoy.event.EventBus; +import envoy.event.IssueProposal; + +/** + * This class offers the option for users to submit a bug report. Only the title + * of a bug is needed to be sent. + *

+ * Project: client
+ * File: BugReportPane.java
+ * Created: Aug 4, 2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.2-beta + */ +public class BugReportPane extends OnlyIfOnlineSettingsPane { + + private final Label titleLabel = new Label("Suggest a title for the bug:"); + private final TextField titleTextField = new TextField(); + private final Label pleaseExplainLabel = new Label("Paste here the log of what went wrong and/ or explain what went wrong:"); + private final TextArea errorDetailArea = new TextArea(); + private final CheckBox showUsernameInBugReport = new CheckBox("Show your username in the bug report?"); + private final Button submitReportButton = new Button("Submit report"); + + private final EventHandler inputEventHandler = e -> submitReportButton.setDisable(titleTextField.getText().isBlank()); + + /** + * Creates a new {@code BugReportPane}. + * + * @param user the user whose details to use + * @param online whether this user is currently online + * @since Envoy Client v0.2-beta + */ + public BugReportPane(User user, boolean online) { + super("Report a bug", online); + setSpacing(10); + setToolTipText("A bug can only be reported when being online"); + + // Displaying the label to ask for a title + titleLabel.setWrapText(true); + getChildren().add(titleLabel); + + // Displaying the TextField where to enter the title of this bug + titleTextField.setOnKeyTyped(inputEventHandler); + titleTextField.setOnInputMethodTextChanged(inputEventHandler); + getChildren().add(titleTextField); + + // Displaying the label to ask for clarification + pleaseExplainLabel.setWrapText(true); + getChildren().add(pleaseExplainLabel); + + // Displaying the TextArea where to enter the log and/or own description + errorDetailArea.setWrapText(true); + getChildren().add(errorDetailArea); + + // Displaying the consent button that your user name will be shown + showUsernameInBugReport.setSelected(true); + getChildren().add(showUsernameInBugReport); + + // Displaying the submitReportButton + submitReportButton.setDisable(true); + submitReportButton.setOnAction(e -> { + EventBus.getInstance() + .dispatch(new SendEvent(new IssueProposal(titleTextField.getText(), errorDetailArea.getText(), + showUsernameInBugReport.isSelected() ? user.getName() : null, true))); + }); + getChildren().add(submitReportButton); + } +} diff --git a/client/src/main/java/envoy/client/ui/settings/OnlyIfOnlineSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/OnlyIfOnlineSettingsPane.java new file mode 100644 index 0000000..7e16792 --- /dev/null +++ b/client/src/main/java/envoy/client/ui/settings/OnlyIfOnlineSettingsPane.java @@ -0,0 +1,51 @@ +package envoy.client.ui.settings; + +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.CornerRadii; +import javafx.scene.paint.Color; + +/** + * Inheriting from this class signifies that options should only be available if + * the {@link envoy.data.User} is currently online. If the user is currently + * offline, all {@link javafx.scene.Node} variables will be disabled and a + * {@link Tooltip} will be displayed for the whole node. + *

+ * Project: client
+ * File: OnlyIfOnlineSettingsPane.java
+ * Created: Aug 4, 2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.2-beta + */ +public abstract class OnlyIfOnlineSettingsPane extends SettingsPane { + + private final Tooltip beOnlineReminder = new Tooltip("You need to be online to modify your acount."); + + /** + * @param title + * @since Envoy Client v0.2-beta + */ + protected OnlyIfOnlineSettingsPane(String title, boolean online) { + super(title); + + final var offline = !online; + + setDisable(offline); + + if (offline) { + final var infoLabel = new Label("You shall not pass!\n(... Unless you would happen to be online)"); + infoLabel.setId("infoLabel-warning"); + infoLabel.setWrapText(true); + getChildren().add(infoLabel); + setBackground(new Background(new BackgroundFill(Color.grayRgb(100, 0.3), CornerRadii.EMPTY, Insets.EMPTY))); + + Tooltip.install(this, beOnlineReminder); + } else Tooltip.uninstall(this, beOnlineReminder); + } + + protected void setToolTipText(String text) { beOnlineReminder.setText(text); } +} diff --git a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java index cad265b..098abac 100644 --- a/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java +++ b/client/src/main/java/envoy/client/ui/settings/UserSettingsPane.java @@ -22,7 +22,6 @@ import envoy.client.event.SendEvent; import envoy.client.ui.IconUtil; import envoy.client.ui.SceneContext; import envoy.client.ui.custom.ProfilePicImageView; -import envoy.client.util.ReflectionUtil; import envoy.data.User; import envoy.event.*; import envoy.util.Bounds; @@ -36,7 +35,7 @@ import envoy.util.EnvoyLog; * @author Leon Hofmeister * @since Envoy Client v0.2-beta */ -public class UserSettingsPane extends SettingsPane { +public class UserSettingsPane extends OnlyIfOnlineSettingsPane { private boolean profilePicChanged, usernameChanged, validPassword; private byte[] currentImageBytes; @@ -49,8 +48,6 @@ public class UserSettingsPane extends SettingsPane { private final PasswordField repeatNewPasswordField = new PasswordField(); private final Button saveButton = new Button("Save"); - private final Tooltip beOnlineReminder = new Tooltip("You need to be online to modify your acount."); - private static final EventBus eventBus = EventBus.getInstance(); private static final Logger logger = EnvoyLog.getLogger(UserSettingsPane.class); @@ -63,7 +60,7 @@ public class UserSettingsPane extends SettingsPane { * @since Envoy Client v0.2-beta */ public UserSettingsPane(SceneContext sceneContext, User user, boolean online) { - super("User"); + super("User", online); setSpacing(10); // Display of profile pic change mechanism @@ -143,17 +140,6 @@ public class UserSettingsPane extends SettingsPane { saveButton.setOnAction(e -> save(user.getID(), currentPasswordField.getText())); saveButton.setAlignment(Pos.BOTTOM_RIGHT); getChildren().add(saveButton); - - final var offline = !online; - ReflectionUtil.getAllDeclaredNodeVariables(this).forEach(node -> node.setDisable(offline)); - if (offline) { - final var infoLabel = new Label("You shall not pass!\n(... Unless you would happen to be online)"); - infoLabel.setId("infoLabel-warning"); - infoLabel.setWrapText(true); - getChildren().add(infoLabel); - - Tooltip.install(this, beOnlineReminder); - } else Tooltip.uninstall(this, beOnlineReminder); } /** diff --git a/client/src/main/java/envoy/client/util/ReflectionUtil.java b/client/src/main/java/envoy/client/util/ReflectionUtil.java index 132f5da..92c243a 100644 --- a/client/src/main/java/envoy/client/util/ReflectionUtil.java +++ b/client/src/main/java/envoy/client/util/ReflectionUtil.java @@ -42,7 +42,6 @@ public class ReflectionUtil { try { field.setAccessible(true); final var value = field.get(instance); - field.setAccessible(false); return value; } catch (IllegalArgumentException | IllegalAccessException e) { throw new RuntimeException(e); diff --git a/client/src/main/resources/fxml/SettingsScene.fxml b/client/src/main/resources/fxml/SettingsScene.fxml index 8474c1d..fdf4a8f 100644 --- a/client/src/main/resources/fxml/SettingsScene.fxml +++ b/client/src/main/resources/fxml/SettingsScene.fxml @@ -30,7 +30,7 @@ + prefHeight="400.0" prefWidth="400.0"> diff --git a/common/src/main/java/envoy/event/IssueProposal.java b/common/src/main/java/envoy/event/IssueProposal.java new file mode 100644 index 0000000..1514689 --- /dev/null +++ b/common/src/main/java/envoy/event/IssueProposal.java @@ -0,0 +1,57 @@ +package envoy.event; + +/** + * This class allows envoy users to send an issue proposal to the server who, if + * not disabled by its admin, will forward it directly to gitea. + *

+ * Project: common
+ * File: IssueProposal.java
+ * Created: Aug 5, 2020
+ * + * @author Leon Hofmeister + * @since Envoy Common v0.2-beta + */ +public class IssueProposal extends Event { + + private final String submitterName; + private final String description; + private final boolean bug; + + private static final long serialVersionUID = 1L; + + /** + * @param title the title of the reported bug + * @param description the description of this bug + * @param submitterName the user who submitted the bug + * @param isBug determines whether this {@code IssueProposal} is + * supposed to be a + * feature or a bug (true = bug, false = feature) + * @since Envoy Common v0.2-beta + */ + public IssueProposal(String title, String description, String submitterName, boolean isBug) { + super(title); + this.submitterName = submitterName; + this.description = description; + bug = isBug; + } + + /** + * @return the description + * @since Envoy Common v0.2-beta + */ + public String getDescription() { return description; } + + /** + * @return the name of the user who sent this bug report + * @since Envoy Common v0.2-beta + */ + public String getSubmitterName() { return submitterName; } + + /** + * @return whether this issue is supposed to be a bug - if false it is intended + * as a feature + * @since Envoy Common v0.2-beta + */ + public boolean isBug() { return bug; } + +} diff --git a/server/src/main/java/envoy/server/Startup.java b/server/src/main/java/envoy/server/Startup.java index c131fce..84b569c 100755 --- a/server/src/main/java/envoy/server/Startup.java +++ b/server/src/main/java/envoy/server/Startup.java @@ -59,7 +59,7 @@ public class Startup { public static void main(String[] args) throws IOException { initLogging(); - final Server server = new Server(8080, ObjectMessageReader::new, + final var server = new Server(8080, ObjectMessageReader::new, new ObjectMessageProcessor(Set.of(new LoginCredentialProcessor(), new MessageProcessor(), new GroupMessageProcessor(), @@ -73,10 +73,11 @@ public class Startup { new IsTypingProcessor(), new NameChangeProcessor(), new ProfilePicChangeProcessor(), - new PasswordChangeRequestProcessor()))); + new PasswordChangeRequestProcessor(), + new IssueProposalProcessor()))); // Initialize the current message ID - final PersistenceManager persistenceManager = PersistenceManager.getInstance(); + final var persistenceManager = PersistenceManager.getInstance(); if (persistenceManager.getConfigItemByID("currentMessageId") == null) persistenceManager.addConfigItem(new envoy.server.data.ConfigItem("currentMessageId", "0")); diff --git a/server/src/main/java/envoy/server/processors/IssueProposalProcessor.java b/server/src/main/java/envoy/server/processors/IssueProposalProcessor.java new file mode 100644 index 0000000..9a1201a --- /dev/null +++ b/server/src/main/java/envoy/server/processors/IssueProposalProcessor.java @@ -0,0 +1,88 @@ +package envoy.server.processors; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; + +import envoy.event.IssueProposal; +import envoy.server.net.ObjectWriteProxy; +import envoy.util.EnvoyLog; + +/** + * This processor handles incoming {@link IssueProposal}s and automatically + * creates a new issue on the gitea site, if not disabled by its administrator. + *

+ * Project: server
+ * File: IssueProposalProcessor.java
+ * Created: Aug 5, 2020
+ * + * @author Leon Hofmeister + * @since Envoy Server v0.2-beta + */ +public class IssueProposalProcessor implements ObjectProcessor { + + private static boolean issueReportingEnabled = true; + private static final Logger logger = EnvoyLog.getLogger(IssueProposalProcessor.class); + + @Override + public void process(IssueProposal issueProposal, long socketID, ObjectWriteProxy writeProxy) throws IOException { + // Do nothing if manually disabled + if (!issueReportingEnabled) return; + var issueDescription = issueProposal.getDescription(); + // Appending the submitter name, if this option was enabled + issueDescription += issueProposal.getSubmitterName() != null + ? (issueDescription.endsWith("\n") || issueDescription.endsWith("
") ? "" : "
") + + String.format("Submitted by user %s.", issueProposal.getSubmitterName()) + : ""; + // Markdown does not support "\n". It uses "
" + issueDescription = issueDescription.replaceAll("\n", "
"); + // We do not want any Windows artifacts to remain as that may cause problems + issueDescription = issueDescription.replaceAll("\r", ""); + try { + final var url = new URL( + "https://git.kske.dev/api/v1/repos/zdm/envoy/issues?access_token=6d8ec2a72d64cbaf6319434aa2e7caf0130701b3"); + final var connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/json; utf-8"); + connection.setRequestProperty("Accept", "application/json"); + connection.setDoOutput(true); + + final var json = String.format("{\"title\":\"%s\",\"body\":\"%s\",\"labels\":[240, %d]}", + issueProposal.get(), + issueDescription, + // Label 240 should be user-made, label 117 bug and label 119 feature + issueProposal.isBug() ? 117 : 119); + try (final var os = connection.getOutputStream()) { + final byte[] input = json.getBytes("utf-8"); + os.write(input, 0, input.length); + } + final var status = connection.getResponseCode(); + if (status == 201) logger.log(Level.INFO, "Successfully created an issue"); + else logger.log(Level.WARNING, + String.format("Tried creating an issue but received status code %d - Request params:title=%s,description=%s,json=%s", + status, + issueProposal.get(), + issueDescription, + json)); + } catch (final IOException e) { + logger.log(Level.WARNING, "An error occurred while creating an issue: ", e); + } + } + + /** + * @return whether issue reporting is enabled + * @since Envoy Server v0.2-beta + */ + public static boolean isIssueReportingEnabled() { return issueReportingEnabled; } + + /** + * @param issueReportingEnabled whether issue reporting should be enabled - true + * by default + * @since Envoy Server v0.2-beta + */ + public static void setIssueReportingEnabled(boolean issueReportingEnabled) { + IssueProposalProcessor.issueReportingEnabled = issueReportingEnabled; + } +}