/*
* Copyright 2012 Corpuslinguistic working group Humboldt University Berlin.
*
* 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 annis.gui;
import annis.VersionInfo;
import annis.libgui.Helper;
import com.vaadin.data.fieldgroup.FieldGroup;
import com.vaadin.data.fieldgroup.PropertyId;
import com.vaadin.data.util.BeanItem;
import com.vaadin.data.validator.EmailValidator;
import com.vaadin.server.FontAwesome;
import com.vaadin.server.Page;
import com.vaadin.server.StreamResource;
import com.vaadin.server.UserError;
import com.vaadin.server.VaadinService;
import com.vaadin.shared.Version;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.Field;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Image;
import com.vaadin.ui.Layout;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.BaseTheme;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import javax.activation.FileDataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.mail.ByteArrayDataSource;
import org.apache.commons.mail.EmailException;
import org.apache.commons.mail.MultiPartEmail;
import org.slf4j.LoggerFactory;
/**
*
* @author Thomas Krause <krauseto@hu-berlin.de>
*/
public class ReportBugWindow extends Window
{
private static final org.slf4j.Logger log = LoggerFactory.getLogger(MetaDataPanel.class);
private FieldGroup form;
private Button btSubmit;
private Button btCancel;
private Throwable cause;
public ReportBugWindow(final String bugEMailAddress, final byte[] screenImage,
final String imageMimeType, Throwable cause)
{
this.cause = cause;
setSizeUndefined();
setCaption("Report Problem");
ReportFormLayout layout = new ReportFormLayout();
setContent(layout);
layout.setWidth("100%");
layout.setHeight("-1px");
setHeight("420px");
setWidth("750px");
form = new FieldGroup(new BeanItem<>(new BugReport()));
form.bindMemberFields(layout);
form.setBuffered(true);
final ReportBugWindow finalThis = this;
btSubmit = new Button("Submit problem report", new Button.ClickListener()
{
@Override
public void buttonClick(ClickEvent event)
{
try
{
form.commit();
if(sendBugReport(bugEMailAddress, screenImage, imageMimeType))
{
Notification.show("Problem report was sent",
"We will answer your problem report as soon as possible",
Notification.Type.HUMANIZED_MESSAGE);
}
UI.getCurrent().removeWindow(finalThis);
}
catch (FieldGroup.CommitException ex)
{
List<String> errorFields = new LinkedList<>();
for(Field f : form.getFields())
{
if (f instanceof AbstractComponent)
{
AbstractComponent c = (AbstractComponent) f;
if (f.isValid())
{
c.setComponentError(null);
}
else
{
errorFields.add(f.getCaption());
c.setComponentError(new UserError("Validation failed: "));
}
}
} // for each field
String message = "Please check the error messages "
+ "(place mouse over red triangle) for the following fields:<br />";
message = message + StringUtils.join(errorFields, ",<br/>");
Notification notify = new Notification("Validation failed",
message, Notification.Type.WARNING_MESSAGE,
true);
notify.show(UI.getCurrent().getPage());
}
catch (Exception ex)
{
log.error("Could not send bug report", ex);
Notification.show("Could not send bug report", ex.
getMessage(),
Notification.Type.WARNING_MESSAGE);
}
}
});
btCancel = new Button("Cancel", new Button.ClickListener()
{
@Override
public void buttonClick(ClickEvent event)
{
form.discard();
UI.getCurrent().removeWindow(finalThis);
}
});
addScreenshotPreview(layout, screenImage, imageMimeType);
HorizontalLayout buttons = new HorizontalLayout();
buttons.addComponent(btSubmit);
buttons.addComponent(btCancel);
layout.addComponent(buttons);
}
private void addScreenshotPreview(Layout layout, final byte[] rawImage, String mimeType)
{
StreamResource res = new StreamResource(
new ScreenDumpStreamSource(rawImage), "screendump_" + UUID.randomUUID().toString() + ".png"
);
res.setMIMEType(mimeType);
final Image imgScreenshot =
new Image("Attached screenshot", res);
imgScreenshot.setAlternateText("Screenshot of the ANNIS browser window, "
+ "no other window or part of the desktop is captured.");
imgScreenshot.setVisible(false);
imgScreenshot.setWidth("100%");
Button btShowScreenshot = new Button("Show attached screenshot", new ShowScreenshotClickListener(imgScreenshot));
btShowScreenshot.addStyleName(BaseTheme.BUTTON_LINK);
btShowScreenshot.setIcon(FontAwesome.PLUS_SQUARE_O);
layout.addComponent(btShowScreenshot);
layout.addComponent(imgScreenshot);
}
private boolean sendBugReport(String bugEMailAddress,
byte[] screenImage, String imageMimeType)
{
MultiPartEmail mail = new MultiPartEmail();
try
{
// server setup
mail.setHostName("localhost");
// content of the mail
mail.addReplyTo(form.getField("email").getValue().toString(),
form.getField("name").getValue().toString());
mail.setFrom(bugEMailAddress);
mail.addTo(bugEMailAddress);
mail.setSubject("[ANNIS BUG] " + form.getField("summary").getValue().
toString());
// TODO: add info about version etc.
StringBuilder sbMsg = new StringBuilder();
sbMsg.append("Reporter: ").append(form.getField("name").getValue().
toString()).append(" (").append(form.getField("email").getValue().
toString()).append(")\n");
sbMsg.append("Version: ").append(VersionInfo.getVersion()).append(
"\n");
sbMsg.append("Vaadin Version: ").append(Version.getFullVersion()).append(
"\n");
sbMsg.append("Browser: ").append(Page.getCurrent().getWebBrowser().getBrowserApplication()).append("\n");
sbMsg.append("URL: ").append(UI.getCurrent().getPage().getLocation().toASCIIString()).append(
"\n");
sbMsg.append("\n");
sbMsg.append(form.getField("description").getValue().toString());
mail.setMsg(sbMsg.toString());
if (screenImage != null)
{
try
{
mail.attach(new ByteArrayDataSource(screenImage, imageMimeType),
"screendump.png", "Screenshot of the browser content at time of problem report");
}
catch (IOException ex)
{
log.error(null, ex);
}
}
File logfile = new File(VaadinService.getCurrent().getBaseDirectory(), "/WEB-INF/log/annis-gui.log");
if(logfile.exists() && logfile.isFile() && logfile.canRead())
{
mail.attach(new FileDataSource(logfile), "annis-gui.log", "Logfile of the GUI (shared by all users)");
}
if(cause != null)
{
try
{
mail.attach(new ByteArrayDataSource(Helper.convertExceptionToMessage(
cause), "text/plain"), "exception.txt",
"Exception that caused the user to report the problem");
}
catch (IOException ex)
{
log.error(null, ex);
}
}
mail.send();
return true;
}
catch (EmailException ex)
{
Notification.show("E-Mail not configured on server",
"If this is no Kickstarter version please ask the administrator ("
+ bugEMailAddress
+ ") of this ANNIS-instance for assistance. "
+ "Problem reports are not available for ANNIS Kickstarter", Notification.Type.ERROR_MESSAGE);
log.error(null,
ex);
return false;
}
}
public static class BugReport
{
private String summary = "";
private String description =
"What steps will reproduce the problem?\n"
+ "1.\n"
+ "2.\n"
+ "3.\n"
+ "\n"
+ "What is the expected result?\n"
+ "\n"
+ "\n"
+ "What happens instead\n"
+ "\n"
+ "\n"
+ "Please provide any additional information below.\n";
private String name = "";
private String email = "";
public String getSummary()
{
return summary;
}
public void setSummary(String summary)
{
this.summary = summary;
}
public String getDescription()
{
return description;
}
public void setDescription(String description)
{
this.description = description;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getEmail()
{
return email;
}
public void setEmail(String mail)
{
this.email = mail;
}
}
public static class ReportFormLayout extends FormLayout
{
@PropertyId("summary")
private TextField txtSummary;
@PropertyId("description")
private TextArea txtDescription;
@PropertyId("name")
private TextField txtName;
@PropertyId("email")
private TextField txtMail;
public ReportFormLayout()
{
txtSummary = new TextField("Short Summary");
txtSummary.setRequired(true);
txtSummary.setRequiredError("You must provide a summary");
txtSummary.setWidth("100%");
txtDescription = new TextArea("Long Description");
txtDescription.setRequired(true);
txtDescription.setRequiredError("You must provide a description");
txtDescription.setRows(10);
txtDescription.setWidth("100%");
txtName = new TextField("Your Name");
txtName.setRequired(true);
txtName.setRequiredError("You must provide your name");
txtName.setWidth("100%");
txtMail = new TextField("Your e-mail adress");
txtMail.setRequired(true);
txtMail.setRequiredError("You must provide a valid e-mail adress");
txtMail.addValidator(new EmailValidator(
"You must provide a valid e-mail adress"));
txtMail.setWidth("100%");
addComponents(txtSummary, txtDescription, txtName, txtMail);
}
}
private static class ScreenDumpStreamSource implements StreamResource.StreamSource
{
private final byte[] rawImage;
public ScreenDumpStreamSource(byte[] rawImage)
{
this.rawImage = rawImage;
}
@Override
public InputStream getStream()
{
return new ByteArrayInputStream(rawImage);
}
}
private static class ShowScreenshotClickListener implements ClickListener
{
private final Image imgScreenshot;
public ShowScreenshotClickListener(Image imgScreenshot)
{
this.imgScreenshot = imgScreenshot;
}
@Override
public void buttonClick(ClickEvent event)
{
imgScreenshot.setVisible(!imgScreenshot.isVisible());
if(imgScreenshot.isVisible())
{
event.getButton().setCaption("Hide attached screenshot");
event.getButton().setIcon(FontAwesome.MINUS_SQUARE_O);
}
else
{
event.getButton().setCaption("Show attached screenshot");
event.getButton().setIcon(FontAwesome.PLUS_SQUARE_O);
}
}
}
}