Skip to content

Commit 6c64c6e

Browse files
committed
Implement mail templates
1 parent c674d90 commit 6c64c6e

18 files changed

+208
-204
lines changed

pom.xml

+5-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,11 @@
138138
<artifactId>jxls-poi</artifactId>
139139
<version>1.0.11</version>
140140
</dependency>
141-
141+
<dependency>
142+
<groupId>org.apache.velocity</groupId>
143+
<artifactId>velocity</artifactId>
144+
<version>1.7</version>
145+
</dependency>
142146
</dependencies>
143147

144148
<build>

src/org/traccar/Context.java

+16
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
import com.ning.http.client.AsyncHttpClient;
1919

20+
import java.util.Properties;
21+
22+
import org.apache.velocity.app.VelocityEngine;
2023
import org.traccar.database.AliasesManager;
2124
import org.traccar.database.ConnectionManager;
2225
import org.traccar.database.DataManager;
@@ -125,6 +128,12 @@ public static NotificationManager getNotificationManager() {
125128
return notificationManager;
126129
}
127130

131+
private static VelocityEngine velocityEngine;
132+
133+
public static VelocityEngine getVelocityEngine() {
134+
return velocityEngine;
135+
}
136+
128137
private static final AsyncHttpClient ASYNC_HTTP_CLIENT = new AsyncHttpClient();
129138

