From 63c88f49bb8d29440a61b26ab0e9ff08f4e71180 Mon Sep 17 00:00:00 2001
From: Daniel Gerhardt <code@dgerhardt.net>
Date: Mon, 19 Nov 2018 15:50:44 +0100
Subject: [PATCH] Replace legacy events triggered by entity state changes

Replaced the following events with StateChangeEvent:
* FeatureChangeEvent
* LockFeedbackEvent
* LockQuestionEvent
* LockVoteEvent
* LockVotesEvent
* PiRoundCancelEvent
* PiRoundDelayedStartEvent
* PiRoundEndEvent
* PiRoundResetEvent
* StatusRoomEvent
* UnlockQuestionEvent
* UnlockVoteEvent

Removed the following redundant events:
* DeleteAllLectureAnswersEvent
* DeleteAllPrepareationAnswersEvent
* DeleteAllQuestionsAnswersEvent
* DeleteAllQuestionsEvent
* LockQuestionsEvent
* UnlockQuestionsEvent
* UnlockVotesEvent

To trigger events, some code was migrated to use EntityServices instead
of directly accessing Repositories.
---
 .../controller/v2/ContentController.java      |   3 +-
 .../arsnova/controller/v2/RoomController.java |   9 +-
 .../event/DeleteAllLectureAnswersEvent.java   |  31 ----
 .../DeleteAllPreparationAnswersEvent.java     |  30 ----
 .../event/DeleteAllQuestionsAnswersEvent.java |  30 ----
 .../event/DeleteAllQuestionsEvent.java        |  32 ----
 .../thm/arsnova/event/FeatureChangeEvent.java |  31 ----
 .../thm/arsnova/event/LockFeedbackEvent.java  |  30 ----
 .../thm/arsnova/event/LockQuestionEvent.java  |  40 -----
 .../thm/arsnova/event/LockQuestionsEvent.java |  42 -----
 .../de/thm/arsnova/event/LockVoteEvent.java   |  61 -------
 .../de/thm/arsnova/event/LockVotesEvent.java  |  42 -----
 .../thm/arsnova/event/PiRoundCancelEvent.java |  33 ----
 .../event/PiRoundDelayedStartEvent.java       |  80 ----------
 .../de/thm/arsnova/event/PiRoundEndEvent.java |  59 -------
 .../thm/arsnova/event/PiRoundResetEvent.java  |  59 -------
 .../de/thm/arsnova/event/StatusRoomEvent.java |  31 ----
 .../arsnova/event/UnlockQuestionEvent.java    |  40 -----
 .../arsnova/event/UnlockQuestionsEvent.java   |  42 -----
 .../de/thm/arsnova/event/UnlockVoteEvent.java |  59 -------
 .../thm/arsnova/event/UnlockVotesEvent.java   |  42 -----
 .../thm/arsnova/service/ContentService.java   |   5 +-
 .../arsnova/service/ContentServiceImpl.java   |  55 ++-----
 .../de/thm/arsnova/service/RoomService.java   |   5 +-
 .../thm/arsnova/service/RoomServiceImpl.java  |  21 +--
 .../thm/arsnova/service/TimerServiceImpl.java |  42 ++---
 .../score/ScoreCalculatorFactoryImpl.java     |  61 +------
 .../websocket/ArsnovaSocketioServerImpl.java  | 151 +++++++++---------
 28 files changed, 127 insertions(+), 1039 deletions(-)
 delete mode 100644 src/main/java/de/thm/arsnova/event/DeleteAllLectureAnswersEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/DeleteAllPreparationAnswersEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/DeleteAllQuestionsAnswersEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/DeleteAllQuestionsEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/FeatureChangeEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/LockFeedbackEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/LockQuestionEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/LockQuestionsEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/LockVoteEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/LockVotesEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/PiRoundCancelEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/PiRoundDelayedStartEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/PiRoundEndEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/PiRoundResetEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/StatusRoomEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/UnlockQuestionEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/UnlockQuestionsEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/UnlockVoteEvent.java
 delete mode 100644 src/main/java/de/thm/arsnova/event/UnlockVotesEvent.java

