From d898163368bdaaee17a2982ec219e997fd0c9b1f Mon Sep 17 00:00:00 2001 From: gustavofarias Date: Sat, 15 Apr 2023 16:33:54 -0300 Subject: Translate camel case names of alarms into proper readable names. --- templates/full/alarm.vm | 80 +++++++++++++++++++++++++++++++++++++++++++++++- templates/short/alarm.vm | 80 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 158 insertions(+), 2 deletions(-) (limited to 'templates') diff --git a/templates/full/alarm.vm b/templates/full/alarm.vm index 9fae1f13b..fb596ecde 100644 --- a/templates/full/alarm.vm +++ b/templates/full/alarm.vm @@ -1,9 +1,87 @@ #set($subject = "$device.name: alarm!") +#set($alarmName = $position.getString("alarm")) +#if( $alarmName == "general") + #set($alarmName = "General") +#elseif($alarmName == "sos") + #set($alarmName = "SOS") +#elseif($alarmName == "vibration") + #set($alarmName = "Vibration") +#elseif($alarmName == "movement") + #set($alarmName = "Movement") +#elseif($alarmName == "lowspeed") + #set($alarmName = "Low Speed") +#elseif($alarmName == "overspeed") + #set($alarmName = "Overspeed") +#elseif($alarmName == "fallDown") + #set($alarmName = "Fall Down") +#elseif($alarmName == "lowPower") + #set($alarmName = "Low Power") +#elseif($alarmName == "lowBattery") + #set($alarmName = "Low Battery") +#elseif($alarmName == "fault") + #set($alarmName = "Fault") +#elseif($alarmName == "powerOff") + #set($alarmName = "Power Off") +#elseif($alarmName == "powerOn") + #set($alarmName = "Power On") +#elseif($alarmName == "door") + #set($alarmName = "Door") +#elseif($alarmName == "lock") + #set($alarmName = "Lock") +#elseif($alarmName == "unlock") + #set($alarmName = "Unlock") +#elseif($alarmName == "geofence") + #set($alarmName = "Geofence") +#elseif($alarmName == "geofenceEnter") + #set($alarmName = "Geofence Enter") +#elseif($alarmName == "geofenceExit") + #set($alarmName = "Geofence Exit") +#elseif($alarmName == "gpsAntennaCut") + #set($alarmName = "GPS Antenna Cut") +#elseif($alarmName == "accident") + #set($alarmName = "Accident") +#elseif($alarmName == "tow") + #set($alarmName = "Tow") +#elseif($alarmName == "idle") + #set($alarmName = "Idle") +#elseif($alarmName == "highRpm") + #set($alarmName = "High RPM") +#elseif($alarmName == "hardAcceleration") + #set($alarmName = "Hard Acceleration") +#elseif($alarmName == "hardBraking") + #set($alarmName = "Hard Braking") +#elseif($alarmName == "hardCornering") + #set($alarmName = "Hard Cornering") +#elseif($alarmName == "laneChange") + #set($alarmName = "Lane Change") +#elseif($alarmName == "fatigueDriving") + #set($alarmName = "Fatigue Driving") +#elseif($alarmName == "powerCut") + #set($alarmName = "Power Cut") +#elseif($alarmName == "powerRestored") + #set($alarmName = "Power Restored") +#elseif($alarmName == "jamming") + #set($alarmName = "Jamming") +#elseif($alarmName == "temperature") + #set($alarmName = "Temperature") +#elseif($alarmName == "parking") + #set($alarmName = "Parking") +#elseif($alarmName == "bonnet") + #set($alarmName = "Bonnet") +#elseif($alarmName == "footBrake") + #set($alarmName = "Foot Brake") +#elseif($alarmName == "fuelLeak") + #set($alarmName = "Fuel Leak") +#elseif($alarmName == "tampering") + #set($alarmName = "Tampering") +#elseif($alarmName == "removing") + #set($alarmName = "Removing") +#end Device: $device.name
-Alarm: $position.getString("alarm")
+Alarm: $alarmName
Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
Point: #{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}