130139
public static AsyncHttpClient getAsyncHttpClient() {
@@ -242,6 +251,13 @@ public static void init(String[] arguments) throws Exception {
242251

243252
if (config.getBoolean("event.enable")) {
244253
notificationManager = new NotificationManager(dataManager);
254+
Properties velocityProperties = new Properties();
255+
velocityProperties.setProperty("file.resource.loader.path",
256+
Context.getConfig().getString("mail.templatesPath", "templates/mail") + "/");
257+
velocityProperties.setProperty("runtime.log.logsystem.class",
258+
"org.apache.velocity.runtime.log.NullLogChute");
259+
velocityEngine = new VelocityEngine();
260+
velocityEngine.init(velocityProperties);
245261
}
246262

247263
serverManager = new ServerManager();

src/org/traccar/notification/NotificationFormatter.java

+43-201
Original file line numberDiff line numberDiff line change
@@ -15,223 +15,65 @@
1515
*/
1616
package org.traccar.notification;
1717

18-
import java.text.DecimalFormat;
19-
import java.util.Formatter;
20-
import java.util.Locale;
18+
import java.io.StringWriter;
2119

20+
import org.apache.velocity.Template;
21+
import org.apache.velocity.VelocityContext;
22+
import org.apache.velocity.exception.ResourceNotFoundException;
2223
import org.traccar.Context;
23-
import org.traccar.helper.UnitsConverter;
24+
import org.traccar.helper.Log;
2425
import org.traccar.model.Device;
2526
import org.traccar.model.Event;
2627
import org.traccar.model.Position;
28+
import org.traccar.reports.ReportUtils;
2729

2830
public final class NotificationFormatter {
2931

30-
private NotificationFormatter() {
32+
public static class MailMessage {
33+
private String subject;
34+
private String body;
35+
public MailMessage(String subject, String body) {
36+
this.subject = subject;
37+
this.body = body;
38+
}
39+
public String getSubject() {
40+
return subject;
41+
}
42+
public String getBody() {
43+
return body;
44+
}
3145
}
3246

33-
public static final String TITLE_TEMPLATE_TYPE_COMMAND_RESULT = "%1$s: command result received";
34-
public static final String MESSAGE_TEMPLATE_TYPE_COMMAND_RESULT = "Device: %1$s%n"
35-
+ "Result: %3$s%n"
36-
+ "Time: %2$tc%n";
37-
38-
public static final String TITLE_TEMPLATE_TYPE_DEVICE_ONLINE = "%1$s: online";
39-
public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_ONLINE = "Device: %1$s%n"
40-
+ "Online%n"
41-
+ "Time: %2$tc%n";
42-
public static final String TITLE_TEMPLATE_TYPE_DEVICE_UNKNOWN = "%1$s: status is unknown";
43-
public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_UNKNOWN = "Device: %1$s%n"
44-
+ "Status is unknown%n"
45-
+ "Time: %2$tc%n";
46-
public static final String TITLE_TEMPLATE_TYPE_DEVICE_OFFLINE = "%1$s: offline";
47-
public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_OFFLINE = "Device: %1$s%n"
48-
+ "Offline%n"
49-
+ "Time: %2$tc%n";
50-
51-
public static final String TITLE_TEMPLATE_TYPE_DEVICE_MOVING = "%1$s: moving";
52-
public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_MOVING = "Device: %1$s%n"
53-
+ "Moving%n"
54-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
55-
+ "Time: %2$tc%n";
56-
public static final String TITLE_TEMPLATE_TYPE_DEVICE_STOPPED = "%1$s: stopped";
57-
public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_STOPPED = "Device: %1$s%n"
58-
+ "Stopped%n"
59-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
60-
+ "Time: %2$tc%n";
61-
62-
public static final String TITLE_TEMPLATE_TYPE_DEVICE_OVERSPEED = "%1$s: exceeds the speed";
63-
public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_OVERSPEED = "Device: %1$s%n"
64-
+ "Exceeds the speed: %5$s%n"
65-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
66-
+ "Time: %2$tc%n";
67-
68-
public static final String TITLE_TEMPLATE_TYPE_GEOFENCE_ENTER = "%1$s: has entered geofence";
69-
public static final String MESSAGE_TEMPLATE_TYPE_GEOFENCE_ENTER = "Device: %1$s%n"
70-
+ "Has entered geofence: %5$s%n"
71-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
72-
+ "Time: %2$tc%n";
73-
public static final String TITLE_TEMPLATE_TYPE_GEOFENCE_EXIT = "%1$s: has exited geofence";
74-
public static final String MESSAGE_TEMPLATE_TYPE_GEOFENCE_EXIT = "Device: %1$s%n"
75-
+ "Has exited geofence: %5$s%n"
76-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
77-
+ "Time: %2$tc%n";
78-
79-
public static final String TITLE_TEMPLATE_TYPE_ALARM = "%1$s: alarm!";
80-
public static final String MESSAGE_TEMPLATE_TYPE_ALARM = "Device: %1$s%n"
81-
+ "Alarm: %5$s%n"
82-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
83-
+ "Time: %2$tc%n";
84-
85-
public static final String TITLE_TEMPLATE_TYPE_IGNITION_ON = "%1$s: ignition ON";
86-
public static final String MESSAGE_TEMPLATE_TYPE_IGNITION_ON = "Device: %1$s%n"
87-
+ "Ignition ON%n"
88-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
89-
+ "Time: %2$tc%n";
90-
public static final String TITLE_TEMPLATE_TYPE_IGNITION_OFF = "%1$s: ignition OFF";
91-
public static final String MESSAGE_TEMPLATE_TYPE_IGNITION_OFF = "Device: %1$s%n"
92-
+ "Ignition OFF%n"
93-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
94-
+ "Time: %2$tc%n";
95-
public static final String TITLE_TEMPLATE_TYPE_MAINTENANCE = "%1$s: maintenance is required";
96-
public static final String MESSAGE_TEMPLATE_TYPE_MAINTENANCE = "Device: %1$s%n"
97-
+ "Maintenance is required%n"
98-
+ "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
99-
+ "Time: %2$tc%n";
100-
101-
public static String formatTitle(long userId, Event event, Position position) {
102-
Device device = Context.getIdentityManager().getDeviceById(event.getDeviceId());
103-
StringBuilder stringBuilder = new StringBuilder();
104-
Formatter formatter = new Formatter(stringBuilder, Locale.getDefault());
105-
106-
switch (event.getType()) {
107-
case Event.TYPE_COMMAND_RESULT:
108-
formatter.format(TITLE_TEMPLATE_TYPE_COMMAND_RESULT, device.getName());
109-
break;
110-
case Event.TYPE_DEVICE_ONLINE:
111-
formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_ONLINE, device.getName());
112-
break;
113-
case Event.TYPE_DEVICE_UNKNOWN:
114-
formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_UNKNOWN, device.getName());
115-
break;
116-
case Event.TYPE_DEVICE_OFFLINE:
117-
formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_OFFLINE, device.getName());
118-
break;
119-
case Event.TYPE_DEVICE_MOVING:
120-
formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_MOVING, device.getName());
121-
break;
122-
case Event.TYPE_DEVICE_STOPPED:
123-
formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_STOPPED, device.getName());
124-
break;
125-
case Event.TYPE_DEVICE_OVERSPEED:
126-
formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_OVERSPEED, device.getName());
127-
break;
128-
case Event.TYPE_GEOFENCE_ENTER:
129-
formatter.format(TITLE_TEMPLATE_TYPE_GEOFENCE_ENTER, device.getName());
130-
break;
131-
case Event.TYPE_GEOFENCE_EXIT:
132-
formatter.format(TITLE_TEMPLATE_TYPE_GEOFENCE_EXIT, device.getName());
133-
break;
134-
case Event.TYPE_ALARM:
135-
formatter.format(TITLE_TEMPLATE_TYPE_ALARM, device.getName());
136-
break;
137-
case Event.TYPE_IGNITION_ON:
138-
formatter.format(TITLE_TEMPLATE_TYPE_IGNITION_ON, device.getName());
139-
break;
140-
case Event.TYPE_IGNITION_OFF:
141-
formatter.format(TITLE_TEMPLATE_TYPE_IGNITION_OFF, device.getName());
142-
break;
143-
case Event.TYPE_MAINTENANCE:
144-
formatter.format(TITLE_TEMPLATE_TYPE_MAINTENANCE, device.getName());
145-
break;
146-
default:
147-
formatter.format("Unknown type");
148-
break;
149-
}
150-
String result = formatter.toString();
151-
formatter.close();
152-
return result;
47+
private NotificationFormatter() {
15348
}
15449