diff --git a/src/main/java/de/thm/arsnova/controller/v2/ContentController.java b/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
index e097c27a0..b9239788f 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/ContentController.java
@@ -53,6 +53,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.naming.OperationNotSupportedException;
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -250,7 +251,7 @@ public class ContentController extends PaginationController {
 			@RequestParam(required = false) final Boolean publish,
 			@RequestParam(value = "lecturequestionsonly", defaultValue = "false", required = false) boolean lectureContentsOnly,
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) boolean preparationContentsOnly
-			) {
+			) throws IOException {
 		String roomId = roomService.getIdByShortId(roomShortId);
 		boolean p = publish == null || publish;
 		Iterable<de.thm.arsnova.model.Content> contents;
diff --git a/src/main/java/de/thm/arsnova/controller/v2/RoomController.java b/src/main/java/de/thm/arsnova/controller/v2/RoomController.java
index baef51c6f..981d13b73 100644
--- a/src/main/java/de/thm/arsnova/controller/v2/RoomController.java
+++ b/src/main/java/de/thm/arsnova/controller/v2/RoomController.java
@@ -51,6 +51,7 @@ import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -307,7 +308,7 @@ public class RoomController extends PaginationController {
 			@ApiParam(value = "wether statistics shall be exported", required = true) @RequestParam(value = "withAnswerStatistics", defaultValue = "false") final Boolean withAnswerStatistics,
 			@ApiParam(value = "wether comments shall be exported", required = true) @RequestParam(value = "withFeedbackQuestions", defaultValue = "false") final Boolean withFeedbackQuestions,
 			final HttpServletResponse response
-		) {
+		) throws IOException {
 		List<ImportExportContainer> rooms = new ArrayList<>();
 		ImportExportContainer temp;
 		for (String shortId : shortIds) {
@@ -327,7 +328,7 @@ public class RoomController extends PaginationController {
 	public Room copyToPublicPool(
 			@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
 			@ApiParam(value = "public pool attributes for Room", required = true) @RequestBody final ImportExportContainer.PublicPool publicPool
-			) {
+			) throws IOException {
 		String id = roomService.getIdByShortId(shortId);
 		roomService.setActive(id, false);
 		de.thm.arsnova.model.Room roomInfo = roomService.copyRoomToPublicPool(shortId, publicPool);
@@ -347,7 +348,7 @@ public class RoomController extends PaginationController {
 			@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
 			@ApiParam(value = "lock", required = true) @RequestParam(required = false) final Boolean lock,
 			final HttpServletResponse response
-			) {
+			) throws IOException {
 		if (lock != null) {
 			return toV2Migrator.migrate(roomService.setActive(roomService.getIdByShortId(shortId), lock));
 		}
@@ -412,7 +413,7 @@ public class RoomController extends PaginationController {
 			@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
 			@ApiParam(value = "lock", required = true) @RequestParam(required = true) final Boolean lock,
 			final HttpServletResponse response
-			) {
+			) throws IOException {
 		return String.valueOf(roomService.lockFeedbackInput(roomService.getIdByShortId(shortId), lock));
 	}
 
diff --git a/src/main/java/de/thm/arsnova/event/DeleteAllLectureAnswersEvent.java b/src/main/java/de/thm/arsnova/event/DeleteAllLectureAnswersEvent.java
deleted file mode 100644
index 0ad169a83..000000000
--- a/src/main/java/de/thm/arsnova/event/DeleteAllLectureAnswersEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-/**
- * Fires whenever all answers of all lecture questions of a session are deleted.
- */
-public class DeleteAllLectureAnswersEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public DeleteAllLectureAnswersEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/DeleteAllPreparationAnswersEvent.java b/src/main/java/de/thm/arsnova/event/DeleteAllPreparationAnswersEvent.java
deleted file mode 100644
index 49a8d9342..000000000
--- a/src/main/java/de/thm/arsnova/event/DeleteAllPreparationAnswersEvent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-/**
- * Fires whenever all answers of all preparation questions of a session are deleted.
- */
-public class DeleteAllPreparationAnswersEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public DeleteAllPreparationAnswersEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsAnswersEvent.java b/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsAnswersEvent.java
deleted file mode 100644
index aa671783f..000000000
--- a/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsAnswersEvent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-/**
- * Fires whenever all answers of all questions of a session are deleted.
- */
-public class DeleteAllQuestionsAnswersEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public DeleteAllQuestionsAnswersEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsEvent.java b/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsEvent.java
deleted file mode 100644
index 2bf5bac22..000000000
--- a/src/main/java/de/thm/arsnova/event/DeleteAllQuestionsEvent.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-/**
- * Fires whenever all questions of a session are deleted. Note that this implies that all answers are deleted as well,
- * even though the specific answer events are not fired.
- */
-public class DeleteAllQuestionsEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public DeleteAllQuestionsEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/FeatureChangeEvent.java b/src/main/java/de/thm/arsnova/event/FeatureChangeEvent.java
deleted file mode 100644
index 790eaea27..000000000
--- a/src/main/java/de/thm/arsnova/event/FeatureChangeEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-/**
- * Fires whenever a new session is created.
- */
-public class FeatureChangeEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public FeatureChangeEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockFeedbackEvent.java b/src/main/java/de/thm/arsnova/event/LockFeedbackEvent.java
deleted file mode 100644
index 6c50fc4b8..000000000
--- a/src/main/java/de/thm/arsnova/event/LockFeedbackEvent.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-/**
- * Fires whenever voting on a question is disabled.
- */
-public class LockFeedbackEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public LockFeedbackEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockQuestionEvent.java b/src/main/java/de/thm/arsnova/event/LockQuestionEvent.java
deleted file mode 100644
index 58d5b6fbf..000000000
--- a/src/main/java/de/thm/arsnova/event/LockQuestionEvent.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-/**
- * Fires whenever a content is disabled, i.e., it is hidden from students.
- */
-public class LockQuestionEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final Content content;
-
-	public LockQuestionEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.content = content;
-	}
-
-	public Content getQuestion() {
-		return this.content;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockQuestionsEvent.java b/src/main/java/de/thm/arsnova/event/LockQuestionsEvent.java
deleted file mode 100644
index 869d0c1a0..000000000
--- a/src/main/java/de/thm/arsnova/event/LockQuestionsEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.List;
-
-/**
- * Fires whenever a set of contents are disabled, i.e., they are hidden from students.
- */
-public class LockQuestionsEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private List<Content> contents;
-
-	public LockQuestionsEvent(Object source, String roomId, List<Content> contents) {
-		super(source, roomId);
-		this.contents = contents;
-	}
-
-	public List<Content> getQuestions() {
-		return this.contents;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockVoteEvent.java b/src/main/java/de/thm/arsnova/event/LockVoteEvent.java
deleted file mode 100644
index bf3ba2973..000000000
--- a/src/main/java/de/thm/arsnova/event/LockVoteEvent.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever voting on a content is disabled.
- */
-public class LockVoteEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final Content content;
-
-	public LockVoteEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.content = content;
-	}
-
-	public String getQuestionId() {
-		return this.content.getId();
-	}
-
-	public String getGroup() {
-		/* FIXME: Event does not support content with multiple groups */
-		return content.getGroups().toArray(new String[1])[0];
-	}
-
-	public Boolean getVotingDisabled() {
-		/* FIXME: migrate */
-		return false;
-		//return this.content.isVotingDisabled();
-	}
-
-	public Map<String, Object> getVotingAdmission() {
-		Map<String, Object> map = new HashMap<>();
-
-		map.put("_id", getQuestionId());
-		map.put("variant", getGroup());
-		return map;
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/LockVotesEvent.java b/src/main/java/de/thm/arsnova/event/LockVotesEvent.java
deleted file mode 100644
index 7595c19a2..000000000
--- a/src/main/java/de/thm/arsnova/event/LockVotesEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.List;
-
-/**
- * Fires whenever voting of multiple contents is disabled.
- */
-public class LockVotesEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private List<Content> contents;
-
-	public LockVotesEvent(Object source, String roomId, List<Content> contents) {
-		super(source, roomId);
-		this.contents = contents;
-	}
-
-	public List<Content> getQuestions() {
-		return this.contents;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/PiRoundCancelEvent.java b/src/main/java/de/thm/arsnova/event/PiRoundCancelEvent.java
deleted file mode 100644
index 73fadca6d..000000000
--- a/src/main/java/de/thm/arsnova/event/PiRoundCancelEvent.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-/**
- * Fires whenever a peer instruction round is canceled.
- */
-public class PiRoundCancelEvent extends PiRoundEndEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public PiRoundCancelEvent(Object source, String roomId, Content content) {
-		super(source, roomId, content);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/PiRoundDelayedStartEvent.java b/src/main/java/de/thm/arsnova/event/PiRoundDelayedStartEvent.java
deleted file mode 100644
index c12cc8782..000000000
--- a/src/main/java/de/thm/arsnova/event/PiRoundDelayedStartEvent.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever a delayed peer instruction round is initiated. The delayed part denotes that this round might not
- * have been started yet.
- */
-public class PiRoundDelayedStartEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-	private final String questionId;
-	private final Date startTime;
-	private final Date endTime;
-	private final String group;
-	private int piRound;
-
-	public PiRoundDelayedStartEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.questionId = content.getId();
-		/* FIXME: Event does not support content with multiple groups */
-		this.group = content.getGroups().toArray(new String[1])[0];
-		this.piRound = content.getState().getRound();
-		this.endTime = content.getState().getRoundEndTimestamp();
-		this.startTime = new Date();
-	}
-
-	public String getQuestionId() {
-		return questionId;
-	}
-
-	public Date getStartTime() {
-		return startTime;
-	}
-
-	public Date getEndTime() {
-		return endTime;
-	}
-
-	public String getGroup() {
-		return group;
-	}
-
-	public Integer getPiRound() {
-		return piRound;
-	}
-
-	public Map<String, Object> getPiRoundInformations() {
-		Map<String, Object> map = new HashMap<>();
-
-		map.put("_id", getQuestionId());
-		map.put("endTime", getEndTime().getTime());
-		map.put("startTime", getStartTime().getTime());
-		map.put("variant", getGroup());
-		map.put("round", getPiRound());
-
-		return map;
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/PiRoundEndEvent.java b/src/main/java/de/thm/arsnova/event/PiRoundEndEvent.java
deleted file mode 100644
index 8d56a8139..000000000
--- a/src/main/java/de/thm/arsnova/event/PiRoundEndEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever a peer instruction round has ended.
- */
-public class PiRoundEndEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final String contentId;
-	private final String group;
-
-	public PiRoundEndEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		contentId = content.getId();
-		/* FIXME: Event does not support content with multiple groups */
-		this.group = content.getGroups().toArray(new String[1])[0];
-	}
-
-	public String getContentId() {
-		return contentId;
-	}
-
-	public String getGroup() {
-		return group;
-	}
-
-	public Map<String, String> getPiRoundEndInformations() {
-		Map<String, String> map = new HashMap<>();
-
-		map.put("_id", getContentId());
-		map.put("variant", getGroup());
-
-		return map;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/PiRoundResetEvent.java b/src/main/java/de/thm/arsnova/event/PiRoundResetEvent.java
deleted file mode 100644
index 2b4cd5325..000000000
--- a/src/main/java/de/thm/arsnova/event/PiRoundResetEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever a peer instruction round is reset.
- */
-public class PiRoundResetEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final String contentId;
-	private final String group;
-
-	public PiRoundResetEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		contentId = content.getId();
-		/* FIXME: Event does not support content with multiple groups */
-		this.group = content.getGroups().toArray(new String[1])[0];
-	}
-
-	public String getContentId() {
-		return contentId;
-	}
-
-	public String getGroup() {
-		return group;
-	}
-
-	public Map<String, String> getPiRoundResetInformations() {
-		Map<String, String> map = new HashMap<>();
-
-		map.put("_id", getContentId());
-		map.put("variant", getGroup());
-
-		return map;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/StatusRoomEvent.java b/src/main/java/de/thm/arsnova/event/StatusRoomEvent.java
deleted file mode 100644
index d7307cd79..000000000
--- a/src/main/java/de/thm/arsnova/event/StatusRoomEvent.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-/**
- * Fires whenever the status of a session changes, i.e., it is enabled or disabled.
- */
-public class StatusRoomEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	public StatusRoomEvent(Object source, String roomId) {
-		super(source, roomId);
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/UnlockQuestionEvent.java b/src/main/java/de/thm/arsnova/event/UnlockQuestionEvent.java
deleted file mode 100644
index deddf45f1..000000000
--- a/src/main/java/de/thm/arsnova/event/UnlockQuestionEvent.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-/**
- * Fires whenever a content is enabled, i.e., it becomes visible to students.
- */
-public class UnlockQuestionEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final Content content;
-
-	public UnlockQuestionEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.content = content;
-	}
-
-	public Content getQuestion() {
-		return this.content;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/UnlockQuestionsEvent.java b/src/main/java/de/thm/arsnova/event/UnlockQuestionsEvent.java
deleted file mode 100644
index 81f38cc60..000000000
--- a/src/main/java/de/thm/arsnova/event/UnlockQuestionsEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.List;
-
-/**
- * Fires whenever a set of contents are enabled, i.e., they become visible to students.
- */
-public class UnlockQuestionsEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private List<Content> contents;
-
-	public UnlockQuestionsEvent(Object source, String roomId, List<Content> contents) {
-		super(source, roomId);
-		this.contents = contents;
-	}
-
-	public List<Content> getQuestions() {
-		return this.contents;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/event/UnlockVoteEvent.java b/src/main/java/de/thm/arsnova/event/UnlockVoteEvent.java
deleted file mode 100644
index 3dd6996e3..000000000
--- a/src/main/java/de/thm/arsnova/event/UnlockVoteEvent.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Fires whenever voting on a content is enabled.
- */
-public class UnlockVoteEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private final Content content;
-
-	public UnlockVoteEvent(Object source, String roomId, Content content) {
-		super(source, roomId);
-		this.content = content;
-	}
-
-	public String getQuestionId() {
-		return this.content.getId();
-	}
-
-	public String getGroup() {
-		/* FIXME: Event does not support content with multiple groups */
-		return content.getGroups().toArray(new String[1])[0];
-	}
-
-	public Boolean getVotingDisabled() {
-		return !this.content.getState().isResponsesEnabled();
-	}
-
-	public Map<String, Object> getVotingAdmission() {
-		Map<String, Object> map = new HashMap<>();
-
-		map.put("_id", getQuestionId());
-		map.put("variant", getGroup());
-		return map;
-	}
-}
diff --git a/src/main/java/de/thm/arsnova/event/UnlockVotesEvent.java b/src/main/java/de/thm/arsnova/event/UnlockVotesEvent.java
deleted file mode 100644
index 85cf1d06f..000000000
--- a/src/main/java/de/thm/arsnova/event/UnlockVotesEvent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * This file is part of ARSnova Backend.
- * Copyright (C) 2012-2018 The ARSnova Team and Contributors
- *
- * ARSnova Backend is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * ARSnova Backend is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-package de.thm.arsnova.event;
-
-import de.thm.arsnova.model.Content;
-
-import java.util.List;
-
-/**
- * Fires whenever voting of multiple contents is enabled.
- */
-public class UnlockVotesEvent extends RoomEvent {
-
-	private static final long serialVersionUID = 1L;
-
-	private List<Content> contents;
-
-	public UnlockVotesEvent(Object source, String roomId, List<Content> contents) {
-		super(source, roomId);
-		this.contents = contents;
-	}
-
-	public List<Content> getQuestions() {
-		return this.contents;
-	}
-
-}
diff --git a/src/main/java/de/thm/arsnova/service/ContentService.java b/src/main/java/de/thm/arsnova/service/ContentService.java
index dbb3d8b5a..355377a44 100644
--- a/src/main/java/de/thm/arsnova/service/ContentService.java
+++ b/src/main/java/de/thm/arsnova/service/ContentService.java
@@ -36,6 +36,7 @@ package de.thm.arsnova.service;
 
 import de.thm.arsnova.model.Content;
 
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -74,9 +75,9 @@ public interface ContentService extends EntityService<Content> {
 
 	List<String> getUnAnsweredPreparationContentIds(String roomId, String userId);
 
-	void publishAll(String roomId, boolean publish);
+	void publishAll(String roomId, boolean publish) throws IOException;
 
-	void publishContents(String roomId, boolean publish, Iterable<Content> contents);
+	void publishContents(String roomId, boolean publish, Iterable<Content> contents) throws IOException;
 
 	void deleteAllContentsAnswers(String roomId);
 
diff --git a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
index eec4adbd4..265382e26 100644
--- a/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/ContentServiceImpl.java
@@ -17,7 +17,10 @@
  */
 package de.thm.arsnova.service;
 
-import de.thm.arsnova.event.*;
+import de.thm.arsnova.event.AfterCreationEvent;
+import de.thm.arsnova.event.AfterDeletionEvent;
+import de.thm.arsnova.event.BeforeCreationEvent;
+import de.thm.arsnova.event.BeforeDeletionEvent;
 import de.thm.arsnova.model.Content;
 import de.thm.arsnova.model.Room;
 import de.thm.arsnova.persistence.AnswerRepository;
@@ -39,7 +42,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
 
 import java.io.IOException;
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -317,13 +320,11 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			contentIds = contentRepository.findIdsByRoomIdAndVariant(room.getId(), variant);
 		}
 
+		/* TODO: use EntityService! */
 		final int answerCount = answerRepository.deleteByContentIds(contentIds);
 		final int contentCount = contentRepository.deleteByRoomId(room.getId());
 		dbLogger.log("delete", "type", "question", "questionCount", contentCount);
 		dbLogger.log("delete", "type", "answer", "answerCount", answerCount);
-
-		final DeleteAllQuestionsEvent event = new DeleteAllQuestionsEvent(this, room.getId());
-		this.eventPublisher.publishEvent(event);
 	}
 
 	@Override
@@ -358,7 +359,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	public void setVotingAdmission(final String contentId, final boolean disableVoting) {
 		final Content content = contentRepository.findOne(contentId);
-		final Room room = roomRepository.findOne(content.getRoomId());
 		content.getState().setResponsesEnabled(!disableVoting);
 
 		if (!disableVoting && !content.getState().isVisible()) {
@@ -367,13 +367,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		} else {
 			update(content);
 		}
-		ArsnovaEvent event;
-		if (disableVoting) {
-			event = new LockVoteEvent(this, room.getId(), content);
-		} else {
-			event = new UnlockVoteEvent(this, room.getId(), content);
-		}
-		this.eventPublisher.publishEvent(event);
 	}
 
 	@Override
@@ -395,15 +388,6 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		patches.put("responsesEnabled", !disableVoting);
 		try {
 			patch(contents, patches, Content::getState);
-			ArsnovaEvent event;
-			List<Content> list = new ArrayList<>();
-			contents.forEach(list::add);
-			if (disableVoting) {
-				event = new LockVotesEvent(this, room.getId(), list);
-			} else {
-				event = new UnlockVotesEvent(this, room.getId(), list);
-			}
-			this.eventPublisher.publishEvent(event);
 		} catch (IOException e) {
 			logger.error("Patching of contents failed", e);
 		}
@@ -468,7 +452,7 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public void publishAll(final String roomId, final boolean publish) {
+	public void publishAll(final String roomId, final boolean publish) throws IOException {
 		/* TODO: resolve redundancies */
 		final User user = getCurrentUser();
 		final Room room = roomRepository.findOne(roomId);
@@ -486,25 +470,13 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 			@CacheEvict(value = "lecturecontentlists", key = "#roomId"),
 			@CacheEvict(value = "preparationcontentlists", key = "#roomId"),
 			@CacheEvict(value = "flashcardcontentlists", key = "#roomId") })
-	public void publishContents(final String roomId, final boolean publish, Iterable<Content> contents) {
+	public void publishContents(final String roomId, final boolean publish, Iterable<Content> contents) throws IOException {
 		final User user = getCurrentUser();
 		final Room room = roomRepository.findOne(roomId);
 		if (!room.getOwnerId().equals(user.getId())) {
 			throw new UnauthorizedException();
 		}
-		for (final Content content : contents) {
-			content.getState().setVisible(publish);
-		}
-		contentRepository.saveAll(contents);
-		ArsnovaEvent event;
-		List<Content> list = new ArrayList<>();
-		contents.forEach(list::add);
-		if (publish) {
-			event = new UnlockQuestionsEvent(this, room.getId(), list);
-		} else {
-			event = new LockQuestionsEvent(this, room.getId(), list);
-		}
-		this.eventPublisher.publishEvent(event);
+		patch(contents, Collections.singletonMap("visible", publish), Content::getState);
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -521,9 +493,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId());
 		resetContentsRoundState(room.getId(), contents);
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
+		/* TODO: use EntityService! */
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
-
-		this.eventPublisher.publishEvent(new DeleteAllQuestionsAnswersEvent(this, room.getId()));
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -537,9 +508,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId(), "preparation");
 		resetContentsRoundState(room.getId(), contents);
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
+		/* TODO: use EntityService! */
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
-
-		this.eventPublisher.publishEvent(new DeleteAllPreparationAnswersEvent(this, room.getId()));
 	}
 
 	/* TODO: Split and move answer part to AnswerService */
@@ -553,9 +523,8 @@ public class ContentServiceImpl extends DefaultEntityServiceImpl<Content> implem
 		final List<Content> contents = contentRepository.findByRoomIdAndVariantAndActive(room.getId(), "lecture");
 		resetContentsRoundState(room.getId(), contents);
 		final List<String> contentIds = contents.stream().map(Content::getId).collect(Collectors.toList());
+		/* TODO: use EntityService! */
 		answerRepository.deleteAllAnswersForQuestions(contentIds);
-
-		this.eventPublisher.publishEvent(new DeleteAllLectureAnswersEvent(this, room.getId()));
 	}
 
 	@Caching(evict = {
diff --git a/src/main/java/de/thm/arsnova/service/RoomService.java b/src/main/java/de/thm/arsnova/service/RoomService.java
index 2c30896d2..036e097ad 100644
--- a/src/main/java/de/thm/arsnova/service/RoomService.java
+++ b/src/main/java/de/thm/arsnova/service/RoomService.java
@@ -23,6 +23,7 @@ import de.thm.arsnova.model.migration.v2.ClientAuthentication;
 import de.thm.arsnova.model.transport.ImportExportContainer;
 import de.thm.arsnova.model.transport.ScoreStatistics;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.UUID;
 
@@ -56,7 +57,7 @@ public interface RoomService extends EntityService<Room> {
 
 	int activeUsers(String id);
 
-	Room setActive(String id, Boolean lock);
+	Room setActive(String id, Boolean lock) throws IOException;
 
 	Room join(String id, UUID socketId);
 
@@ -88,7 +89,7 @@ public interface RoomService extends EntityService<Room> {
 
 	Room.Settings updateFeatures(String id, Room.Settings settings);
 
-	boolean lockFeedbackInput(String id, Boolean lock);
+	boolean lockFeedbackInput(String id, Boolean lock) throws IOException;
 
 	boolean flipFlashcards(String id, Boolean flip);
 
diff --git a/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java b/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
index 83b06681b..ec193d3c0 100644
--- a/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/RoomServiceImpl.java
@@ -21,10 +21,7 @@ import de.thm.arsnova.connector.client.ConnectorClient;
 import de.thm.arsnova.connector.model.Course;
 import de.thm.arsnova.event.AfterDeletionEvent;
 import de.thm.arsnova.event.BeforeDeletionEvent;
-import de.thm.arsnova.event.FeatureChangeEvent;
 import de.thm.arsnova.event.FlipFlashcardsEvent;
-import de.thm.arsnova.event.LockFeedbackEvent;
-import de.thm.arsnova.event.StatusRoomEvent;
 import de.thm.arsnova.model.Room;
 import de.thm.arsnova.model.UserProfile;
 import de.thm.arsnova.model.migration.v2.ClientAuthentication;
@@ -53,8 +50,10 @@ import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
@@ -401,11 +400,9 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'owner')")
-	public Room setActive(final String id, final Boolean lock) {
+	public Room setActive(final String id, final Boolean lock) throws IOException {
 		final Room room = roomRepository.findOne(id);
-		room.setClosed(!lock);
-		this.eventPublisher.publishEvent(new StatusRoomEvent(this, room.getId()));
-		roomRepository.save(room);
+		patch(room, Collections.singletonMap("closed", lock));
 
 		return room;
 	}
@@ -519,7 +516,8 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 	public Room.Settings updateFeatures(String id, Room.Settings settings) {
 		final Room room = roomRepository.findOne(id);
 		room.setSettings(settings);
-		this.eventPublisher.publishEvent(new FeatureChangeEvent(this, room.getId()));
+
+
 		roomRepository.save(room);
 
 		return room.getSettings();
@@ -527,15 +525,12 @@ public class RoomServiceImpl extends DefaultEntityServiceImpl<Room> implements R
 
 	@Override
 	@PreAuthorize("hasPermission(#id, 'room', 'owner')")
-	public boolean lockFeedbackInput(String id, Boolean lock) {
+	public boolean lockFeedbackInput(String id, Boolean lock) throws IOException {
 		final Room room = roomRepository.findOne(id);
 		if (!lock) {
 			feedbackService.cleanFeedbackVotesByRoomId(id, 0);
 		}
-
-		room.getSettings().setFeedbackLocked(lock);
-		this.eventPublisher.publishEvent(new LockFeedbackEvent(this, room.getId()));
-		roomRepository.save(room);
+		patch(room, Collections.singletonMap("feedbackLocked", lock), Room::getSettings);
 
 		return room.getSettings().isFeedbackLocked();
 	}
diff --git a/src/main/java/de/thm/arsnova/service/TimerServiceImpl.java b/src/main/java/de/thm/arsnova/service/TimerServiceImpl.java
index 40b07f175..43bbea949 100644
--- a/src/main/java/de/thm/arsnova/service/TimerServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/service/TimerServiceImpl.java
@@ -1,18 +1,11 @@
 package de.thm.arsnova.service;
 
-import de.thm.arsnova.event.PiRoundCancelEvent;
-import de.thm.arsnova.event.PiRoundDelayedStartEvent;
-import de.thm.arsnova.event.PiRoundEndEvent;
-import de.thm.arsnova.event.PiRoundResetEvent;
 import de.thm.arsnova.model.Content;
 import de.thm.arsnova.model.Room;
 import de.thm.arsnova.persistence.AnswerRepository;
-import de.thm.arsnova.persistence.ContentRepository;
 import de.thm.arsnova.persistence.RoomRepository;
 import de.thm.arsnova.security.User;
 import org.springframework.cache.annotation.CacheEvict;
-import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
 
@@ -22,26 +15,25 @@ import java.util.Timer;
 import java.util.TimerTask;
 
 @Service
-public class TimerServiceImpl implements TimerService, ApplicationEventPublisherAware {
+public class TimerServiceImpl implements TimerService {
 	private HashMap<String, Timer> timerList = new HashMap<>();
 	private UserService userService;
 	private RoomRepository roomRepository;
-	private ContentRepository contentRepository;
+	private ContentService contentService;
 	private AnswerRepository answerRepository;
-	private ApplicationEventPublisher publisher;
 
 	public TimerServiceImpl(final UserService userService, final RoomRepository roomRepository,
-			final ContentRepository contentRepository, final AnswerRepository answerRepository) {
+			final ContentService contentService, final AnswerRepository answerRepository) {
 		this.userService = userService;
 		this.roomRepository = roomRepository;
-		this.contentRepository = contentRepository;
+		this.contentService = contentService;
 		this.answerRepository = answerRepository;
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated() and hasPermission(#contentId, 'content', 'owner')")
 	public void startNewRound(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
+		final Content content = contentService.get(contentId);
 		final Room room = roomRepository.findOne(content.getRoomId());
 
 		cancelDelayedRoundChange(contentId);
@@ -49,25 +41,22 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 		content.getState().setRoundEndTimestamp(null);
 		content.getState().setResponsesEnabled(false);
 		updateRoundManagementState(content);
-		contentRepository.save(content);
-
-		this.publisher.publishEvent(new PiRoundEndEvent(this, room.getId(), content));
+		contentService.update(content);
 	}
 
 	@Override
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	public void startNewRoundDelayed(final String contentId, final int time) {
 		final User user = userService.getCurrentUser();
-		final Content content = contentRepository.findOne(contentId);
+		final Content content = contentService.get(contentId);
 		final Room room = roomRepository.findOne(content.getRoomId());
 
 		final Date date = new Date();
 		final Timer timer = new Timer();
 		final Date endDate = new Date(date.getTime() + (time * 1000));
 		updateRoundStartVariables(content, date, endDate);
-		contentRepository.save(content);
+		contentService.update(content);
 
-		this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, room.getId(), content));
 		timerList.put(contentId, timer);
 
 		timer.schedule(new TimerTask() {
@@ -81,7 +70,7 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 	@Override
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	public void cancelRoundChange(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
+		final Content content = contentService.get(contentId);
 		final Room room = roomRepository.findOne(content.getRoomId());
 
 		cancelDelayedRoundChange(contentId);
@@ -92,8 +81,7 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 		}
 		content.getState().setRoundEndTimestamp(null);
 
-		contentRepository.save(content);
-		this.publisher.publishEvent(new PiRoundCancelEvent(this, room.getId(), content));
+		contentService.update(content);
 	}
 
 	@Override
@@ -111,7 +99,7 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 	@PreAuthorize("hasPermission(#contentId, 'content', 'owner')")
 	@CacheEvict("answerlists")
 	public void resetRoundState(final String contentId) {
-		final Content content = contentRepository.findOne(contentId);
+		final Content content = contentService.get(contentId);
 		final Room room = roomRepository.findOne(content.getRoomId());
 		cancelDelayedRoundChange(contentId);
 
@@ -123,8 +111,7 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 
 		resetRoundManagementState(content);
 		answerRepository.deleteByContentId(content.getId());
-		contentRepository.save(content);
-		this.publisher.publishEvent(new PiRoundResetEvent(this, room.getId(), content));
+		contentService.update(content);
 	}
 
 	private void updateRoundStartVariables(final Content content, final Date start, final Date end) {
@@ -157,9 +144,4 @@ public class TimerServiceImpl implements TimerService, ApplicationEventPublisher
 		content.getState().setRound(1);
 		content.getState().setRoundEndTimestamp(null);
 	}
-
-	@Override
-	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
-		this.publisher = applicationEventPublisher;
-	}
 }
diff --git a/src/main/java/de/thm/arsnova/service/score/ScoreCalculatorFactoryImpl.java b/src/main/java/de/thm/arsnova/service/score/ScoreCalculatorFactoryImpl.java
index 908c17bcc..2eea76075 100644
--- a/src/main/java/de/thm/arsnova/service/score/ScoreCalculatorFactoryImpl.java
+++ b/src/main/java/de/thm/arsnova/service/score/ScoreCalculatorFactoryImpl.java
@@ -17,7 +17,10 @@
  */
 package de.thm.arsnova.service.score;
 
-import de.thm.arsnova.event.*;
+import de.thm.arsnova.event.AfterCreationEvent;
+import de.thm.arsnova.event.AfterDeletionEvent;
+import de.thm.arsnova.event.ChangeScoreEvent;
+import de.thm.arsnova.event.StateChangeEvent;
 import de.thm.arsnova.model.Answer;
 import de.thm.arsnova.model.Content;
 import de.thm.arsnova.persistence.SessionStatisticsRepository;
@@ -59,28 +62,10 @@ public class ScoreCalculatorFactoryImpl implements ScoreCalculatorFactory, Appli
 		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getEntity().getRoomId()));
 	}
 
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleUnlockQuestion(UnlockQuestionEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleUnlockQuestions(UnlockQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleLockQuestion(LockQuestionEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleLockQuestions(LockQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
+	@CacheEvict(value = "score", key = "#event.entity.roomId")
+	@EventListener(condition = "#event.stateName == 'state'")
+	public void handleContentStateChange(StateChangeEvent<Content, Content.State> event) {
+		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getEntity().getRoomId()));
 	}
 
 	@CacheEvict(value = "score", key = "#event.entity.roomId")
@@ -101,36 +86,6 @@ public class ScoreCalculatorFactoryImpl implements ScoreCalculatorFactory, Appli
 		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getEntity().getRoomId()));
 	}
 
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleDeleteAllQuestions(DeleteAllQuestionsEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleDeleteAllQuestionsAnswers(DeleteAllQuestionsAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleDeleteAllPreparationAnswers(DeleteAllPreparationAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handleDeleteAllLectureAnswers(DeleteAllLectureAnswersEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
-	@CacheEvict(value = "score", key = "#event.roomId")
-	@EventListener
-	public void handlePiRoundReset(PiRoundResetEvent event) {
-		this.publisher.publishEvent(new ChangeScoreEvent(this, event.getRoomId()));
-	}
-
 	@Override
 	public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
 		this.publisher = publisher;
diff --git a/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
index 8e99eca9e..874ea935b 100644
--- a/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
+++ b/src/main/java/de/thm/arsnova/websocket/ArsnovaSocketioServerImpl.java
@@ -28,7 +28,13 @@ import com.corundumstudio.socketio.listener.DataListener;
 import com.corundumstudio.socketio.listener.DisconnectListener;
 import com.corundumstudio.socketio.protocol.Packet;
 import com.corundumstudio.socketio.protocol.PacketType;
-import de.thm.arsnova.event.*;
+import de.thm.arsnova.event.AfterCreationEvent;
+import de.thm.arsnova.event.AfterDeletionEvent;
+import de.thm.arsnova.event.ChangeScoreEvent;
+import de.thm.arsnova.event.DeleteFeedbackForRoomsEvent;
+import de.thm.arsnova.event.FlipFlashcardsEvent;
+import de.thm.arsnova.event.NewFeedbackEvent;
+import de.thm.arsnova.event.StateChangeEvent;
 import de.thm.arsnova.model.Answer;
 import de.thm.arsnova.model.Comment;
 import de.thm.arsnova.model.ScoreOptions;
@@ -60,7 +66,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
@@ -496,24 +504,13 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer {
 		this.reportContentAvailable(event.getEntity().getId(), Collections.singletonList(event.getEntity()));
 	}
 
-	@EventListener
-	public void handleUnlockQuestion(UnlockQuestionEvent event) {
-		this.reportContentAvailable(event.getRoomId(), Collections.singletonList(event.getQuestion()));
-	}
-
-	@EventListener
-	public void handleLockQuestion(LockQuestionEvent event) {
-		this.reportContentsLocked(event.getRoomId(), Collections.singletonList(event.getQuestion()));
-	}
-
-	@EventListener
-	public void handleUnlockQuestions(UnlockQuestionsEvent event) {
-		this.reportContentAvailable(event.getRoomId(), event.getQuestions());
-	}
-
-	@EventListener
-	public void handleLockQuestions(LockQuestionsEvent event) {
-		this.reportContentsLocked(event.getRoomId(), event.getQuestions());
+	@EventListener(condition = "#event.stateName == 'state'")
+	public void handleContentIsVisibleStateChange(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		if (event.getEntity().getState().isVisible()) {
+			this.reportContentAvailable(event.getEntity().getRoomId(), Collections.singletonList(event.getEntity()));
+		} else {
+			this.reportContentsLocked(event.getEntity().getRoomId(), Collections.singletonList(event.getEntity()));
+		}
 	}
 
 	@EventListener
@@ -555,80 +552,80 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer {
 	}
 
 	@Async
-	@EventListener
+	@EventListener(condition = "#event.stateName == 'state'")
 	@Timed
-	public void handlePiRoundDelayedStart(PiRoundDelayedStartEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "startDelayedPiRound", event.getPiRoundInformations());
+	public void handlePiRoundDelayedStart(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		broadcastInRoom(event.getEntity().getRoomId(), "startDelayedPiRound", generateRoundInfo(event.getEntity()));
 	}
 
 	@Async
-	@EventListener
+	@EventListener(condition = "#event.stateName == 'state'")
 	@Timed
-	public void handlePiRoundEnd(PiRoundEndEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "endPiRound", event.getPiRoundEndInformations());
+	public void handlePiRoundEnd(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		broadcastInRoom(event.getEntity().getRoomId(), "endPiRound", generateRoundInfo(event.getEntity()));
 	}
 
 	@Async
-	@EventListener
+	@EventListener(condition = "#event.stateName == 'state'")
 	@Timed
-	public void handlePiRoundCancel(PiRoundCancelEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "cancelPiRound", event.getContentId());
+	public void handlePiRoundCancel(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		broadcastInRoom(event.getEntity().getRoomId(), "cancelPiRound", event.getEntity().getId());
 	}
 
-	@EventListener
-	public void handlePiRoundReset(PiRoundResetEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "resetPiRound", event.getPiRoundResetInformations());
+	@EventListener(condition = "#event.stateName == 'state'")
+	public void handlePiRoundReset(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		broadcastInRoom(event.getEntity().getRoomId(), "resetPiRound", generateRoundInfo(event.getEntity()));
 	}
 
-	@EventListener
-	public void handleLockVote(LockVoteEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "lockVote", event.getVotingAdmission());
-	}
-
-	@EventListener
-	public void handleUnlockVote(UnlockVoteEvent event) {
-		final String roomId = event.getRoomId();
-		broadcastInRoom(roomId, "unlockVote", event.getVotingAdmission());
-	}
-
-	@EventListener
-	public void handleLockVotes(LockVotesEvent event) {
-		List<Content> contents = new ArrayList<>();
-		for (de.thm.arsnova.model.Content q : event.getQuestions()) {
-			contents.add(new Content(q));
+	private Map<String, Object> generateRoundInfo(de.thm.arsnova.model.Content content) {
+		Map<String, Object> map = new HashMap<>();
+		map.put("_id", content.getId());
+		if (content.getState().getRoundEndTimestamp() != null) {
+			map.put("endTime", content.getState().getRoundEndTimestamp().getTime());
 		}
-		broadcastInRoom(event.getRoomId(), "lockVotes", contents);
-	}
-
-	@EventListener
-	public void handleUnlockVotes(UnlockVotesEvent event) {
-		List<Content> contents = new ArrayList<>();
-		for (de.thm.arsnova.model.Content q : event.getQuestions()) {
-			contents.add(new Content(q));
+		/* FIXME: getRoundStartTimestamp is not implemented for Content.State. Is a delayed start still useful? */
+		/*
+		if (content.getState().getRoundStartTimestamp() != null) {
+			map.put("startTime", content.getState().getRoundStartTimestamp().getTime());
+		}
+		*/
+		map.put("variant", content.getGroups());
+		map.put("round", content.getState().getRound());
+
+		return map;
+	}
+
+	@EventListener(condition = "#event.stateName == 'state'")
+	public void handleContentResponsesEnabledStateChange(StateChangeEvent<de.thm.arsnova.model.Content, de.thm.arsnova.model.Content.State> event) {
+		/* Multiple groups for a single Content are not handled. */
+		final String groupName = event.getEntity().getGroups().iterator().hasNext() ?
+				event.getEntity().getGroups().iterator().next() : "";
+		Map<String, Object> map = new HashMap<>();
+		map.put("_id", event.getEntity().getId());
+		map.put("variant", groupName);
+		if (event.getEntity().getState().isResponsesEnabled()) {
+			this.reportContentAvailable(event.getEntity().getRoomId(), Collections.singletonList(event.getEntity()));
+			broadcastInRoom(event.getEntity().getRoomId(), "unlockVote", map);
+		} else {
+			broadcastInRoom(event.getEntity().getRoomId(), "lockVote", map);
 		}
-		broadcastInRoom(event.getRoomId(), "unlockVotes", contents);
 	}
 
-	@EventListener
-	public void handleFeatureChange(FeatureChangeEvent event) {
-//		final String roomId = event.getRoomId();
-//		final de.thm.arsnova.model.Room.Settings settings = event.getRoom().getSettings();
-//		broadcastInRoom(roomId, "featureChange", toV2Migrator.migrate(settings));
-//
-//		if (settings.isFlashcardsEnabled()) {
-//			broadcastInRoom(roomId, "countFlashcards", contentService.countFlashcardsForUserInternal(roomId));
-//			broadcastInRoom(roomId, "flipFlashcards", event.getRoom().getFlipFlashcards());
-//		}
+	@EventListener(condition = "#event.stateName == 'settings'")
+	public void handleFeatureChange(StateChangeEvent<de.thm.arsnova.model.Room, de.thm.arsnova.model.Room.Settings> event) {
+		final String roomId = event.getEntity().getId();
+		final de.thm.arsnova.model.Room.Settings settings = event.getEntity().getSettings();
+		broadcastInRoom(roomId, "featureChange", toV2Migrator.migrate(settings));
+
+		if (settings.isFlashcardsEnabled()) {
+			broadcastInRoom(roomId, "countFlashcards", contentService.countFlashcardsForUserInternal(roomId));
+//			broadcastInRoom(roomId, "flipFlashcards", event.getEntity().getSettings().isFlipFlashcards());
+		}
 	}
 
-	@EventListener
-	public void handleLockFeedback(LockFeedbackEvent event) {
-//		broadcastInRoom(event.getRoomId(), "lockFeedback", event.getRoom().getSettings().isFeedbackLocked());
+	@EventListener(condition = "#event.stateName == 'settings'")
+	public void handleLockFeedback(StateChangeEvent<de.thm.arsnova.model.Room, de.thm.arsnova.model.Room.Settings> event) {
+		broadcastInRoom(event.getEntity().getId(), "lockFeedback", event.getEntity().getSettings().isFeedbackLocked());
 	}
 
 	@EventListener
@@ -647,9 +644,9 @@ public class ArsnovaSocketioServerImpl implements ArsnovaSocketioServer {
 
 	}
 
-	@EventListener
-	public void handleStatusRoom(StatusRoomEvent event) {
-//		this.reportRoomStatus(event.getRoomId(), !event.getRoom().isClosed());
+	@EventListener(condition = "#event.stateName == 'closed'")
+	public void handleRoomClosedStateChange(StateChangeEvent<de.thm.arsnova.model.Room, Boolean> event) {
+		this.reportRoomStatus(event.getEntity().getId(), !event.getNewValue());
 	}
 
 	@EventListener
-- 
GitLab