Add report notification by email support.

This commit is contained in:
Paulo Gustavo Veiga 2012-06-23 14:39:50 -03:00
parent 2d8fa5c259
commit c8837baadd
16 changed files with 250 additions and 39 deletions

View File

@ -20,10 +20,8 @@ $assert = function(assert, message) {
stack = e; stack = e;
} }
console.log(message + "," + stack); console.log(message + "," + stack);
window.errorStack = stackTrace();
throw message; throw message;
// wLogger.error(message + "," + stack);
// core.Logger.logError(message + "," + stack);
} }
}; };
@ -31,6 +29,25 @@ Math.sign = function(value) {
return (value >= 0) ? 1 : -1; return (value >= 0) ? 1 : -1;
}; };
function stackTrace() {
var result = "";
var isCallstackPopulated = false;
try {
null.eval();
} catch(e) {
if (e.stack) { //Firefox and Chrome...
result = e.stack;
isCallstackPopulated = true;
}
else if (window.opera && e.message) { //Opera
result = e.message;
isCallstackPopulated = true;
}
}
return result;
}
/* /*
* DOMParser HTML extension * DOMParser HTML extension
* 2012-02-02 * 2012-02-02

View File

@ -30,13 +30,26 @@ function buildDesigner(options) {
window.waitDialog = null; window.waitDialog = null;
}); });
window.onerror = function(e) { window.onerror = function(message, url, lineNo) {
// Log error message ...
if (window.waitDialog) { if (window.waitDialog) {
window.waitDialog.close.delay(1000, window.waitDialog); window.waitDialog.close.delay(1000, window.waitDialog);
window.waitDialog = null; window.waitDialog = null;
} }
var req = new Request({
method: 'post',
url: "/service/logger/editor",
headers: {"Content-Type":"application/json","Accept":"application/json"},
emulation:false,
urlEncoded:false
}).post(JSON.encode({
jsErrorMsg : "message: " + message + ", line:" + lineNo + ", :" + url,
jsStack : window.errorStack,
userAgent: navigator.userAgent,
mapId: options.mapId}));
errorDialog.show(); errorDialog.show();
console.log(e);
}; };
// Configure default persistence manager ... // Configure default persistence manager ...

View File

@ -60,7 +60,7 @@ public class BrowserSupportInterceptor extends HandlerInterceptorAdapter {
} }
// Is a Explorer 9 or less without Google Chrome Frame ?. // Is a Explorer 9 or less without Google Chrome Frame ?.
if (!userAgent.needsGCF()) { if (userAgent.needsGCF()) {
throw new GoogleChromeFrameRequiredException(); throw new GoogleChromeFrameRequiredException();
} }

View File

@ -331,9 +331,7 @@ public class UserAgent implements Serializable {
} }
public boolean needsGCF() { public boolean needsGCF() {
return true; final UserAgent.Product product = this.getProduct();
// final UserAgent.Product product = this.getProduct(); return product == UserAgent.Product.EXPLORER && this.isVersionLessThan(9) && this.getOs() == UserAgent.OS.WINDOWS && !this.hasGCFInstalled;
// return product == UserAgent.Product.EXPLORER && this.isVersionLessThan(9) && this.getOs() == UserAgent.OS.WINDOWS && !this.hasGCFInstalled;
} }
} }

View File

@ -37,12 +37,14 @@ public final class Mailer {
private VelocityEngine velocityEngine; private VelocityEngine velocityEngine;
private String serverFromEmail; private String serverFromEmail;
private String supportEmail; private String supportEmail;
private String errorReporterEmail;
//~ Methods .............................................................................................. //~ Methods ..............................................................................................
public Mailer(@NotNull String siteEmail, @NotNull String supportEmail) { public Mailer(@NotNull String siteEmail, @NotNull String supportEmail,@NotNull String errorReporterEmail) {
this.serverFromEmail = siteEmail; this.serverFromEmail = siteEmail;
this.supportEmail = supportEmail; this.supportEmail = supportEmail;
this.errorReporterEmail = errorReporterEmail;
} }
public String getServerSenderEmail() { public String getServerSenderEmail() {
@ -79,4 +81,8 @@ public final class Mailer {
public String getSupportEmail() { public String getSupportEmail() {
return supportEmail; return supportEmail;
} }
public String getErrorReporterEmail() {
return errorReporterEmail;
}
} }

View File

@ -25,6 +25,9 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -140,6 +143,65 @@ final public class NotificationService {
mailer.sendEmail(mailer.getServerSenderEmail(), user.getEmail(), "Welcome to Wisemapping!", model, mailer.sendEmail(mailer.getServerSenderEmail(), user.getEmail(), "Welcome to Wisemapping!", model,
"confirmationMail.vm"); "confirmationMail.vm");
} }
public void reportMindmapEditorError(@NotNull MindMap mindmap, @NotNull User user, @NotNull String userAgent, @Nullable String jsErrorMsg) {
try {
final Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("errorMsg", jsErrorMsg);
model.put("mapXML", mindmap.getXmlStr().replaceAll("<", "&lt;"));
model.put("mapId", mindmap.getId());
model.put("mapTitle", mindmap.getTitle());
model.put("userAgent", userAgent);
final String errorReporterEmail = mailer.getErrorReporterEmail();
if (errorReporterEmail != null) {
mailer.sendEmail(mailer.getServerSenderEmail(), errorReporterEmail, "[WiseMapping] Editor error from " + user.getEmail(), model,
"editorErrorReport.vm");
}
} catch (Exception e) {
handleException(e);
}
}
public void reportMindmapExportError(@NotNull String exportContent, @NotNull User user, @NotNull String userAgent, @NotNull Throwable exception) {
try {
final Map<String, Object> model = new HashMap<String, Object>();
model.put("user", user);
model.put("errorMsg", stackTraceToString(exception));
model.put("mapXML", exportContent.replaceAll("<", "&lt;"));
model.put("userAgent", userAgent);
final String errorReporterEmail = mailer.getErrorReporterEmail();
if (errorReporterEmail != null) {
mailer.sendEmail(mailer.getServerSenderEmail(), errorReporterEmail, "[WiseMapping] Export error from " + user.getEmail(), model,
"editorErrorReport.vm");
}
} catch (Exception e) {
handleException(e);
}
}
public String stackTraceToString(@NotNull Throwable e) {
String retValue = null;
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
e.printStackTrace(pw);
retValue = sw.toString();
} finally {
try {
if (pw != null) pw.close();
if (sw != null) sw.close();
} catch (IOException ignore) {
}
}
return retValue;
}
} }

View File

@ -198,8 +198,4 @@ public class MindmapController {
private MindMapBean findMindmapBean(long mapId) { private MindMapBean findMindmapBean(long mapId) {
return new MindMapBean(findMindmap(mapId), Utils.getUser()); return new MindMapBean(findMindmap(mapId), Utils.getUser());
} }
private boolean isWelcomeMap(MindMapBean map) {
return map.getTitle().startsWith("Welcome ");
}
} }

View File

@ -18,8 +18,12 @@
package com.wisemapping.rest; package com.wisemapping.rest;
import com.wisemapping.mail.NotificationService;
import com.wisemapping.model.MindMap;
import com.wisemapping.model.User; import com.wisemapping.model.User;
import com.wisemapping.rest.model.RestLogItem;
import com.wisemapping.security.Utils; import com.wisemapping.security.Utils;
import com.wisemapping.service.MindmapService;
import com.wisemapping.service.UserService; import com.wisemapping.service.UserService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
@ -33,6 +37,13 @@ public class AccountController extends BaseController {
@Autowired @Autowired
private UserService userService; private UserService userService;
@Qualifier("mindmapService")
@Autowired
private MindmapService mindmapService;
@Autowired
private NotificationService notificationService;
@RequestMapping(method = RequestMethod.PUT, value = "account/password", consumes = {"text/plain"}) @RequestMapping(method = RequestMethod.PUT, value = "account/password", consumes = {"text/plain"})
@ResponseStatus(value = HttpStatus.NO_CONTENT) @ResponseStatus(value = HttpStatus.NO_CONTENT)
public void changePassword(@RequestBody String password) { public void changePassword(@RequestBody String password) {
@ -68,4 +79,13 @@ public class AccountController extends BaseController {
user.setLastname(lastname); user.setLastname(lastname);
userService.updateUser(user); userService.updateUser(user);
} }
@RequestMapping(method = RequestMethod.POST, value = "logger/editor", consumes = {"application/xml", "application/json"}, produces = {"application/json", "text/html", "application/xml"})
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void changePassword(@RequestBody RestLogItem item) {
final MindMap mindmap = mindmapService.findMindmapById(item.getMapId());
final User user = Utils.getUser();
notificationService.reportMindmapEditorError(mindmap, user, item.getUserAgent(), item.getJsErrorMsg() + "\n" + item.getJsStack());
}
} }

View File

@ -0,0 +1,56 @@
package com.wisemapping.rest.model;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.jetbrains.annotations.NotNull;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "logitem")
@XmlAccessorType(XmlAccessType.PROPERTY)
@JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.NONE,
getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY,
isGetterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY)
public class RestLogItem {
private String jsStack;
private String userAgent;
private String jsErrorMsg;
public int getMapId() {
return mapId;
}
public void setMapId(int mapId) {
this.mapId = mapId;
}
private int mapId;
public String getJsStack() {
return jsStack;
}
public void setJsStack(@NotNull String jsStack) {
this.jsStack = jsStack;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public String getJsErrorMsg() {
return jsErrorMsg;
}
public void setJsErrorMsg(String jsErrorMsg) {
this.jsErrorMsg = jsErrorMsg;
}
}

View File

@ -18,25 +18,38 @@
package com.wisemapping.rest.view; package com.wisemapping.rest.view;
import com.wisemapping.exporter.ExportException;
import com.wisemapping.exporter.ExportFormat; import com.wisemapping.exporter.ExportFormat;
import com.wisemapping.exporter.ExportProperties; import com.wisemapping.exporter.ExportProperties;
import com.wisemapping.exporter.ExporterFactory; import com.wisemapping.exporter.ExporterFactory;
import com.wisemapping.mail.NotificationService;
import com.wisemapping.security.Utils;
import org.apache.batik.transcoder.TranscoderException;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.oxm.XmlMappingException;
import org.springframework.oxm.jaxb.Jaxb2Marshaller; import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.web.servlet.view.AbstractView; import org.springframework.web.servlet.view.AbstractView;
import org.xml.sax.SAXException;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.util.Map; import java.util.Map;
public class TransformView extends AbstractView { public class TransformView extends AbstractView {
private String contentType; private String contentType;
private ExportFormat exportFormat; private ExportFormat exportFormat;
private NotificationService notificationService;
@Autowired @Autowired
private Jaxb2Marshaller jaxbMarshaller; private Jaxb2Marshaller jaxbMarshaller;
@ -75,6 +88,7 @@ public class TransformView extends AbstractView {
// Change image link URL. // Change image link URL.
setBaseBaseImgUrl(exportFormat, properties); setBaseBaseImgUrl(exportFormat, properties);
try {
// Write the conversion content ... // Write the conversion content ...
final ServletOutputStream outputStream = response.getOutputStream(); final ServletOutputStream outputStream = response.getOutputStream();
if (exportFormat == ExportFormat.FREEMIND) { if (exportFormat == ExportFormat.FREEMIND) {
@ -86,6 +100,9 @@ public class TransformView extends AbstractView {
} else { } else {
ExporterFactory.export(properties, null, outputStream, content); ExporterFactory.export(properties, null, outputStream, content);
} }
} catch (Throwable e) {
notificationService.reportMindmapExportError(content, Utils.getUser(), request.getHeader("User-Agent"),e);
}
} }
@Override @Override

View File

@ -0,0 +1,19 @@
<html>
<body>
<ul>
<li>User Name: ${user.fullName}</li>
<li>Email: ${user.email}</li>
<li>User Agent: ${userAgent}</li>
<li>Mindmap Id: ${mapId}</li>
<li>Mindmap Title: ${mapTitle}</li>
</ul>
<pre style="border:1px dashed #a9a9a9">
${errorMsg}
</pre>
<pre style="border:1px dashed #a9a9a9">
${mapXML}
</pre>
</body>
</html>

View File

@ -3,18 +3,18 @@
################################################################################## ##################################################################################
# MySQL 5.X configuration properties # MySQL 5.X configuration properties
#database.url=jdbc:mysql://localhost/wisemapping database.url=jdbc:mysql://localhost/wisemapping
#database.driver=com.mysql.jdbc.Driver database.driver=com.mysql.jdbc.Driver
#database.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect database.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
#database.username=wisemapping database.username=wisemapping
#database.password=password database.password=password
# HSQL Configuration properties # HSQL Configuration properties
database.url=jdbc:hsqldb:file:target/db/wisemapping #database.url=jdbc:hsqldb:file:target/db/wisemapping
database.driver=org.hsqldb.jdbc.JDBCDriver #database.driver=org.hsqldb.jdbc.JDBCDriver
database.hibernate.dialect=org.hibernate.dialect.HSQLDialect #database.hibernate.dialect=org.hibernate.dialect.HSQLDialect
database.username=sa #database.username=sa
database.password= #database.password=
################################################################################## ##################################################################################
@ -44,12 +44,15 @@ mail.smtp.quitwait=false
# Emails configuration # Emails configuration
#------------------------ #------------------------
# "from" email account that will appear in the emails sent from the sender. # Required: "from" email account that will appear in the emails sent from the sender.
mail.serverSendEmail=root@localhost mail.serverSendEmail=root@localhost
# Support account that the users could use to contact you. This address will appear in emails and in some places in the site. # Optional: Support account that the users could use to contact you. This address will appear in emails and in some places in the site.
mail.supportEmail=root@localhost mail.supportEmail=root@localhost
# Optional: Unexpected error will be reported to this address.
mail.errorReporterEmail=root@localhost
################################################################################## ##################################################################################
# Users Registration Configuration # Users Registration Configuration
################################################################################## ##################################################################################

View File

@ -2,7 +2,8 @@ log4j.rootLogger=WARN, stdout, R
log4j.logger.com.wisemapping=WARN,stdout,R log4j.logger.com.wisemapping=WARN,stdout,R
log4j.logger.org.springframework=WARN,stdout,R log4j.logger.org.springframework=WARN,stdout,R
log4j.logger.org.codehaus.jackson=WARN,stdout,R log4j.logger.org.codehaus.jackson=WARN,stdout,R
log4j.additivity.org.hibernate.SQL=false log4j.logger.org.hibernate=DEBUG,stdout,R
log4j.logger.org.hibernate.SQL=true
# Stdout logger <20> # Stdout logger <20>

View File

@ -25,6 +25,7 @@
<value>com.wisemapping.rest.model.RestErrors</value> <value>com.wisemapping.rest.model.RestErrors</value>
<value>com.wisemapping.rest.model.RestCollaboration</value> <value>com.wisemapping.rest.model.RestCollaboration</value>
<value>com.wisemapping.rest.model.RestCollaborationList</value> <value>com.wisemapping.rest.model.RestCollaborationList</value>
<value>com.wisemapping.rest.model.RestLogItem</value>
</list> </list>
</property> </property>
</bean> </bean>

View File

@ -5,6 +5,8 @@
<bean id="mailer" class="com.wisemapping.mail.Mailer" singleton="true"> <bean id="mailer" class="com.wisemapping.mail.Mailer" singleton="true">
<constructor-arg index="0" value="${mail.serverSendEmail}"/> <constructor-arg index="0" value="${mail.serverSendEmail}"/>
<constructor-arg index="1" value="${mail.supportEmail}"/> <constructor-arg index="1" value="${mail.supportEmail}"/>
<constructor-arg index="2" value="${mail.errorReporterEmail}"/>
<property name="mailSender" ref="mailSender"/> <property name="mailSender" ref="mailSender"/>
<property name="velocityEngine" ref="velocityEngine"/> <property name="velocityEngine" ref="velocityEngine"/>
</bean> </bean>