/*
* Copyright 2003-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.ide.blame.dialog;
import com.intellij.credentialStore.CredentialAttributesKt;
import com.intellij.credentialStore.Credentials;
import com.intellij.diagnostic.DiagnosticBundle;
import com.intellij.diagnostic.ErrorReportConfigurable;
import com.intellij.diagnostic.JetBrainsAccountDialogKt;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ex.ApplicationInfoEx;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.DimensionService;
import com.intellij.ui.HyperlinkLabel;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBTextField;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import jetbrains.mps.ide.blame.command.Command;
import jetbrains.mps.ide.blame.command.Poster;
import jetbrains.mps.ide.blame.perform.Query;
import jetbrains.mps.ide.blame.perform.Response;
import jetbrains.mps.util.annotation.ToRemove;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.HyperlinkEvent;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Insets;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
public class BlameDialog extends DialogWrapper {
private static final String CAPTION = "Submit System Exception to Developers";
private JPanel myPanel;
private JTextField myTitleField;
private JTextArea myDescriptionField;
private JPanel myExceptionContainer;
private JTextArea myException;
private JCheckBox myHiddenCheckBox;
private HyperlinkLabel myCredentialsLabel;
private boolean myIsCancelled = true;
private Response myResult;
private Project myProject;
private String myTitle = "";
private String myDescription = "";
private List<Throwable> myThrowableList = new ArrayList<>();
private List<File> myFilesToAttach = new ArrayList<>();
private String mySubsystem = null;
private PluginDescriptor myPluginDescriptor;
public BlameDialog(Project project, Dialog dialog) {
super(dialog, true);
myProject = project;
}
public BlameDialog(Project project, Frame mainFrame) {
super(mainFrame, true);
myProject = project;
}
public void addEx(Throwable throwable) {
myThrowableList.add(throwable);
}
public void setIssueTitle(String message) {
myTitle = message;
}
public void setDescription(String message) {
myDescription = message;
}
public void addFile(@NotNull File file) {
if (file.exists()) {
myFilesToAttach.add(file);
}
}
public void setIssueHidden(boolean hidden) {
myHiddenCheckBox.setSelected(hidden);
}
public void setSubsystem(String subsystem) {
mySubsystem = subsystem;
}
public void setPluginDescriptor(PluginDescriptor pluginDescriptor) {
myPluginDescriptor = pluginDescriptor;
}
@Deprecated /*Unused method*/
@ToRemove(version = 2017.1)
public void setSourceRevision(String sourceRevision) {
}
@Override
protected JComponent createCenterPanel() {
myPanel = new JPanel(new GridLayoutManager(7, 1, new Insets(0, 0, 0, 0), -1, -1));
myPanel.add(new JBLabel("Title:"), getConstraints(myPanel.getComponentCount()));
myTitleField = new JBTextField();
myPanel.add(myTitleField, getConstraints(myPanel.getComponentCount()));
myPanel.add(new JBLabel(DiagnosticBundle.message("error.dialog.comment.prompt")), getConstraints(myPanel.getComponentCount()));
myDescriptionField = new JTextArea();
myDescriptionField.setEditable(true);
myDescriptionField.setRows(2);
JBScrollPane descriptionScrollPane = new JBScrollPane();
descriptionScrollPane.setViewportView(myDescriptionField);
final GridConstraints descriptionConstraints = getConstraints(myPanel.getComponentCount());
descriptionConstraints.setAnchor(GridConstraints.ANCHOR_CENTER);
descriptionConstraints.setFill(GridConstraints.FILL_BOTH);
descriptionConstraints.setVSizePolicy(GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
myPanel.add(descriptionScrollPane, descriptionConstraints);
myExceptionContainer = new JPanel(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));
myExceptionContainer.add(new JBLabel("Exception:"), getConstraints(myExceptionContainer.getComponentCount()));
myException = new JTextArea();
myException.setEditable(false);
final JBScrollPane exceptionScrollPane = new JBScrollPane();
exceptionScrollPane.setViewportView(myException);
myExceptionContainer.add(exceptionScrollPane, getConstraints(myExceptionContainer.getComponentCount()));
myPanel.add(myExceptionContainer, getConstraints(myPanel.getComponentCount()));
myHiddenCheckBox = new JBCheckBox("Visible only to MPS developers");
myPanel.add(myHiddenCheckBox, getConstraints(myPanel.getComponentCount()));
myHiddenCheckBox.setToolTipText("Select this if you want this bug report will be visible only to you and MPS developers");
myCredentialsLabel = new HyperlinkLabel();
myCredentialsLabel.addHyperlinkListener(e -> {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
JetBrainsAccountDialogKt.showJetBrainsAccountDialog(getRootPane()).show();
updateCredentialsPane();
}
});
final GridConstraints credentialsConstraints = getConstraints(myPanel.getComponentCount());
credentialsConstraints.setAnchor(GridConstraints.ANCHOR_EAST);
credentialsConstraints.setFill(GridConstraints.FILL_NONE);
myPanel.add(myCredentialsLabel, credentialsConstraints);
return myPanel;
}
private GridConstraints getConstraints(int row) {
return new GridConstraints(row, 0, 1, 1,
GridConstraints.ANCHOR_WEST,
GridConstraints.FILL_HORIZONTAL,
GridConstraints.SIZEPOLICY_CAN_SHRINK,
GridConstraints.SIZEPOLICY_CAN_SHRINK,
null, null, null);
}
@Nullable
@Override
public JComponent getPreferredFocusedComponent() {
return myDescriptionField;
}
private void updateCredentialsPane() {
Credentials credentials = ErrorReportConfigurable.getCredentials();
if (CredentialAttributesKt.isFulfilled(credentials)) {
assert credentials != null;
myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.report.as", credentials.getUserName()));
} else {
myCredentialsLabel.setHtmlText(DiagnosticBundle.message("diagnostic.error.report.submit.error.anonymously"));
}
}
/**
* This method initialize UI components and update them with data from setters.<br/>
* Must be called before {@link DialogWrapper#show()} method.
*/
public void initDialog() {
setTitle(CAPTION);
setModal(true);
init();
myTitleField.setText(myTitle);
myDescriptionField.setText(myDescription);
myExceptionContainer.setVisible(!myThrowableList.isEmpty());
if (!myThrowableList.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (Throwable throwable : myThrowableList) {
builder.append(ex2str(throwable)).append("\n\n");
}
myException.setText(builder.toString());
}
updateCredentialsPane();
Dimension size = DimensionService.getInstance().getSize(getDimensionServiceKey());
if (size == null) {
myPanel.setPreferredSize(new Dimension(750, 550));
}
setOKButtonText("Send");
setOKButtonMnemonic('S');
}
@Override
@NotNull
protected String getDimensionServiceKey() {
return getClass().getName();
}
private Query createQuery() {
Credentials credentials = ErrorReportConfigurable.getCredentials();
return CredentialAttributesKt.isFulfilled(credentials) ?
new Query(credentials.getUserName(), credentials.getPasswordAsString()) : Query.getAnonymousQuery();
}
private String ex2str(Throwable e) {
if (e == null) {
return "";
}
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return (e.getMessage() == null ? "" : e.getMessage() + "\n") + sw.toString();
}
private String getAdditionalInfo() {
ApplicationInfo ai = ApplicationInfo.getInstance();
StringBuilder builder = new StringBuilder("{cut [Build info]}*[Build info]*\n");
if (ai instanceof ApplicationInfoEx) {
builder.append("Application name: ''").append(((ApplicationInfoEx) ai).getFullApplicationName()).append("''\n");
}
builder.append("Build number: ''").append(ai.getBuild().asString()).append("''\n");
builder.append("Version: ''").append(ai.getFullVersion()).append("''\n");
if (myPluginDescriptor != null) {
builder.append("*[Plugin info]*").append("\n");
builder.append("Plugin id: ''").append(myPluginDescriptor.getPluginId()).append("''\n");
if (myPluginDescriptor instanceof IdeaPluginDescriptor) {
final IdeaPluginDescriptor pluginDescriptor = (IdeaPluginDescriptor) myPluginDescriptor;
builder.append("Name: ''").append(pluginDescriptor.getName()).append("''\n");
builder.append("Version: ''").append(pluginDescriptor.getVersion()).append("''\n");
builder.append("Vendor: ''").append(pluginDescriptor.getVendor()).append("''\n");
builder.append("Category: ''").append(pluginDescriptor.getCategory()).append("''\n");
builder.append("Is bundled: ''").append(pluginDescriptor.isBundled()).append("''\n");
builder.append("Is enabled: ''").append(pluginDescriptor.isEnabled()).append("''\n");
}
}
builder.append("{cut}");
return builder.toString();
}
protected JComponent getMainComponent() {
return myPanel;
}
public boolean isCancelled() {
return myIsCancelled;
}
public Response getResult() {
return myResult;
}
@Override
protected void doOKAction() {
StringBuilder description = new StringBuilder(myTitleField.getText().length() + myDescriptionField.getText().length() + 1000);
if (myTitleField.getText().trim().length() != 0) {
description.append(myTitleField.getText());
description.append("\n\n");
}
if (myDescriptionField.getText().trim().length() != 0) {
description.append(myDescriptionField.getText());
description.append("\n\n");
}
description.append(getAdditionalInfo());
description.append("\n");
if (!myThrowableList.isEmpty()) {
description.append("{code}");
for (Throwable ex : myThrowableList) {
description.append(ex2str(ex)).append("\n");
}
description.append("{code}");
}
Poster poster = new Poster(myProject);
Query query = createQuery();
query.setIssueTitle(myTitleField.getText());
query.setDescription(description.toString());
query.setFiles(myFilesToAttach.toArray(new File[myFilesToAttach.size()]));
query.setHidden(myHiddenCheckBox.isSelected());
query.setSubsystem(mySubsystem);
myResult = poster.send(query);
if (!myResult.isSuccess()) {
String message = myResult.getMessage();
String response = myResult.getResponseString();
if (response != null && !response.equals("")) {
Element responseXml = myResult.getResponseXml();
if (responseXml != null && "error".equalsIgnoreCase(responseXml.getName())) {
message += ". " + responseXml.getText();
} else {
message += ". " + response;
}
}
Messages.showErrorDialog(BlameDialog.this.getOwner(), message, "Error");
return;
}
openIssueInBrowser();
myIsCancelled = false;
close(DialogWrapper.OK_EXIT_CODE);
}
private void openIssueInBrowser() {
String id = myResult.getIssueId();
if (id != null) {
BrowserUtil.browse(Command.ISSUE_BASE_URL + id);
}
}
@Override
public void doCancelAction() {
myResult = null;
myIsCancelled = true;
close(DialogWrapper.CANCEL_EXIT_CODE);
}
}