diff --git a/templates/short/alarm.vm b/templates/short/alarm.vm index 15970dab8..effcb8f15 100644 --- a/templates/short/alarm.vm +++ b/templates/short/alarm.vm @@ -1,2 +1,80 @@ #set($subject = "$device.name: alarm!") -$device.name alarm: $position.getString("alarm") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) +#set($alarmName = $position.getString("alarm")) +#if( $alarmName == "general") + #set($alarmName = "General") +#elseif($alarmName == "sos") + #set($alarmName = "SOS") +#elseif($alarmName == "vibration") + #set($alarmName = "Vibration") +#elseif($alarmName == "movement") + #set($alarmName = "Movement") +#elseif($alarmName == "lowspeed") + #set($alarmName = "Low Speed") +#elseif($alarmName == "overspeed") + #set($alarmName = "Overspeed") +#elseif($alarmName == "fallDown") + #set($alarmName = "Fall Down") +#elseif($alarmName == "lowPower") + #set($alarmName = "Low Power") +#elseif($alarmName == "lowBattery") + #set($alarmName = "Low Battery") +#elseif($alarmName == "fault") + #set($alarmName = "Fault") +#elseif($alarmName == "powerOff") + #set($alarmName = "Power Off") +#elseif($alarmName == "powerOn") + #set($alarmName = "Power On") +#elseif($alarmName == "door") + #set($alarmName = "Door") +#elseif($alarmName == "lock") + #set($alarmName = "Lock") +#elseif($alarmName == "unlock") + #set($alarmName = "Unlock") +#elseif($alarmName == "geofence") + #set($alarmName = "Geofence") +#elseif($alarmName == "geofenceEnter") + #set($alarmName = "Geofence Enter") +#elseif($alarmName == "geofenceExit") + #set($alarmName = "Geofence Exit") +#elseif($alarmName == "gpsAntennaCut") + #set($alarmName = "GPS Antenna Cut") +#elseif($alarmName == "accident") + #set($alarmName = "Accident") +#elseif($alarmName == "tow") + #set($alarmName = "Tow") +#elseif($alarmName == "idle") + #set($alarmName = "Idle") +#elseif($alarmName == "highRpm") + #set($alarmName = "High RPM") +#elseif($alarmName == "hardAcceleration") + #set($alarmName = "Hard Acceleration") +#elseif($alarmName == "hardBraking") + #set($alarmName = "Hard Braking") +#elseif($alarmName == "hardCornering") + #set($alarmName = "Hard Cornering") +#elseif($alarmName == "laneChange") + #set($alarmName = "Lane Change") +#elseif($alarmName == "fatigueDriving") + #set($alarmName = "Fatigue Driving") +#elseif($alarmName == "powerCut") + #set($alarmName = "Power Cut") +#elseif($alarmName == "powerRestored") + #set($alarmName = "Power Restored") +#elseif($alarmName == "jamming") + #set($alarmName = "Jamming") +#elseif($alarmName == "temperature") + #set($alarmName = "Temperature") +#elseif($alarmName == "parking") + #set($alarmName = "Parking") +#elseif($alarmName == "bonnet") + #set($alarmName = "Bonnet") +#elseif($alarmName == "footBrake") + #set($alarmName = "Foot Brake") +#elseif($alarmName == "fuelLeak") + #set($alarmName = "Fuel Leak") +#elseif($alarmName == "tampering") + #set($alarmName = "Tampering") +#elseif($alarmName == "removing") + #set($alarmName = "Removing") +#end +$device.name alarm: $alarmName at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) -- cgit v1.2.3 From dee954b9f28c129215e9fa600e6860e2d4adb673 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 25 May 2023 06:48:50 -0700 Subject: Remove status event links --- templates/full/deviceOffline.vm | 1 - templates/full/deviceOnline.vm | 1 - templates/full/deviceUnknown.vm | 1 - 3 files changed, 3 deletions(-) (limited to 'templates') diff --git a/templates/full/deviceOffline.vm b/templates/full/deviceOffline.vm index 8f2c515b2..6d2122624 100644 --- a/templates/full/deviceOffline.vm +++ b/templates/full/deviceOffline.vm @@ -5,7 +5,6 @@ Device: $device.name
Offline
Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
-Link: $webUrl?eventId=$event.id

Unsubscribe diff --git a/templates/full/deviceOnline.vm b/templates/full/deviceOnline.vm index 81a4ccbc8..02260c4fb 100644 --- a/templates/full/deviceOnline.vm +++ b/templates/full/deviceOnline.vm @@ -5,7 +5,6 @@ Device: $device.name
Online
Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
-Link: $webUrl?eventId=$event.id