155-
public static String formatMessage(long userId, Event event, Position position) {
50+
public static MailMessage formatMessage(long userId, Event event, Position position) {
15651
Device device = Context.getIdentityManager().getDeviceById(event.getDeviceId());
157-
StringBuilder stringBuilder = new StringBuilder();
158-
Formatter formatter = new Formatter(stringBuilder, Locale.getDefault());
15952

160-
switch (event.getType()) {
161-
case Event.TYPE_COMMAND_RESULT:
162-
formatter.format(MESSAGE_TEMPLATE_TYPE_COMMAND_RESULT, device.getName(), event.getServerTime(),
163-
position.getAttributes().get(Position.KEY_RESULT));
164-
break;
165-
case Event.TYPE_DEVICE_ONLINE:
166-
formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_ONLINE, device.getName(), event.getServerTime());
167-
break;
168-
case Event.TYPE_DEVICE_UNKNOWN:
169-
formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_UNKNOWN, device.getName(), event.getServerTime());
170-
break;
171-
case Event.TYPE_DEVICE_OFFLINE:
172-
formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_OFFLINE, device.getName(), event.getServerTime());
173-
break;
174-
case Event.TYPE_DEVICE_MOVING:
175-
formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_MOVING, device.getName(), position.getFixTime(),
176-
position.getLatitude(), position.getLongitude());
177-
break;
178-
case Event.TYPE_DEVICE_STOPPED:
179-
formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_STOPPED, device.getName(), position.getFixTime(),
180-
position.getLatitude(), position.getLongitude());
181-
break;
182-
case Event.TYPE_DEVICE_OVERSPEED:
183-
formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_OVERSPEED, device.getName(), position.getFixTime(),
184-
position.getLatitude(), position.getLongitude(), formatSpeed(userId, position.getSpeed()));
185-
break;
186-
case Event.TYPE_GEOFENCE_ENTER:
187-
formatter.format(MESSAGE_TEMPLATE_TYPE_GEOFENCE_ENTER, device.getName(), position.getFixTime(),
188-
position.getLatitude(), position.getLongitude(),
189-
Context.getGeofenceManager().getGeofence(event.getGeofenceId()).getName());
190-
break;
191-
case Event.TYPE_GEOFENCE_EXIT:
192-
formatter.format(MESSAGE_TEMPLATE_TYPE_GEOFENCE_EXIT, device.getName(), position.getFixTime(),
193-
position.getLatitude(), position.getLongitude(),
194-
Context.getGeofenceManager().getGeofence(event.getGeofenceId()).getName());
195-
break;
196-
case Event.TYPE_ALARM:
197-
formatter.format(MESSAGE_TEMPLATE_TYPE_ALARM, device.getName(), event.getServerTime(),
198-
position.getLatitude(), position.getLongitude(),
199-
position.getAttributes().get(Position.KEY_ALARM));
200-
break;
201-
case Event.TYPE_IGNITION_ON:
202-
formatter.format(MESSAGE_TEMPLATE_TYPE_IGNITION_ON, device.getName(), position.getFixTime(),
203-
position.getLatitude(), position.getLongitude());
204-
break;
205-
case Event.TYPE_IGNITION_OFF:
206-
formatter.format(MESSAGE_TEMPLATE_TYPE_IGNITION_OFF, device.getName(), position.getFixTime(),
207-
position.getLatitude(), position.getLongitude());
208-
break;
209-
case Event.TYPE_MAINTENANCE:
210-
formatter.format(MESSAGE_TEMPLATE_TYPE_MAINTENANCE, device.getName(), position.getFixTime(),
211-
position.getLatitude(), position.getLongitude());
212-
break;
213-
default:
214-
formatter.format("Unknown type");
215-
break;
53+
VelocityContext velocityContext = new VelocityContext();
54+
velocityContext.put("device", device);
55+
velocityContext.put("event", event);
56+
if (position != null) {
57+
velocityContext.put("position", position);
58+
velocityContext.put("speedUnits", ReportUtils.getSpeedUnit(userId));
59+
}
60+
if (event.getGeofenceId() != 0) {
61+
velocityContext.put("geofence", Context.getGeofenceManager().getGeofence(event.getGeofenceId()));
21662
}
217-
String result = formatter.toString();
218-
formatter.close();
219-
return result;
220-
}
22163

