aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbyss777 <abyss@fox5.ru>2016-12-14 17:34:34 +0500
committerAbyss777 <abyss@fox5.ru>2016-12-14 18:02:17 +0500
commitfff8f0c792606daff151624479d9ae9dc54187d5 (patch)
tree97e0ab38de08aba1a7b7adf90283df82d850a3ad
parent4557cf912fada1dc249333aa05215b0116b8ebab (diff)
downloadtraccar-server-fff8f0c792606daff151624479d9ae9dc54187d5.tar.gz
traccar-server-fff8f0c792606daff151624479d9ae9dc54187d5.tar.bz2
traccar-server-fff8f0c792606daff151624479d9ae9dc54187d5.zip
Calendars implementation
-rw-r--r--pom.xml5
-rw-r--r--schema/changelog-3.10.xml45
-rw-r--r--schema/changelog-master.xml1
-rw-r--r--setup/default.xml38
-rw-r--r--src/org/traccar/Context.java8
-rw-r--r--src/org/traccar/api/resource/CalendarPermissionResource.java57
-rw-r--r--src/org/traccar/api/resource/CalendarResource.java85
-rw-r--r--src/org/traccar/database/CalendarManager.java112
-rw-r--r--src/org/traccar/database/DataManager.java43
-rw-r--r--src/org/traccar/database/PermissionsManager.java6
-rw-r--r--src/org/traccar/database/QueryBuilder.java30
-rw-r--r--src/org/traccar/events/GeofenceEventHandler.java20
-rw-r--r--src/org/traccar/model/Calendar.java86
-rw-r--r--src/org/traccar/model/CalendarPermission.java40
-rw-r--r--src/org/traccar/model/Geofence.java9
-rw-r--r--test/org/traccar/calendar/CalendarTest.java54
16 files changed, 631 insertions, 8 deletions
diff --git a/pom.xml b/pom.xml
index 45df20efc..b93ff8e44 100644
--- a/pom.xml
+++ b/pom.xml
@@ -143,6 +143,11 @@
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
+ <dependency>
+ <groupId>org.mnode.ical4j</groupId>
+ <artifactId>ical4j</artifactId>
+ <version>2.0.0</version>
+ </dependency>
</dependencies>
<build>
diff --git a/schema/changelog-3.10.xml b/schema/changelog-3.10.xml
new file mode 100644
index 000000000..8cd7b3704
--- /dev/null
+++ b/schema/changelog-3.10.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-3.10">
+
+ <changeSet author="author" id="changelog-3.10">
+
+ <createTable tableName="calendars">
+ <column name="id" type="INT" autoIncrement="true">
+ <constraints primaryKey="true" />
+ </column>
+ <column name="name" type="VARCHAR(128)">
+ <constraints nullable="false" />
+ </column>
+ <column name="calendardata" type="BLOB">
+ <constraints nullable="false" />
+ </column>
+ <column name="attributes" type="VARCHAR(4000)">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <createTable tableName="user_calendar">
+ <column name="userid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ <column name="calendarid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <addForeignKeyConstraint baseTableName="user_calendar" baseColumnNames="userid" constraintName="fk_user_calendar_userid" referencedTableName="users" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="user_calendar" baseColumnNames="calendarid" constraintName="fk_user_calendar_geofenceid" referencedTableName="calendars" referencedColumnNames="id" onDelete="CASCADE" />
+
+ <addColumn tableName="geofences">
+ <column name="calendarid" type="INT" />
+ </addColumn>
+
+ <addForeignKeyConstraint baseColumnNames="calendarid" baseTableName="geofences" constraintName="fk_geofence_calendar_calendarid" onDelete="SET NULL" onUpdate="RESTRICT" referencedColumnNames="id" referencedTableName="calendars"/>
+
+ </changeSet>
+</databaseChangeLog>
diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml
index 448015568..2aed50e72 100644
--- a/schema/changelog-master.xml
+++ b/schema/changelog-master.xml
@@ -11,4 +11,5 @@
<include file="changelog-3.7.xml" relativeToChangelogFile="true" />
<include file="changelog-3.8.xml" relativeToChangelogFile="true" />
<include file="changelog-3.9.xml" relativeToChangelogFile="true" />
+ <include file="changelog-3.10.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git a/setup/default.xml b/setup/default.xml
index ad23d7bd3..aeb8fbe54 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -206,14 +206,15 @@
</entry>
<entry key='database.insertGeofence'>
- INSERT INTO geofences (name, description, area, attributes)
- VALUES (:name, :description, :area, :attributes)
+ INSERT INTO geofences (name, description, calendarid, area, attributes)
+ VALUES (:name, :description, :calendarid, :area, :attributes)
</entry>
<entry key='database.updateGeofence'>
UPDATE geofences SET
name = :name,
description = :description,
+ calendarid = :calendarid,
area = :area,
attributes = :attributes
WHERE id = :id
@@ -315,6 +316,39 @@
INSERT INTO statistics (captureTime, activeUsers, activeDevices, requests, messagesReceived, messagesStored, attributes)
VALUES (:captureTime, :activeUsers, :activeDevices, :requests, :messagesReceived, :messagesStored, :attributes)
</entry>
+
+ <entry key='database.selectCalendarsAll'>
+ SELECT * FROM calendars
+ </entry>
+
+ <entry key='database.insertCalendar'>
+ INSERT INTO calendars (name, calendarData, attributes)
+ VALUES (:name, :calendarData, :attributes)
+ </entry>
+
+ <entry key='database.updateCalendar'>
+ UPDATE calendars SET
+ name = :name,
+ calendarData = :calendarData,
+ attributes = :attributes
+ WHERE id = :id
+ </entry>
+
+ <entry key='database.deleteCalendar'>
+ DELETE FROM calendars WHERE id = :id
+ </entry>
+
+ <entry key='database.selectCalendarPermissions'>
+ SELECT userId, calendarId FROM user_calendar
+ </entry>
+
+ <entry key='database.linkCalendar'>
+ INSERT INTO user_calendar (userId, calendarId) VALUES (:userId, :calendarId)
+ </entry>
+
+ <entry key='database.unlinkCalendar'>
+ DELETE FROM user_calendar WHERE userId = :userId AND calendarId = :calendarId
+ </entry>
<!-- PROTOCOL CONFIG -->
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 2b8860187..03e4635b6 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -23,6 +23,7 @@ import java.util.Properties;
import org.apache.velocity.app.VelocityEngine;
import org.eclipse.jetty.util.URIUtil;
import org.traccar.database.AliasesManager;
+import org.traccar.database.CalendarManager;
import org.traccar.database.ConnectionManager;
import org.traccar.database.DataManager;
import org.traccar.database.DeviceManager;
@@ -124,6 +125,12 @@ public final class Context {
return geofenceManager;
}
+ private static CalendarManager calendarManager;
+
+ public static CalendarManager getCalendarManager() {
+ return calendarManager;
+ }
+
private static NotificationManager notificationManager;
public static NotificationManager getNotificationManager() {
@@ -253,6 +260,7 @@ public final class Context {
if (config.getBoolean("event.geofenceHandler")) {
geofenceManager = new GeofenceManager(dataManager);
+ calendarManager = new CalendarManager(dataManager);
}
if (config.getBoolean("event.enable")) {
diff --git a/src/org/traccar/api/resource/CalendarPermissionResource.java b/src/org/traccar/api/resource/CalendarPermissionResource.java
new file mode 100644
index 000000000..a49254b6b
--- /dev/null
+++ b/src/org/traccar/api/resource/CalendarPermissionResource.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@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.api.resource;
+
+import java.sql.SQLException;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.CalendarPermission;
+
+@Path("permissions/calendars")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class CalendarPermissionResource extends BaseResource {
+
+ @POST
+ public Response add(CalendarPermission entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
+ Context.getPermissionsManager().checkCalendar(getUserId(), entity.getCalendarId());
+ Context.getDataManager().linkCalendar(entity.getUserId(), entity.getCalendarId());
+ Context.getCalendarManager().refreshUserCalendars();
+ return Response.ok(entity).build();
+ }
+
+ @DELETE
+ public Response remove(CalendarPermission entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
+ Context.getPermissionsManager().checkCalendar(getUserId(), entity.getCalendarId());
+ Context.getDataManager().unlinkCalendar(entity.getUserId(), entity.getCalendarId());
+ Context.getCalendarManager().refreshUserCalendars();
+ return Response.noContent().build();
+ }
+}
diff --git a/src/org/traccar/api/resource/CalendarResource.java b/src/org/traccar/api/resource/CalendarResource.java
new file mode 100644
index 000000000..0a9bb5daf
--- /dev/null
+++ b/src/org/traccar/api/resource/CalendarResource.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@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.api.resource;
+
+import java.sql.SQLException;
+import java.util.Collection;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.Calendar;
+
+@Path("calendars")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class CalendarResource extends BaseResource {
+
+ @GET
+ public Collection<Calendar> get(
+ @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
+
+ if (all) {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ return Context.getCalendarManager().getAllCalendars();
+ } else {
+ if (userId == 0) {
+ userId = getUserId();
+ }
+ Context.getPermissionsManager().checkUser(getUserId(), userId);
+ return Context.getCalendarManager().getUserCalendars(userId);
+ }
+ }
+
+ @POST
+ public Response add(Calendar entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getCalendarManager().addCalendar(entity);
+ Context.getDataManager().linkCalendar(getUserId(), entity.getId());
+ Context.getCalendarManager().refreshUserCalendars();
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @PUT
+ public Response update(Calendar entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkCalendar(getUserId(), entity.getId());
+ Context.getCalendarManager().updateCalendar(entity);
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @DELETE
+ public Response remove(@PathParam("id") long id) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkCalendar(getUserId(), id);
+ Context.getCalendarManager().removeCalendar(id);
+ return Response.noContent().build();
+ }
+}
diff --git a/src/org/traccar/database/CalendarManager.java b/src/org/traccar/database/CalendarManager.java
new file mode 100644
index 000000000..3e95f6698
--- /dev/null
+++ b/src/org/traccar/database/CalendarManager.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@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.database;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.traccar.helper.Log;
+import org.traccar.model.Calendar;
+import org.traccar.model.CalendarPermission;
+
+public class CalendarManager {
+
+ private final DataManager dataManager;
+
+ private final Map<Long, Calendar> calendars = new ConcurrentHashMap<>();
+ private final Map<Long, Set<Long>> userCalendars = new ConcurrentHashMap<>();
+
+ public CalendarManager(DataManager dataManager) {
+ this.dataManager = dataManager;
+ refreshCalendars();
+ }
+
+ public final void refreshCalendars() {
+ if (dataManager != null) {
+ try {
+ calendars.clear();
+ for (Calendar calendar : dataManager.getCalendars()) {
+ calendars.put(calendar.getId(), calendar);
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+ refreshUserCalendars();
+ }
+
+ private Set<Long> getUserCalendarIds(long userId) {
+ if (!userCalendars.containsKey(userId)) {
+ userCalendars.put(userId, new HashSet<Long>());
+ }
+ return userCalendars.get(userId);
+ }
+
+ public Collection<Calendar> getUserCalendars(long userId) {
+ ArrayList<Calendar> result = new ArrayList<>();
+ for (long calendarId : getUserCalendarIds(userId)) {
+ result.add(calendars.get(calendarId));
+ }
+ return result;
+ }
+
+ public final void refreshUserCalendars() {
+ if (dataManager != null) {
+ try {
+ userCalendars.clear();
+ for (CalendarPermission calendarsPermission : dataManager.getCalendarPermissions()) {
+ getUserCalendarIds(calendarsPermission.getUserId()).add(calendarsPermission.getCalendarId());
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+ }
+
+ public Calendar getCalendar(long calendarId) {
+ return calendars.get(calendarId);
+ }
+
+ public final void addCalendar(Calendar calendar) throws SQLException {
+ dataManager.addCalendar(calendar);
+ calendars.put(calendar.getId(), calendar);
+ }
+
+ public final void updateCalendar(Calendar calendar) throws SQLException {
+ dataManager.updateCalendar(calendar);
+ calendars.put(calendar.getId(), calendar);
+ }
+
+ public final void removeCalendar(long calendarId) throws SQLException {
+ dataManager.removeCalendar(calendarId);
+ calendars.remove(calendarId);
+ refreshUserCalendars();
+ }
+
+ public Collection<Calendar> getAllCalendars() {
+ return calendars.values();
+ }
+
+ public boolean checkCalendar(long userId, long calendarId) {
+ return getUserCalendarIds(userId).contains(calendarId);
+ }
+}
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index 8be53ad7b..278109229 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -37,6 +37,8 @@ import liquibase.resource.ResourceAccessor;
import org.traccar.Config;
import org.traccar.helper.Log;
import org.traccar.model.AttributeAlias;
+import org.traccar.model.Calendar;
+import org.traccar.model.CalendarPermission;
import org.traccar.model.Device;
import org.traccar.model.DevicePermission;
import org.traccar.model.Event;
@@ -484,4 +486,45 @@ public class DataManager {
.executeUpdate());
}
+ public Collection<Calendar> getCalendars() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectCalendarsAll"))
+ .executeQuery(Calendar.class);
+ }
+
+ public void addCalendar(Calendar calendar) throws SQLException {
+ calendar.setId(QueryBuilder.create(dataSource, getQuery("database.insertCalendar"), true)
+ .setObject(calendar)
+ .executeUpdate());
+ }
+
+ public void updateCalendar(Calendar calendar) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.updateCalendar"))
+ .setObject(calendar)
+ .executeUpdate();
+ }
+
+ public void removeCalendar(long calendarId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.deleteCalendar"))
+ .setLong("id", calendarId)
+ .executeUpdate();
+ }
+
+ public Collection<CalendarPermission> getCalendarPermissions() throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectCalendarPermissions"))
+ .executeQuery(CalendarPermission.class);
+ }
+
+ public void linkCalendar(long userId, long calendarId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.linkCalendar"))
+ .setLong("userId", userId)
+ .setLong("calendarId", calendarId)
+ .executeUpdate();
+ }
+
+ public void unlinkCalendar(long userId, long calendarId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery("database.unlinkCalendar"))
+ .setLong("userId", userId)
+ .setLong("calendarId", calendarId)
+ .executeUpdate();
+ }
}
diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java
index 078a5f935..6c0610655 100644
--- a/src/org/traccar/database/PermissionsManager.java
+++ b/src/org/traccar/database/PermissionsManager.java
@@ -207,6 +207,12 @@ public class PermissionsManager {
}
}
+ public void checkCalendar(long userId, long calendarId) throws SecurityException {
+ if (!Context.getCalendarManager().checkCalendar(userId, calendarId) && !isAdmin(userId)) {
+ throw new SecurityException("Calendar access denied");
+ }
+ }
+
public Server getServer() {
return server;
}
diff --git a/src/org/traccar/database/QueryBuilder.java b/src/org/traccar/database/QueryBuilder.java
index 50d689a2a..201240f2f 100644
--- a/src/org/traccar/database/QueryBuilder.java
+++ b/src/org/traccar/database/QueryBuilder.java
@@ -240,6 +240,23 @@ public final class QueryBuilder {
return this;
}
+ public QueryBuilder setBlob(String name, byte[] value) throws SQLException {
+ for (int i : indexes(name)) {
+ try {
+ if (value == null) {
+ statement.setNull(i, Types.BLOB);
+ } else {
+ statement.setBytes(i, value);
+ }
+ } catch (SQLException error) {
+ statement.close();
+ connection.close();
+ throw error;
+ }
+ }
+ return this;
+ }
+
public QueryBuilder setObject(Object object) throws SQLException {
Method[] methods = object.getClass().getMethods();
@@ -260,6 +277,8 @@ public final class QueryBuilder {
setString(name, (String) method.invoke(object));
} else if (method.getReturnType().equals(Date.class)) {
setDate(name, (Date) method.invoke(object));
+ } else if (method.getReturnType().equals(byte[].class)) {
+ setBlob(name, (byte[]) method.invoke(object));
} else if (method.getReturnType().equals(Map.class)) {
if (Context.getConfig().getBoolean("database.xml")) {
setString(name, MiscFormatter.toXmlString((Map) method.invoke(object)));
@@ -375,6 +394,17 @@ public final class QueryBuilder {
}
}
});
+ } else if (parameterType.equals(byte[].class)) {
+ processors.add(new ResultSetProcessor<T>() {
+ @Override
+ public void process(T object, ResultSet resultSet) throws SQLException {
+ try {
+ method.invoke(object, resultSet.getBytes(name));
+ } catch (IllegalAccessException | InvocationTargetException error) {
+ Log.warning(error);
+ }
+ }
+ });
}
}
diff --git a/src/org/traccar/events/GeofenceEventHandler.java b/src/org/traccar/events/GeofenceEventHandler.java
index d31e516ef..fbec932b1 100644
--- a/src/org/traccar/events/GeofenceEventHandler.java
+++ b/src/org/traccar/events/GeofenceEventHandler.java
@@ -57,14 +57,22 @@ public class GeofenceEventHandler extends BaseEventHandler {
Collection<Event> events = new ArrayList<>();
for (long geofenceId : newGeofences) {
- Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position.getDeviceId(), position.getId());
- event.setGeofenceId(geofenceId);
- events.add(event);
+ long calendarId = geofenceManager.getGeofence(geofenceId).getCalendarId();
+ if (calendarId == 0 || Context.getCalendarManager().getCalendar(calendarId) == null
+ || Context.getCalendarManager().getCalendar(calendarId).checkMoment(position.getFixTime())) {
+ Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position.getDeviceId(), position.getId());
+ event.setGeofenceId(geofenceId);
+ events.add(event);
+ }
}
for (long geofenceId : oldGeofences) {
- Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position.getDeviceId(), position.getId());
- event.setGeofenceId(geofenceId);
- events.add(event);
+ long calendarId = geofenceManager.getGeofence(geofenceId).getCalendarId();
+ if (calendarId == 0 || Context.getCalendarManager().getCalendar(calendarId) == null
+ || Context.getCalendarManager().getCalendar(calendarId).checkMoment(position.getFixTime())) {
+ Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position.getDeviceId(), position.getId());
+ event.setGeofenceId(geofenceId);
+ events.add(event);
+ }
}
return events;
}
diff --git a/src/org/traccar/model/Calendar.java b/src/org/traccar/model/Calendar.java
new file mode 100644
index 000000000..e1ae5a93d
--- /dev/null
+++ b/src/org/traccar/model/Calendar.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@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.model;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Date;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import net.fortuna.ical4j.data.CalendarBuilder;
+import net.fortuna.ical4j.data.ParserException;
+import net.fortuna.ical4j.filter.Filter;
+import net.fortuna.ical4j.filter.PeriodRule;
+import net.fortuna.ical4j.filter.Rule;
+import net.fortuna.ical4j.model.Component;
+import net.fortuna.ical4j.model.DateTime;
+import net.fortuna.ical4j.model.Dur;
+import net.fortuna.ical4j.model.Period;
+import net.fortuna.ical4j.model.component.CalendarComponent;
+import net.fortuna.ical4j.validate.ValidationException;
+
+public class Calendar extends Extensible {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ private byte[] calendarData;
+
+ public byte[] getCalendarData() throws ValidationException, IOException {
+ return calendarData.clone();
+ }
+
+ public void setCalendarData(byte[] calendarData) throws IOException, ParserException, SQLException {
+ setCalendar(calendarData);
+ }
+
+ private net.fortuna.ical4j.model.Calendar calendar;
+
+ @JsonIgnore
+ public net.fortuna.ical4j.model.Calendar getCalendar() {
+ return calendar;
+ }
+
+ public void setCalendar(byte[] calendarData) throws IOException, ParserException, SQLException {
+ CalendarBuilder builder = new CalendarBuilder();
+ calendar = builder.build(new ByteArrayInputStream(calendarData));
+ this.calendarData = calendarData.clone();
+ }
+
+ public boolean checkMoment(Date date) {
+ if (calendar != null) {
+ Period period = new Period(new DateTime(date), new Dur(0, 0, 0, 1));
+ Rule<Component> periodRule = new PeriodRule<Component>(period);
+ Filter<CalendarComponent> filter = new Filter<CalendarComponent>(new Rule[] {periodRule}, Filter.MATCH_ANY);
+ Collection<CalendarComponent> events = filter.filter(calendar.getComponents(Component.VEVENT));
+ if (events != null && !events.isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/org/traccar/model/CalendarPermission.java b/src/org/traccar/model/CalendarPermission.java
new file mode 100644
index 000000000..59f54e07b
--- /dev/null
+++ b/src/org/traccar/model/CalendarPermission.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@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.model;
+
+public class CalendarPermission {
+
+ private long userId;
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public void setUserId(long userId) {
+ this.userId = userId;
+ }
+
+ private long calendarId;
+
+ public long getCalendarId() {
+ return calendarId;
+ }
+
+ public void setCalendarId(long calendarId) {
+ this.calendarId = calendarId;
+ }
+}
diff --git a/src/org/traccar/model/Geofence.java b/src/org/traccar/model/Geofence.java
index 326c45b5f..f10ce6862 100644
--- a/src/org/traccar/model/Geofence.java
+++ b/src/org/traccar/model/Geofence.java
@@ -84,4 +84,13 @@ public class Geofence extends Extensible {
this.geometry = geometry;
}
+ private long calendarId;
+
+ public long getCalendarId() {
+ return calendarId;
+ }
+
+ public void setCalendarId(long calendarId) {
+ this.calendarId = calendarId;
+ }
}
diff --git a/test/org/traccar/calendar/CalendarTest.java b/test/org/traccar/calendar/CalendarTest.java
new file mode 100644
index 000000000..44fc9539b
--- /dev/null
+++ b/test/org/traccar/calendar/CalendarTest.java
@@ -0,0 +1,54 @@
+package org.traccar.calendar;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.traccar.model.Calendar;
+
+import net.fortuna.ical4j.data.ParserException;
+
+public class CalendarTest {
+
+ @Test
+ public void testCalendar() throws IOException, ParserException, ParseException, SQLException {
+ String calendarString = "BEGIN:VCALENDAR\n" +
+ "PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN\n" +
+ "VERSION:2.0\n" +
+ "BEGIN:VTIMEZONE\n" +
+ "TZID:Asia/Yekaterinburg\n" +
+ "BEGIN:STANDARD\n" +
+ "TZOFFSETFROM:+0500\n" +
+ "TZOFFSETTO:+0500\n" +
+ "TZNAME:YEKT\n" +
+ "DTSTART:19700101T000000\n" +
+ "END:STANDARD\n" +
+ "END:VTIMEZONE\n" +
+ "BEGIN:VEVENT\n" +
+ "CREATED:20161213T045151Z\n" +
+ "LAST-MODIFIED:20161213T045242Z\n" +
+ "DTSTAMP:20161213T045242Z\n" +
+ "UID:9d000df0-6354-479d-a407-218dac62c7c9\n" +
+ "SUMMARY:Every night\n" +
+ "RRULE:FREQ=DAILY\n" +
+ "DTSTART;TZID=Asia/Yekaterinburg:20161130T230000\n" +
+ "DTEND;TZID=Asia/Yekaterinburg:20161201T070000\n" +
+ "TRANSP:OPAQUE\n" +
+ "END:VEVENT\n" +
+ "END:VCALENDAR";
+ Calendar calendar = new Calendar();
+ calendar.setCalendar(calendarString.getBytes());
+ DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ssX");
+
+ Date date = format.parse("2016-12-13 23:01:00+05");
+ Assert.assertTrue(calendar.checkMoment(date));
+
+ date = format.parse("2016-12-13 07:01:00+05");
+ Assert.assertTrue(!calendar.checkMoment(date));
+ }
+}