Unsubscribe diff --git a/templates/full/deviceUnknown.vm b/templates/full/deviceUnknown.vm index e012845e6..e99981069 100644 --- a/templates/full/deviceUnknown.vm +++ b/templates/full/deviceUnknown.vm @@ -5,7 +5,6 @@ Device: $device.name
Status is unknown
Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
-Link: $webUrl?eventId=$event.id

Unsubscribe -- cgit v1.2.3 From 6c78fc675501ab348f877e423b984d1a2a132ef1 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Wed, 4 Oct 2023 08:16:10 +0800 Subject: Event for sent queued commands --- src/main/java/org/traccar/database/CommandsManager.java | 14 +++++++++++++- src/main/java/org/traccar/model/Event.java | 1 + templates/full/queuedCommandSent.vm | 11 +++++++++++ templates/short/queuedCommandSent.vm | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 templates/full/queuedCommandSent.vm create mode 100644 templates/short/queuedCommandSent.vm (limited to 'templates') diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java index bef696e58..90180b989 100644 --- a/src/main/java/org/traccar/database/CommandsManager.java +++ b/src/main/java/org/traccar/database/CommandsManager.java @@ -22,6 +22,7 @@ import org.traccar.broadcast.BroadcastInterface; import org.traccar.broadcast.BroadcastService; import org.traccar.model.Command; import org.traccar.model.Device; +import org.traccar.model.Event; import org.traccar.model.Position; import org.traccar.model.QueuedCommand; import org.traccar.session.ConnectionManager; @@ -38,6 +39,8 @@ import jakarta.annotation.Nullable; import jakarta.inject.Inject; import jakarta.inject.Singleton; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.stream.Collectors; @Singleton @@ -48,16 +51,19 @@ public class CommandsManager implements BroadcastInterface { private final SmsManager smsManager; private final ConnectionManager connectionManager; private final BroadcastService broadcastService; + private final NotificationManager notificationManager; @Inject public CommandsManager( Storage storage, ServerManager serverManager, @Nullable SmsManager smsManager, - ConnectionManager connectionManager, BroadcastService broadcastService) { + ConnectionManager connectionManager, BroadcastService broadcastService, + NotificationManager notificationManager) { this.storage = storage; this.serverManager = serverManager; this.smsManager = smsManager; this.connectionManager = connectionManager; this.broadcastService = broadcastService; + this.notificationManager = notificationManager; broadcastService.registerListener(this); } @@ -103,10 +109,16 @@ public class CommandsManager implements BroadcastInterface { new Columns.All(), new Condition.Equals("deviceId", deviceId), new Order("id", false, count))); + Map events = new HashMap<>(); for (var command : commands) { storage.removeObject(QueuedCommand.class, new Request( new Condition.Equals("id", command.getId()))); + + Event event = new Event(Event.TYPE_QUEUED_COMMAND_SENT, command.getDeviceId()); + event.set("id", command.getId()); + events.put(event, null); } + notificationManager.updateEvents(events); return commands.stream().map(QueuedCommand::toCommand).collect(Collectors.toList()); } catch (StorageException e) { throw new RuntimeException(e); diff --git a/src/main/java/org/traccar/model/Event.java b/src/main/java/org/traccar/model/Event.java index 0e851d748..6f90de9da 100644 --- a/src/main/java/org/traccar/model/Event.java +++ b/src/main/java/org/traccar/model/Event.java @@ -46,6 +46,7 @@ public class Event extends Message { public static final String TYPE_DEVICE_UNKNOWN = "deviceUnknown"; public static final String TYPE_DEVICE_OFFLINE = "deviceOffline"; public static final String TYPE_DEVICE_INACTIVE = "deviceInactive"; + public static final String TYPE_QUEUED_COMMAND_SENT = "queuedCommandSent"; public static final String TYPE_DEVICE_MOVING = "deviceMoving"; public static final String TYPE_DEVICE_STOPPED = "deviceStopped"; diff --git a/templates/full/queuedCommandSent.vm b/templates/full/queuedCommandSent.vm new file mode 100644 index 000000000..148dd2094 --- /dev/null +++ b/templates/full/queuedCommandSent.vm @@ -0,0 +1,11 @@ +#set($subject = "$device.name: queued command sent") + + + +Device: $device.name
+Queued command sent
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone)
+
+Unsubscribe + + diff --git a/templates/short/queuedCommandSent.vm b/templates/short/queuedCommandSent.vm new file mode 100644 index 000000000..67f031280 --- /dev/null +++ b/templates/short/queuedCommandSent.vm @@ -0,0 +1,2 @@ +#set($subject = "$device.name: queued command sent") +Queued command sent to $device.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.eventTime, $locale, $timezone) -- cgit v1.2.3 From 346a860b0aae7097445aa5aa22f37a88d9d7955e Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 7 Jan 2024 12:13:47 -0800 Subject: User and device expiration emails --- src/main/java/org/traccar/config/Keys.java | 38 ++++-- .../java/org/traccar/schedule/ScheduleManager.java | 3 +- .../java/org/traccar/schedule/TaskExpirations.java | 130 +++++++++++++++++++++ templates/full/deviceExpiration.vm | 7 ++ templates/full/deviceExpirationReminder.vm | 7 ++ templates/full/userExpiration.vm | 7 ++ templates/full/userExpirationReminder.vm | 7 ++ 7 files changed, 189 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/traccar/schedule/TaskExpirations.java create mode 100644 templates/full/deviceExpiration.vm create mode 100644 templates/full/deviceExpirationReminder.vm create mode 100644 templates/full/userExpiration.vm create mode 100644 templates/full/userExpirationReminder.vm (limited to 'templates') diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java index 3059c4f4b..3528dafa0 100644 --- a/src/main/java/org/traccar/config/Keys.java +++ b/src/main/java/org/traccar/config/Keys.java @@ -489,14 +489,6 @@ public final class Keys { "database.throttleUnknown", List.of(KeyType.CONFIG)); - /** - * By default, server syncs with the database if it encounters and unknown device. This flag allows to disable that - * behavior to improve performance in some cases. - */ - public static final ConfigKey DATABASE_IGNORE_UNKNOWN = new BooleanConfigKey( - "database.ignoreUnknown", - List.of(KeyType.CONFIG)); - /** * Automatically register unknown devices in the database. */ @@ -664,7 +656,7 @@ public final class Keys { /** * OpenID Connect Authorization URL. * This can usually be found in the documentation of your identity provider or by using the well-known - * configuration endpoint, eg. https://auth.example.com//.well-known/openid-configuration + * configuration endpoint, e.g. https://auth.example.com//.well-known/openid-configuration * Required to enable SSO if openid.issuerUrl is not set. */ public static final ConfigKey OPENID_AUTH_URL = new StringConfigKey( @@ -1225,6 +1217,34 @@ public final class Keys { "notificator.telegram.sendLocation", List.of(KeyType.CONFIG)); + /** + * Enable user expiration email notification. + */ + public static final ConfigKey NOTIFICATION_EXPIRATION_USER = new BooleanConfigKey( + "notification.expiration.user", + List.of(KeyType.CONFIG)); + + /** + * User expiration reminder. Value in milliseconds. + */ + public static final ConfigKey NOTIFICATION_EXPIRATION_USER_REMINDER = new LongConfigKey( + "notification.expiration.user.reminder", + List.of(KeyType.CONFIG)); + + /** + * Enable device expiration email notification. + */ + public static final ConfigKey NOTIFICATION_EXPIRATION_DEVICE = new BooleanConfigKey( + "notification.expiration.device", + List.of(KeyType.CONFIG)); + + /** + * Device expiration reminder. Value in milliseconds. + */ + public static final ConfigKey NOTIFICATION_EXPIRATION_DEVICE_REMINDER = new LongConfigKey( + "notification.expiration.device.reminder", + List.of(KeyType.CONFIG)); + /** * Maximum time period for reports in seconds. Can be useful to prevent users to request unreasonably long reports. * By default there is no limit. diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java index 38e8f281c..3756d955b 100644 --- a/src/main/java/org/traccar/schedule/ScheduleManager.java +++ b/src/main/java/org/traccar/schedule/ScheduleManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 - 2023 Anton Tananaev (anton@traccar.org) + * Copyright 2020 - 2024 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ public class ScheduleManager implements LifecycleObject { public void start() { executor = Executors.newSingleThreadScheduledExecutor(); var tasks = List.of( + TaskExpirations.class, TaskDeleteTemporary.class, TaskReports.class, TaskDeviceInactivityCheck.class, diff --git a/src/main/java/org/traccar/schedule/TaskExpirations.java b/src/main/java/org/traccar/schedule/TaskExpirations.java new file mode 100644 index 000000000..94f855c5f --- /dev/null +++ b/src/main/java/org/traccar/schedule/TaskExpirations.java @@ -0,0 +1,130 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * 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 org.traccar.schedule; + +import jakarta.inject.Inject; +import jakarta.mail.MessagingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.mail.MailManager; +import org.traccar.model.Device; +import org.traccar.model.Disableable; +import org.traccar.model.Server; +import org.traccar.model.User; +import org.traccar.notification.TextTemplateFormatter; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +public class TaskExpirations implements ScheduleTask { + + private static final Logger LOGGER = LoggerFactory.getLogger(TaskExpirations.class); + + private static final long CHECK_PERIOD_HOURS = 1; + + private final Config config; + private final Storage storage; + private final TextTemplateFormatter textTemplateFormatter; + private final MailManager mailManager; + + @Inject + public TaskExpirations( + Config config, Storage storage, TextTemplateFormatter textTemplateFormatter, MailManager mailManager) { + this.config = config; + this.storage = storage; + this.textTemplateFormatter = textTemplateFormatter; + this.mailManager = mailManager; + } + + @Override + public void schedule(ScheduledExecutorService executor) { + executor.scheduleAtFixedRate(this, CHECK_PERIOD_HOURS, CHECK_PERIOD_HOURS, TimeUnit.HOURS); + } + + private boolean checkTimeTrigger(Disableable disableable, long currentTime, long offsetTime) { + if (disableable.getExpirationTime() != null) { + long previousTime = currentTime - TimeUnit.HOURS.toMillis(CHECK_PERIOD_HOURS); + long expirationTime = disableable.getExpirationTime().getTime() + offsetTime; + return previousTime < expirationTime && currentTime >= expirationTime; + } + return false; + } + + private void sendUserExpiration( + Server server, User user, String template) throws MessagingException { + var velocityContext = textTemplateFormatter.prepareContext(server, user); + velocityContext.put("expiration", user.getExpirationTime()); + var fullMessage = textTemplateFormatter.formatMessage(velocityContext, template, "full"); + mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody()); + } + + private void sendDeviceExpiration( + Server server, Device device, String template) throws MessagingException, StorageException { + var users = storage.getObjects(User.class, new Request( + new Columns.All(), new Condition.Permission(User.class, Device.class, device.getId()))); + for (User user : users) { + var velocityContext = textTemplateFormatter.prepareContext(server, user); + velocityContext.put("expiration", device.getExpirationTime()); + velocityContext.put("device", device); + var fullMessage = textTemplateFormatter.formatMessage(velocityContext, template, "full"); + mailManager.sendMessage(user, true, fullMessage.getSubject(), fullMessage.getBody()); + } + } + + @Override + public void run() { + try { + + long currentTime = System.currentTimeMillis(); + Server server = storage.getObject(Server.class, new Request(new Columns.All())); + + if (config.getBoolean(Keys.NOTIFICATION_EXPIRATION_USER)) { + long reminder = config.getLong(Keys.NOTIFICATION_EXPIRATION_USER_REMINDER); + var users = storage.getObjects(User.class, new Request(new Columns.All())); + for (User user : users) { + if (checkTimeTrigger(user, currentTime, 0)) { + sendUserExpiration(server, user, "userExpiration"); + } else if (reminder > 0 && checkTimeTrigger(user, currentTime, -reminder)) { + sendUserExpiration(server, user, "userExpirationReminder"); + } + } + } + + if (config.getBoolean(Keys.NOTIFICATION_EXPIRATION_DEVICE)) { + long reminder = config.getLong(Keys.NOTIFICATION_EXPIRATION_USER_REMINDER); + var devices = storage.getObjects(Device.class, new Request(new Columns.All())); + for (Device device : devices) { + if (checkTimeTrigger(device, currentTime, 0)) { + sendDeviceExpiration(server, device, "deviceExpiration"); + } else if (reminder > 0 && checkTimeTrigger(device, currentTime, -reminder)) { + sendDeviceExpiration(server, device, "deviceExpirationReminder"); + } + } + } + + } catch (StorageException | MessagingException e) { + LOGGER.warn("Failed to check expirations", e); + } + } + +} diff --git a/templates/full/deviceExpiration.vm b/templates/full/deviceExpiration.vm new file mode 100644 index 000000000..879b31778 --- /dev/null +++ b/templates/full/deviceExpiration.vm @@ -0,0 +1,7 @@ +#set($subject = "Device expiration") + + + +Your device $device.name has expired. + + diff --git a/templates/full/deviceExpirationReminder.vm b/templates/full/deviceExpirationReminder.vm new file mode 100644 index 000000000..aa47ac0ed --- /dev/null +++ b/templates/full/deviceExpirationReminder.vm @@ -0,0 +1,7 @@ +#set($subject = "Device expiration reminder") + + + +Your device $device.name will expire on $dateTool.format("YYYY-MM-dd", $expiration, $locale, $timezone). + + diff --git a/templates/full/userExpiration.vm b/templates/full/userExpiration.vm new file mode 100644 index 000000000..43fe2563e --- /dev/null +++ b/templates/full/userExpiration.vm @@ -0,0 +1,7 @@ +#set($subject = "Account expiration") + + + +Your user account has expired. + + diff --git a/templates/full/userExpirationReminder.vm b/templates/full/userExpirationReminder.vm new file mode 100644 index 000000000..ecdee0588 --- /dev/null +++ b/templates/full/userExpirationReminder.vm @@ -0,0 +1,7 @@ +#set($subject = "Account expiration reminder") + + + +Your user account will expire on $dateTool.format("YYYY-MM-dd", $expiration, $locale, $timezone). + + -- cgit v1.2.3 From b37c5aa05bdd903af998435e073638f42ae9e8d1 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 24 Feb 2024 17:23:39 -0800 Subject: Remove unknown template --- .../org/traccar/notification/TextTemplateFormatter.java | 15 ++------------- templates/full/unknown.vm | 7 ------- templates/short/unknown.vm | 2 -- 3 files changed, 2 insertions(+), 22 deletions(-) delete mode 100644 templates/full/unknown.vm delete mode 100644 templates/short/unknown.vm (limited to 'templates') diff --git a/src/main/java/org/traccar/notification/TextTemplateFormatter.java b/src/main/java/org/traccar/notification/TextTemplateFormatter.java index ab90d76d4..cc6935982 100644 --- a/src/main/java/org/traccar/notification/TextTemplateFormatter.java +++ b/src/main/java/org/traccar/notification/TextTemplateFormatter.java @@ -75,19 +75,8 @@ public class TextTemplateFormatter { } public Template getTemplate(String name, String path) { - - String templateFilePath; - Template template; - - try { - templateFilePath = Paths.get(path, name + ".vm").toString(); - template = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); - } catch (ResourceNotFoundException error) { - LOGGER.warn("Notification template error", error); - templateFilePath = Paths.get(path, "unknown.vm").toString(); - template = velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); - } - return template; + String templateFilePath = Paths.get(path, name + ".vm").toString(); + return velocityEngine.getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); } public NotificationMessage formatMessage(VelocityContext velocityContext, String name, String templatePath) { diff --git a/templates/full/unknown.vm b/templates/full/unknown.vm deleted file mode 100644 index 566ce0d42..000000000 --- a/templates/full/unknown.vm +++ /dev/null @@ -1,7 +0,0 @@ -#set($subject = "Unknown type") - - - -Unknown type - - diff --git a/templates/short/unknown.vm b/templates/short/unknown.vm deleted file mode 100644 index 2f9d5e3af..000000000 --- a/templates/short/unknown.vm +++ /dev/null @@ -1,2 +0,0 @@ -#set($subject = "Unknown type") -Unknown type -- cgit v1.2.3 From f1680218e85ff3310eb5898d8ec28feaac5797c6 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 2 Mar 2024 21:31:37 -0800 Subject: Add device export report --- .../org/traccar/api/resource/ReportResource.java | 15 ++++ .../org/traccar/reports/DevicesReportProvider.java | 78 +++++++++++++++++++++ .../traccar/reports/model/DeviceReportItem.java | 48 +++++++++++++ templates/export/devices.xlsx | Bin 0 -> 9282 bytes 4 files changed, 141 insertions(+) create mode 100644 src/main/java/org/traccar/reports/DevicesReportProvider.java create mode 100644 src/main/java/org/traccar/reports/model/DeviceReportItem.java create mode 100644 templates/export/devices.xlsx (limited to 'templates') diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java index b4882f219..55a96fa90 100644 --- a/src/main/java/org/traccar/api/resource/ReportResource.java +++ b/src/main/java/org/traccar/api/resource/ReportResource.java @@ -23,6 +23,7 @@ import org.traccar.model.Position; import org.traccar.model.Report; import org.traccar.model.UserRestrictions; import org.traccar.reports.CombinedReportProvider; +import org.traccar.reports.DevicesReportProvider; import org.traccar.reports.EventsReportProvider; import org.traccar.reports.RouteReportProvider; import org.traccar.reports.StopsReportProvider; @@ -77,6 +78,9 @@ public class ReportResource extends SimpleObjectResource { @Inject private TripsReportProvider tripsReportProvider; + @Inject + private DevicesReportProvider devicesReportProvider; + @Inject private ReportMailer reportMailer; @@ -319,4 +323,15 @@ public class ReportResource extends SimpleObjectResource { return getStopsExcel(deviceIds, groupIds, from, to, type.equals("mail")); } + @Path("devices/{type:xlsx|mail}") + @GET + @Produces(EXCEL) + public Response geDevicesExcel( + @PathParam("type") String type) throws StorageException { + permissionsService.checkRestriction(getUserId(), UserRestrictions::getDisableReports); + return executeReport(getUserId(), type.equals("mail"), stream -> { + devicesReportProvider.getExcel(stream, getUserId()); + }); + } + } diff --git a/src/main/java/org/traccar/reports/DevicesReportProvider.java b/src/main/java/org/traccar/reports/DevicesReportProvider.java new file mode 100644 index 000000000..7b4294d53 --- /dev/null +++ b/src/main/java/org/traccar/reports/DevicesReportProvider.java @@ -0,0 +1,78 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * 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 org.traccar.reports; + +import jakarta.inject.Inject; +import org.jxls.util.JxlsHelper; +import org.traccar.config.Config; +import org.traccar.config.Keys; +import org.traccar.helper.model.PositionUtil; +import org.traccar.model.Device; +import org.traccar.model.Message; +import org.traccar.model.User; +import org.traccar.reports.common.ReportUtils; +import org.traccar.reports.model.DeviceReportItem; +import org.traccar.storage.Storage; +import org.traccar.storage.StorageException; +import org.traccar.storage.query.Columns; +import org.traccar.storage.query.Condition; +import org.traccar.storage.query.Request; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.stream.Collectors; + +public class DevicesReportProvider { + + private final Config config; + private final ReportUtils reportUtils; + private final Storage storage; + + @Inject + public DevicesReportProvider(Config config, ReportUtils reportUtils, Storage storage) { + this.config = config; + this.reportUtils = reportUtils; + this.storage = storage; + } + + public Collection getObjects(long userId) throws StorageException { + + var positions = PositionUtil.getLatestPositions(storage, userId).stream() + .collect(Collectors.toMap(Message::getDeviceId, p -> p)); + + return storage.getObjects(Device.class, new Request( + new Columns.All(), + new Condition.Permission(User.class, userId, Device.class))).stream() + .map(device -> new DeviceReportItem(device, positions.get(device.getId()))) + .collect(Collectors.toUnmodifiableList()); + } + + public void getExcel(OutputStream outputStream, long userId) throws StorageException, IOException { + + File file = Paths.get(config.getString(Keys.TEMPLATES_ROOT), "export", "devices.xlsx").toFile(); + try (InputStream inputStream = new FileInputStream(file)) { + var context = reportUtils.initializeContext(userId); + context.putVar("items", getObjects(userId)); + JxlsHelper.getInstance().setUseFastFormulaProcessor(false) + .processTemplate(inputStream, outputStream, context); + } + } +} diff --git a/src/main/java/org/traccar/reports/model/DeviceReportItem.java b/src/main/java/org/traccar/reports/model/DeviceReportItem.java new file mode 100644 index 000000000..74cd5f32c --- /dev/null +++ b/src/main/java/org/traccar/reports/model/DeviceReportItem.java @@ -0,0 +1,48 @@ +/* + * Copyright 2024 Anton Tananaev (anton@traccar.org) + * + * 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 org.traccar.reports.model; + +import org.traccar.model.Device; +import org.traccar.model.Position; + +public class DeviceReportItem { + + public DeviceReportItem(Device device, Position position) { + this.device = device; + this.position = position; + } + + private Device device; + + public Device getDevice() { + return device; + } + + public void setDevice(Device device) { + this.device = device; + } + + private Position position; + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + +} diff --git a/templates/export/devices.xlsx b/templates/export/devices.xlsx new file mode 100644 index 000000000..ab6d9d5be Binary files /dev/null and b/templates/export/devices.xlsx differ -- cgit v1.2.3