222-
private static String formatSpeed(long userId, double speed) {
223-
DecimalFormat df = new DecimalFormat("#.##");
224-
String result = df.format(speed) + " kn";
225-
switch (Context.getPermissionsManager().getUser(userId).getSpeedUnit()) {
226-
case "kmh":
227-
result = df.format(UnitsConverter.kphFromKnots(speed)) + " km/h";
228-
break;
229-
case "mph":
230-
result = df.format(UnitsConverter.mphFromKnots(speed)) + " mph";
231-
break;
232-
default:
233-
break;
64+
Template template = null;
65+
try {
66+
template = Context.getVelocityEngine().getTemplate(event.getType() + ".vm");
67+
} catch (ResourceNotFoundException error) {
68+
Log.warning(error);
23469
}
235-
return result;
70+
if (template == null) {
71+
template = Context.getVelocityEngine().getTemplate("unknown.vm");
72+
}
73+
74+
StringWriter writer = new StringWriter();
75+
template.merge(velocityContext, writer);
76+
String subject = (String) velocityContext.get("subject");
77+
return new MailMessage(subject, writer.toString());
23678
}
23779
}

src/org/traccar/notification/NotificationMail.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.traccar.model.Event;
3030
import org.traccar.model.Position;
3131
import org.traccar.model.User;
32+
import org.traccar.notification.NotificationFormatter.MailMessage;
3233

3334
public final class NotificationMail {
3435

@@ -106,8 +107,9 @@ public static void sendMailSync(long userId, Event event, Position position) thr
106107
}
107108

108109
message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
109-
message.setSubject(NotificationFormatter.formatTitle(userId, event, position));
110-
message.setText(NotificationFormatter.formatMessage(userId, event, position));
110+
MailMessage mailMessage = NotificationFormatter.formatMessage(userId, event, position);
111+
message.setSubject(mailMessage.getSubject());
112+
message.setContent(mailMessage.getBody(), "text/html; charset=utf-8");
111113

112114
Transport transport = session.getTransport();
113115
try {

templates/mail/alarm.vm

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#set($subject = "$device.name: alarm!")
2+
<!DOCTYPE html>
3+
<html>
4+
<body>
5+
Device: $device.name<br>
6+
Alarm: $position.getString("alarm")<br>
7+
Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
8+
Time: $event.serverTime<br>
9+
</body>
10+
</html>

templates/mail/commandResult.vm

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#set($subject = "$device.name: command result received")
2+
<!DOCTYPE html>
3+
<html>
4+
<body>
5+
Device: $device.name<br>
6+
Result: $position.getString("result")<br>
7+
Time: $event.serverTime<br>
8+
</body>
9+
</html>

templates/mail/deviceMoving.vm

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#set($subject = "$device.name: moving")
2+
<!DOCTYPE html>
3+
<html>
4+
<body>
5+
Device: $device.name<br>
6+
Moving<br>
7+
Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
8+
Time: $event.serverTime<br>
9+
</body>
10+
</html>

templates/mail/deviceOffline.vm

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#set($subject = "$device.name: offline")
2+
<!DOCTYPE html>
3+
<html>
4+
<body>
5+
Device: $device.name<br>
6+
Offline<br>
7+
Time: $event.serverTime<br>
8+
</body>
9+
</html>

templates/mail/deviceOnline.vm

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#set($subject = "$device.name: online")
2+
<!DOCTYPE html>
3+
<html>
4+
<body>
5+
Device: $device.name<br>
6+
Online<br>
7+
Time: $event.serverTime<br>
8+
</body>
9+
</html>

0 commit comments

Comments
 (0)