/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.dialog;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import com.rapidminer.RapidMiner;
import com.rapidminer.gui.RapidMinerGUI;
import com.rapidminer.gui.tools.ExtendedJScrollPane;
import com.rapidminer.gui.tools.ProgressThread;
import com.rapidminer.gui.tools.ResourceAction;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.gui.tools.dialogs.ButtonDialog;
import com.rapidminer.gui.tools.dialogs.ConfirmDialog;
import com.rapidminer.tools.BugReport;
import com.rapidminer.tools.I18N;
import com.rapidminer.tools.LogService;
import com.rapidminer.tools.XmlRpcHandler;
/**
* This dialog is shown in cases where a non-user error occured and the user
* decided to send a bugreport. Collects all necessary data for bug fixing and
* sends it to the RapidMiner BugZilla tracker.
*
* @author Marco Boeck
*/
public class BugZillaAssistant extends ButtonDialog {
/**
* Class to create the dialog which shows the complete bugreport.
*/
private static class BugReportViewerDialog extends ButtonDialog {
private static final long serialVersionUID = -6701934505993719826L;
private JTextArea propArea;
private JScrollPane scrollPanePropArea;
public BugReportViewerDialog() {
super("send_bugreport_info", true);
final JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
Collection<AbstractButton> buttons = new LinkedList<AbstractButton>();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.insets = new Insets(GAP*2, GAP, GAP*2, GAP);
gbc.fill = GridBagConstraints.BOTH;
propArea = new JTextArea();
propArea.setEditable(false);
scrollPanePropArea = new JScrollPane(propArea);
panel.add(scrollPanePropArea, gbc);
buttons.add(makeCloseButton());
layoutDefault(panel, LARGE, buttons);
}
public void setInfoText(String text) {
propArea.setText(text);
propArea.setSelectionStart(0);
propArea.setSelectionEnd(0);
}
}
private XmlRpcClient rpcClient = null;
private final String descriptionText = I18N.getMessage(I18N.getGUIBundle(), getKey() + ".description.text");
private String email;
private char[] pawo;
private final JTextArea descriptionField = new JTextArea(descriptionText, 5, 20);
private final JList attachments = new JList(new DefaultListModel());
private JButton submitButton;
private static final long serialVersionUID = 8379605320787188372L;
public BugZillaAssistant(ProgressThread thread, final Throwable exception, final XmlRpcClient client) throws XmlRpcException {
super("send_bugreport", true);
rpcClient = client;
thread.getProgressListener().setCompleted(35);
if (thread.isCancelled()) {
return;
}
// gather information to fill out combo boxes
Object[] compVals, severityVals, platformVals, osVals;
// components
Map<String, String> valQueryMap = new HashMap<String, String>();
valQueryMap.put("field", "component");
valQueryMap.put("product_id", "2");
Map resultMap = (Map)rpcClient.execute("Bug.legal_values", new Object[]{ valQueryMap });
compVals = (Object[])resultMap.get("values");
thread.getProgressListener().setCompleted(50);
if (thread.isCancelled()) {
return;
}
// severity
valQueryMap = new HashMap<String, String>();
valQueryMap.put("field", "severity");
valQueryMap.put("product_id", "2");
resultMap = (Map)rpcClient.execute("Bug.legal_values", new Object[]{ valQueryMap });
severityVals = (Object[])resultMap.get("values");
thread.getProgressListener().setCompleted(65);
if (thread.isCancelled()) {
return;
}
// platform
valQueryMap = new HashMap<String, String>();
valQueryMap.put("field", "platform");
valQueryMap.put("product_id", "2");
resultMap = (Map)rpcClient.execute("Bug.legal_values", new Object[]{ valQueryMap });
platformVals = (Object[])resultMap.get("values");
thread.getProgressListener().setCompleted(80);
if (thread.isCancelled()) {
return;
}
// operating system
valQueryMap = new HashMap<String, String>();
valQueryMap.put("field", "op_sys");
valQueryMap.put("product_id", "2");
resultMap = (Map)rpcClient.execute("Bug.legal_values", new Object[]{ valQueryMap });
osVals = (Object[])resultMap.get("values");
thread.getProgressListener().setCompleted(95);
if (thread.isCancelled()) {
return;
}
Collection<AbstractButton> buttons = new LinkedList<AbstractButton>();
final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
final JPanel loginPanel = new JPanel();
loginPanel.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0;
gbc.weighty = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(GAP, 0, 0, GAP);
JLabel loginLabel = new JLabel(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".login_e_mail.label"));
loginPanel.add(loginLabel, gbc);
final JTextField loginName = new JTextField(15);
loginName.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".login_e_mail.tip"));
gbc.gridx = 1;
gbc.weightx = 1;
loginPanel.add(loginName, gbc);
JLabel passwordLabel = new JLabel(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".login_password.label"));
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 0;
loginPanel.add(passwordLabel, gbc);
final JPasswordField loginPassword = new JPasswordField(15);
loginPassword.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".login_password.tip"));
gbc.gridx = 1;
gbc.weightx = 1;
loginPanel.add(loginPassword, gbc);
final JCheckBox useAnonymousLogin = new JCheckBox();
useAnonymousLogin.setText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".login_as_anonymous.label"));
useAnonymousLogin.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".login_as_anonymous.tip"));
useAnonymousLogin.setSelected(false);
useAnonymousLogin.setAlignmentX(Component.LEFT_ALIGNMENT);
useAnonymousLogin.setMinimumSize(useAnonymousLogin.getPreferredSize());
useAnonymousLogin.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
loginName.setEditable(false);
loginPassword.setEditable(false);
} else {
loginName.setEditable(true);
loginPassword.setEditable(true);
}
}
});
gbc.gridx = 0;
gbc.gridy = 2;
gbc.weightx = 0;
loginPanel.add(useAnonymousLogin, gbc);
gbc.gridwidth = 2;
gbc.weightx = 1;
gbc.gridy = 3;
gbc.insets = new Insets(GAP*2, 0, GAP, 0);
loginPanel.add(new JSeparator(), gbc);
panel.add(loginPanel);
final JPanel detailPanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = 1;
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(GAP, 0, 0, GAP);
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.anchor = GridBagConstraints.WEST;
c.weighty = 0;
detailPanel.add(new JLabel(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".component.label") + ":"), c);
c.gridx = 1;
c.gridy = 0;
final JComboBox compBox = new JComboBox(compVals);
compBox.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".component.tip"));
compBox.setSelectedItem("Vega: Processes, data flow and meta data");
detailPanel.add(compBox, c);
c.gridx = 2;
c.gridy = 0;
detailPanel.add(new JLabel(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".severity.label") + ":"), c);
c.gridx = 3;
c.gridy = 0;
final JComboBox severityBox = new JComboBox(severityVals);
severityBox.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".severity.tip"));
severityBox.setSelectedItem("normal");
detailPanel.add(severityBox, c);
c.gridx = 0;
c.gridy = 1;
detailPanel.add(new JLabel(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".platform.label") + ":"), c);
c.gridx = 1;
c.gridy = 1;
final JComboBox platformBox = new JComboBox(platformVals);
platformBox.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".platform.tip"));
detailPanel.add(platformBox, c);
c.gridx = 2;
c.gridy = 1;
detailPanel.add(new JLabel(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".os.label") + ":"), c);
c.gridx = 3;
c.gridy = 1;
final JComboBox osBox = new JComboBox(osVals);
osBox.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".os.tip"));
String os = System.getProperty("os.name");
if (os.toLowerCase(Locale.ENGLISH).contains("windows")) {
osBox.setSelectedItem("Windows");
platformBox.setSelectedItem("PC");
} else if (os.toLowerCase(Locale.ENGLISH).contains("linux")) {
osBox.setSelectedItem("Linux");
platformBox.setSelectedItem("PC");
} else if (os.toLowerCase(Locale.ENGLISH).contains("mac")) {
osBox.setSelectedItem("Mac OS");
platformBox.setSelectedItem("Macintosh");
} else {
osBox.setSelectedItem("Other");
platformBox.setSelectedItem("Other");
}
detailPanel.add(osBox, c);
c.gridx = 4;
c.gridy = 0;
c.weightx = 1;
detailPanel.add(new JLabel(), c);
c.gridy = 1;
detailPanel.add(new JLabel(), c);
c.gridwidth = 5;
c.gridx = 0;
c.gridy = 2;
c.fill = GridBagConstraints.BOTH;
c.insets = new Insets(GAP*2, 0, GAP, 0);
detailPanel.add(new JSeparator(), c);
panel.add(detailPanel);
final JPanel mailPanel = new JPanel(new GridBagLayout());
c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(GAP, 0, 0, GAP);
c.weighty = 0;
c.weightx = 0;
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
final JCheckBox addProcessCheckBox = new JCheckBox();
addProcessCheckBox.setText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".add_process_xml.label"));
addProcessCheckBox.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".add_process_xml.tip"));
addProcessCheckBox.setSelected(true);
addProcessCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
addProcessCheckBox.setMinimumSize(addProcessCheckBox.getPreferredSize());
mailPanel.add(addProcessCheckBox, c);
c.gridx = 1;
final JCheckBox addSysPropsCheckBox = new JCheckBox();
addSysPropsCheckBox.setText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".add_system_props.label"));
addSysPropsCheckBox.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".add_system_props.tip"));
addSysPropsCheckBox.setSelected(true);
addSysPropsCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);
addSysPropsCheckBox.setMinimumSize(addSysPropsCheckBox.getPreferredSize());
mailPanel.add(addSysPropsCheckBox, c);
c.gridx = 2;
c.weightx = 0.75;
mailPanel.add(new JLabel(), c);
c.gridwidth = 3;
c.fill = GridBagConstraints.BOTH;
c.gridx = 0;
c.gridy = 1;
mailPanel.add(new JLabel(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".summary.label") + ":"), c);
final JTextField summaryField = new JTextField(15);
summaryField.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".summary.tip"));
c.gridy = 2;
mailPanel.add(summaryField, c);
c.gridy = 3;
mailPanel.add(new JLabel(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".description.label") + ":"), c);
descriptionField.setLineWrap(true);
descriptionField.setWrapStyleWord(true);
descriptionField.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
if (descriptionField.getText().equals(descriptionText)) {
descriptionField.setText("");
descriptionField.removeFocusListener(this);
}
}
});
descriptionField.setToolTipText(I18N.getMessage(I18N.getGUIBundle(), getKey() + ".description.tip"));
JScrollPane descriptionPane = new ExtendedJScrollPane(descriptionField);
descriptionPane.setBorder(createBorder());
descriptionPane.setPreferredSize(new Dimension(400, 400));
c.gridy = 4;
c.weighty = 1;
mailPanel.add(descriptionPane, c);
c.insets = new Insets(GAP, 0, 0, 0);
c.gridx = 3;
c.gridy = 0;
c.gridheight = 5;
c.weightx = 0.25;
c.weighty = 1;
attachments.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane attachmentPane = new JScrollPane(attachments);
attachmentPane.setBorder(createBorder());
attachmentPane.setPreferredSize(new Dimension(150, 400));
mailPanel.add(attachmentPane, c);
panel.add(mailPanel);
buttons.add(new JButton(new ResourceAction("send_bugreport.add_file") {
private static final long serialVersionUID = 5152169309271935854L;
@Override
public void actionPerformed(ActionEvent e) {
File file = SwingTools.chooseFile(null, null, true, null, null);
if (file != null) {
((DefaultListModel) attachments.getModel()).addElement(file);
}
}
}));
buttons.add(new JButton(new ResourceAction("send_bugreport.remove_file") {
private static final long serialVersionUID = 5353693430346577972L;
public void actionPerformed(ActionEvent e) {
if (attachments.getSelectedIndex() >= 0) {
((DefaultListModel) attachments.getModel()).remove(attachments.getSelectedIndex());
}
}
}));
JButton infoButton = new JButton(new ResourceAction("send_bugreport.info") {
private static final long serialVersionUID = 2135052418891516027L;
@Override
public void actionPerformed(ActionEvent e) {
BugReportViewerDialog dialog = new BugReportViewerDialog();
dialog.setInfoText(BugReport.createCompleteBugDescription(descriptionField.getText().trim(), exception, addProcessCheckBox.isSelected(), addSysPropsCheckBox.isSelected()));
dialog.setVisible(true);
}
});
buttons.add(infoButton);
submitButton = new JButton(new ResourceAction("send_bugreport.submit") {
private static final long serialVersionUID = -4559762951458936715L;
@Override
public void actionPerformed(ActionEvent e) {
// check fields
email = loginName.getText().trim();
pawo = loginPassword.getPassword();
String summary = summaryField.getText().trim();
String description = descriptionField.getText().trim();
final String version = RapidMiner.getShortVersion();
if ( !useAnonymousLogin.isSelected()) {
if (email.length() <= 0) {
SwingTools.showVerySimpleErrorMessage("enter_email");
return;
}
if (!email.matches("(.+?)@(.+?)[.](.+?)")) {
SwingTools.showVerySimpleErrorMessage("enter_correct_email");
return;
}
boolean noPW = true;
for (char c : pawo) {
if (c != ' ') {
noPW = false;
break;
}
}
if (noPW) {
SwingTools.showVerySimpleErrorMessage("enter_password");
return;
}
} else {
email = "bugs@rapid-i.com";
pawo = new char[] { '!', 'z', '4', '8', '#', 'H', 'c', '2', '$', '%', 'm', ')', '9', '+', '*', '*' };
}
if (summary.length() <= 0) {
SwingTools.showVerySimpleErrorMessage("enter_summary");
return;
}
// more than a single word for a descriptive summary required!
String[] splitResult = summary.trim().split("\\s");
if (splitResult.length < 2) {
SwingTools.showVerySimpleErrorMessage("enter_descriptive_summary");
return;
}
if (description.length() <= 0 || description.equals(descriptionText)) {
SwingTools.showVerySimpleErrorMessage("enter_description");
return;
}
// all checks passed, bug report would be created right now, however we want the user
// to check his browser for similar/duplicate bugs by opening the bugzilla search page with
// given parameters
try {
if(Desktop.isDesktopSupported()) {
Desktop desktop = Desktop.getDesktop();
if(desktop.isSupported(Desktop.Action.BROWSE)) {
String bugzillaSearchString =
"http://bugs.rapid-i.com/buglist.cgi?field0-0-0=attach_data.thedata&type0-0-1=allwordssubstr&field0-0-1=longdesc&query_format=advanced&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&value0-0-1=" +
exception.getMessage() +
"&type0-0-0=allwordssubstr&value0-0-0=" +
exception.getMessage();
URL bugzillaURL = new URL(bugzillaSearchString);
URI bugzillaURI = new URI(bugzillaURL.getProtocol(), bugzillaURL.getHost(), bugzillaURL.getPath(), bugzillaURL.getQuery(), null);
desktop.browse(bugzillaURI);
int returnVal = SwingTools.showConfirmDialog("send_bugreport.check_browser_for_duplicates", ConfirmDialog.YES_NO_OPTION);
// user clicked no, don't submit
if (returnVal == ConfirmDialog.NO_OPTION) {
return;
}
}
}
} catch (URISyntaxException e1) {
// should not occur (famous last comment)
} catch (IOException e1) {
// we can't change it, so ignore it
}
// create bugreport in own progess thread (cancel not allowed)
submitButton.setEnabled(false);
new ProgressThread("send_report_to_bugzilla", false) {
@Override
public void run() {
try {
getProgressListener().setTotal(100);
ListModel model = attachments.getModel();
File[] attachments = new File[model.getSize()];
for (int i = 0; i < attachments.length; i++) {
attachments[i] = (File) model.getElementAt(i);
}
getProgressListener().setCompleted(20);
XmlRpcClient client = XmlRpcHandler.login(XmlRpcHandler.BUGZILLA_URL, email, pawo);
getProgressListener().setCompleted(40);
BugReport.createBugZillaReport(client, exception, summaryField.getText().trim(),
descriptionField.getText().trim(), String.valueOf(compBox.getSelectedItem()), version, String.valueOf(severityBox.getSelectedItem()),
String.valueOf(platformBox.getSelectedItem()), String.valueOf(osBox.getSelectedItem()), RapidMinerGUI.getMainFrame().getProcess(),
RapidMinerGUI.getMainFrame().getMessageViewer().getLogMessage(), attachments, addProcessCheckBox.isSelected(), addSysPropsCheckBox.isSelected());
getProgressListener().setCompleted(100);
SwingTools.showMessageDialog("bugreport_successful");
dispose();
} catch(XmlRpcException e1) {
SwingTools.showVerySimpleErrorMessage("bugreport_xmlrpc_error", e1.getLocalizedMessage());
} catch (Exception e2) {
LogService.getRoot().warning(e2.getLocalizedMessage());
SwingTools.showVerySimpleErrorMessage("bugreport_creation_failed");
} finally {
getProgressListener().complete();
for (int i=0; i<pawo.length; i++) {
pawo[i] = 0;
}
submitButton.setEnabled(true);
}
}
}.start();
}
});
buttons.add(submitButton);
buttons.add(makeCancelButton());
layoutDefault(panel, LARGE, buttons);
}
}