From 1a7b2dcdb8bceaae676951bce141636f4caa1ae9 Mon Sep 17 00:00:00 2001
From: Daniel Gerhardt <code@dgerhardt.net>
Date: Sun, 28 May 2017 21:28:29 +0200
Subject: [PATCH] Separate content persistance code and migrate it to Ektorp

(Lecturer/Skill)Question has been renamed to Content. Method names have
not been touched yet to ease reviewing the changes.
---
 .../de/thm/arsnova/cache/CacheBuster.java     |   2 +-
 .../java/de/thm/arsnova/config/AppConfig.java |   8 +
 .../arsnova/controller/CommentController.java |  18 +-
 ...Controller.java => ContentController.java} | 198 ++---
 .../arsnova/controller/LegacyController.java  |   6 +-
 .../java/de/thm/arsnova/dao/CouchDBDao.java   | 796 +-----------------
 .../java/de/thm/arsnova/dao/IDatabaseDao.java |  82 +-
 .../entities/{Question.java => Content.java}  | 170 +++-
 .../thm/arsnova/entities/PossibleAnswer.java  |   8 +
 .../CouchDbTypeFieldConverter.java            |   2 +
 .../arsnova/entities/transport/Answer.java    |  16 +-
 .../transport/AnswerQueueElement.java         |  12 +-
 .../transport/ImportExportSession.java        |  19 +-
 .../thm/arsnova/events/DeleteAnswerEvent.java |  12 +-
 .../arsnova/events/DeleteQuestionEvent.java   |  14 +-
 .../thm/arsnova/events/LockQuestionEvent.java |  14 +-
 .../arsnova/events/LockQuestionsEvent.java    |  14 +-
 .../de/thm/arsnova/events/LockVoteEvent.java  |  16 +-
 .../de/thm/arsnova/events/LockVotesEvent.java |  14 +-
 .../de/thm/arsnova/events/NewAnswerEvent.java |  12 +-
 .../thm/arsnova/events/NewQuestionEvent.java  |  14 +-
 .../arsnova/events/PiRoundCancelEvent.java    |   6 +-
 .../events/PiRoundDelayedStartEvent.java      |  14 +-
 .../thm/arsnova/events/PiRoundEndEvent.java   |   8 +-
 .../thm/arsnova/events/PiRoundResetEvent.java |   8 +-
 .../arsnova/events/UnlockQuestionEvent.java   |  14 +-
 .../arsnova/events/UnlockQuestionsEvent.java  |  14 +-
 .../thm/arsnova/events/UnlockVoteEvent.java   |  16 +-
 .../thm/arsnova/events/UnlockVotesEvent.java  |  14 +-
 .../persistance/ContentRepository.java        |  44 +
 .../couchdb/CouchDbContentRepository.java     | 514 +++++++++++
 .../couchdb/CouchDbSessionRepository.java     |  20 +-
 .../ApplicationPermissionEvaluator.java       |  14 +-
 ...estionService.java => ContentService.java} | 391 ++++-----
 ...stionService.java => IContentService.java} |  26 +-
 .../arsnova/socket/ARSnovaSocketIOServer.java |  92 +-
 .../message/{Question.java => Content.java}   |   8 +-
 ...erTest.java => ContentControllerTest.java} |   4 +-
 .../de/thm/arsnova/dao/StubDatabaseDao.java   | 223 +----
 .../{QuestionTest.java => ContentTest.java}   |  10 +-
 ...rviceTest.java => ContentServiceTest.java} |  28 +-
 41 files changed, 1311 insertions(+), 1604 deletions(-)
 rename src/main/java/de/thm/arsnova/controller/{LecturerQuestionController.java => ContentController.java} (76%)
 rename src/main/java/de/thm/arsnova/entities/{Question.java => Content.java} (75%)
 create mode 100644 src/main/java/de/thm/arsnova/persistance/ContentRepository.java
 create mode 100644 src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbContentRepository.java
 rename src/main/java/de/thm/arsnova/services/{QuestionService.java => ContentService.java} (69%)
 rename src/main/java/de/thm/arsnova/services/{IQuestionService.java => IContentService.java} (88%)
 rename src/main/java/de/thm/arsnova/socket/message/{Question.java => Content.java} (85%)
 rename src/test/java/de/thm/arsnova/controller/{LecturerQuestionControllerTest.java => ContentControllerTest.java} (96%)
 rename src/test/java/de/thm/arsnova/entities/{QuestionTest.java => ContentTest.java} (96%)
 rename src/test/java/de/thm/arsnova/services/{QuestionServiceTest.java => ContentServiceTest.java} (86%)

diff --git a/src/main/java/de/thm/arsnova/cache/CacheBuster.java b/src/main/java/de/thm/arsnova/cache/CacheBuster.java
index ed38eab6b..c998bd4f3 100644
--- a/src/main/java/de/thm/arsnova/cache/CacheBuster.java
+++ b/src/main/java/de/thm/arsnova/cache/CacheBuster.java
@@ -51,7 +51,7 @@ public class CacheBuster implements ICacheBuster, NovaEventVisitor {
 	@Override
 	public void visit(LockQuestionsEvent lockQuestionsEvent) { }
 
-	@CacheEvict(value = "answers", key = "#event.Question")
+	@CacheEvict(value = "answers", key = "#event.content")
 	@Override
 	public void visit(NewAnswerEvent event) { }
 
diff --git a/src/main/java/de/thm/arsnova/config/AppConfig.java b/src/main/java/de/thm/arsnova/config/AppConfig.java
index 146d44750..16db42433 100644
--- a/src/main/java/de/thm/arsnova/config/AppConfig.java
+++ b/src/main/java/de/thm/arsnova/config/AppConfig.java
@@ -27,16 +27,19 @@ import de.thm.arsnova.entities.Comment;
 import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.LogEntry;
 import de.thm.arsnova.entities.Motd;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.serialization.CouchDbDocumentModule;
 import de.thm.arsnova.entities.serialization.CouchDbObjectMapperFactory;
 import de.thm.arsnova.entities.serialization.View;
 import de.thm.arsnova.persistance.CommentRepository;
+import de.thm.arsnova.persistance.ContentRepository;
 import de.thm.arsnova.persistance.LogEntryRepository;
 import de.thm.arsnova.persistance.MotdRepository;
 import de.thm.arsnova.persistance.SessionRepository;
 import de.thm.arsnova.persistance.UserRepository;
 import de.thm.arsnova.persistance.couchdb.CouchDbCommentRepository;
+import de.thm.arsnova.persistance.couchdb.CouchDbContentRepository;
 import de.thm.arsnova.persistance.couchdb.CouchDbLogEntryRepository;
 import de.thm.arsnova.persistance.couchdb.CouchDbMotdRepository;
 import de.thm.arsnova.persistance.couchdb.CouchDbSessionRepository;
@@ -300,6 +303,11 @@ public class AppConfig extends WebMvcConfigurerAdapter {
 		return new CouchDbCommentRepository(Comment.class, couchDbConnector(), false);
 	}
 
+	@Bean
+	public ContentRepository contentRepository() throws Exception {
+		return new CouchDbContentRepository(Content.class, couchDbConnector(), false);
+	}
+
 	@Bean
 	public UserRepository userRepository() throws Exception {
 		return new CouchDbUserRepository(DbUser.class, couchDbConnector(), false);
diff --git a/src/main/java/de/thm/arsnova/controller/CommentController.java b/src/main/java/de/thm/arsnova/controller/CommentController.java
index 57beb79d6..e38c98630 100644
--- a/src/main/java/de/thm/arsnova/controller/CommentController.java
+++ b/src/main/java/de/thm/arsnova/controller/CommentController.java
@@ -20,7 +20,7 @@ package de.thm.arsnova.controller;
 import de.thm.arsnova.entities.CommentReadingCount;
 import de.thm.arsnova.entities.transport.Comment;
 import de.thm.arsnova.exceptions.BadRequestException;
-import de.thm.arsnova.services.IQuestionService;
+import de.thm.arsnova.services.IContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import de.thm.arsnova.web.Pagination;
 import io.swagger.annotations.Api;
@@ -45,11 +45,11 @@ import java.util.List;
  */
 @RestController
 @RequestMapping("/audiencequestion")
-@Api(value = "/audiencequestion", description = "the Audience Question API")
+@Api(value = "/audiencequestion", description = "the Audience Content API")
 public class CommentController extends PaginationController {
 
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
 	@ApiOperation(value = "Count all the comments in current session",
 			nickname = "getAudienceQuestionCount")
@@ -57,7 +57,7 @@ public class CommentController extends PaginationController {
 	@DeprecatedApi
 	@Deprecated
 	public int getInterposedCount(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey) {
-		return questionService.getInterposedCount(sessionkey);
+		return contentService.getInterposedCount(sessionkey);
 	}
 
 	@ApiOperation(value = "count all unread comments",
@@ -66,7 +66,7 @@ public class CommentController extends PaginationController {
 	@DeprecatedApi
 	@Deprecated
 	public CommentReadingCount getUnreadInterposedCount(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam("sessionkey") final String sessionkey, String user) {
-		return questionService.getInterposedReadingCount(sessionkey, user);
+		return contentService.getInterposedReadingCount(sessionkey, user);
 	}
 
 	@ApiOperation(value = "Retrieves all Comments for a Session",
@@ -74,14 +74,14 @@ public class CommentController extends PaginationController {
 	@RequestMapping(value = "/", method = RequestMethod.GET)
 	@Pagination
 	public List<Comment> getInterposedQuestions(@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey) {
-		return Comment.fromList(questionService.getInterposedQuestions(sessionkey, offset, limit));
+		return Comment.fromList(contentService.getInterposedQuestions(sessionkey, offset, limit));
 	}
 
 	@ApiOperation(value = "Retrieves an Comment",
 			nickname = "getInterposedQuestion")
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
 	public Comment getInterposedQuestion(@ApiParam(value = "ID of the Comment that needs to be deleted", required = true) @PathVariable final String questionId) {
-		return new Comment(questionService.readInterposedQuestion(questionId));
+		return new Comment(contentService.readInterposedQuestion(questionId));
 	}
 
 	@ApiOperation(value = "Creates a new Comment for a Session and returns the Comment's data",
@@ -95,7 +95,7 @@ public class CommentController extends PaginationController {
 			@ApiParam(value = "Session-Key from current session", required = true) @RequestParam final String sessionkey,
 			@ApiParam(value = "the body from the new comment", required = true) @RequestBody final de.thm.arsnova.entities.Comment comment
 			) {
-		if (questionService.saveQuestion(comment)) {
+		if (contentService.saveQuestion(comment)) {
 			return;
 		}
 
@@ -106,6 +106,6 @@ public class CommentController extends PaginationController {
 			nickname = "deleteInterposedQuestion")
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.DELETE)
 	public void deleteInterposedQuestion(@ApiParam(value = "ID of the comment that needs to be deleted", required = true) @PathVariable final String questionId) {
-		questionService.deleteInterposedQuestion(questionId);
+		contentService.deleteInterposedQuestion(questionId);
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java b/src/main/java/de/thm/arsnova/controller/ContentController.java
similarity index 76%
rename from src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
rename to src/main/java/de/thm/arsnova/controller/ContentController.java
index 85ee7c9c5..9695d3651 100644
--- a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
+++ b/src/main/java/de/thm/arsnova/controller/ContentController.java
@@ -19,12 +19,12 @@ package de.thm.arsnova.controller;
 
 import de.thm.arsnova.PaginationListDecorator;
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.exceptions.BadRequestException;
 import de.thm.arsnova.exceptions.ForbiddenException;
 import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
-import de.thm.arsnova.services.IQuestionService;
+import de.thm.arsnova.services.IContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import de.thm.arsnova.web.Pagination;
 import io.swagger.annotations.Api;
@@ -52,9 +52,9 @@ import java.util.List;
 @RestController
 @RequestMapping("/lecturerquestion")
 @Api(value = "/lecturerquestion", description = "Operations for Lecture Questions")
-public class LecturerQuestionController extends PaginationController {
+public class ContentController extends PaginationController {
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
 	@ApiOperation(value = "Get question with provided question Id",
 			nickname = "getQuestion")
@@ -62,56 +62,56 @@ public class LecturerQuestionController extends PaginationController {
 		@ApiResponse(code = 404, message = HTML_STATUS_404)
 	})
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
-	public Question getQuestion(@PathVariable final String questionId) {
-		final Question question = questionService.getQuestion(questionId);
-		if (question != null) {
-			return question;
+	public Content getQuestion(@PathVariable final String questionId) {
+		final Content content = contentService.getQuestion(questionId);
+		if (content != null) {
+			return content;
 		}
 
 		throw new NotFoundException();
 	}
 
-	@ApiOperation(value = "Post provided question",
+	@ApiOperation(value = "Post provided content",
 			nickname = "postQuestion")
 	@ApiResponses(value = {
 		@ApiResponse(code = 400, message = HTML_STATUS_400)
 	})
 	@RequestMapping(value = "/", method = RequestMethod.POST)
 	@ResponseStatus(HttpStatus.CREATED)
-	public Question postQuestion(@RequestBody final Question question) {
-		if (questionService.saveQuestion(question) != null) {
-			return question;
+	public Content postQuestion(@RequestBody final Content content) {
+		if (contentService.saveQuestion(content) != null) {
+			return content;
 		}
 		throw new BadRequestException();
 	}
 
-	@ApiOperation(value = "Post provided questions", nickname = "bulkPostQuestions")
+	@ApiOperation(value = "Post provided contents", nickname = "bulkPostQuestions")
 	@ApiResponses(value = {
 		@ApiResponse(code = 400, message = HTML_STATUS_400)
 	})
 	@RequestMapping(value = "/bulk", method = RequestMethod.POST)
 	@ResponseStatus(HttpStatus.CREATED)
-	public List<Question> bulkPostQuestions(@RequestBody final List<Question> questions) {
-		for (final Question question : questions) {
-			if (questionService.saveQuestion(question) == null) {
+	public List<Content> bulkPostQuestions(@RequestBody final List<Content> contents) {
+		for (final Content content : contents) {
+			if (contentService.saveQuestion(content) == null) {
 				throw new BadRequestException();
 			}
 		}
-		return questions;
+		return contents;
 	}
 
-	@ApiOperation(value = "Update the question, identified by provided id, with the provided question in the Request Body",
+	@ApiOperation(value = "Update the content, identified by provided id, with the provided content in the Request Body",
 			nickname = "updateQuestion")
 	@ApiResponses(value = {
 		@ApiResponse(code = 400, message = HTML_STATUS_400)
 	})
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.PUT)
-	public Question updateQuestion(
+	public Content updateQuestion(
 			@PathVariable final String questionId,
-			@RequestBody final Question question
+			@RequestBody final Content content
 			) {
 		try {
-			return questionService.update(question);
+			return contentService.update(content);
 		} catch (final Exception e) {
 			throw new BadRequestException();
 		}
@@ -126,9 +126,9 @@ public class LecturerQuestionController extends PaginationController {
 			) {
 
 		if (fcImage) {
-			return questionService.getQuestionFcImage(questionId);
+			return contentService.getQuestionFcImage(questionId);
 		} else {
-			return questionService.getQuestionImage(questionId);
+			return contentService.getQuestionImage(questionId);
 		}
 	}
 
@@ -139,9 +139,9 @@ public class LecturerQuestionController extends PaginationController {
 			) {
 
 		if (time == 0) {
-			questionService.startNewPiRound(questionId, null);
+			contentService.startNewPiRound(questionId, null);
 		} else {
-			questionService.startNewPiRoundDelayed(questionId, time);
+			contentService.startNewPiRoundDelayed(questionId, time);
 		}
 	}
 
@@ -151,7 +151,7 @@ public class LecturerQuestionController extends PaginationController {
 	public void cancelPiRound(
 			@PathVariable final String questionId
 			) {
-		questionService.cancelPiRoundChange(questionId);
+		contentService.cancelPiRoundChange(questionId);
 	}
 
 	@RequestMapping(value = "/{questionId}/resetpiroundstate", method = RequestMethod.POST)
@@ -160,7 +160,7 @@ public class LecturerQuestionController extends PaginationController {
 	public void resetPiQuestion(
 			@PathVariable final String questionId
 			) {
-		questionService.resetPiRoundState(questionId);
+		contentService.resetPiRoundState(questionId);
 	}
 
 	@ApiOperation(value = "Set voting admission on question, identified by provided id",
@@ -176,7 +176,7 @@ public class LecturerQuestionController extends PaginationController {
 			disable = disableVote;
 		}
 
-		questionService.setVotingAdmission(questionId, disable);
+		contentService.setVotingAdmission(questionId, disable);
 	}
 
 	@ApiOperation(value = "Set voting admission for all questions",
@@ -189,35 +189,35 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) final boolean preparationQuestionsOnly
 			) {
 		boolean disable = false;
-		List<Question> questions;
+		List<Content> contents;
 
 		if (disableVote != null) {
 			disable = disableVote;
 		}
 
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
-			questionService.setVotingAdmissions(sessionkey, disable, questions);
+			contents = contentService.getLectureQuestions(sessionkey);
+			contentService.setVotingAdmissions(sessionkey, disable, contents);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
-			questionService.setVotingAdmissions(sessionkey, disable, questions);
+			contents = contentService.getPreparationQuestions(sessionkey);
+			contentService.setVotingAdmissions(sessionkey, disable, contents);
 		} else {
-			questionService.setVotingAdmissionForAllQuestions(sessionkey, disable);
+			contentService.setVotingAdmissionForAllQuestions(sessionkey, disable);
 		}
 	}
 
-	@ApiOperation(value = "Publish a question, identified by provided id and question in Request Body.",
+	@ApiOperation(value = "Publish a content, identified by provided id and content in Request Body.",
 			nickname = "publishQuestion")
 	@RequestMapping(value = "/{questionId}/publish", method = RequestMethod.POST)
 	public void publishQuestion(
 			@PathVariable final String questionId,
 			@RequestParam(required = false) final Boolean publish,
-			@RequestBody final Question question
+			@RequestBody final Content content
 			) {
 		if (publish != null) {
-			question.setActive(publish);
+			content.setActive(publish);
 		}
-		questionService.update(question);
+		contentService.update(content);
 	}
 
 	@ApiOperation(value = "Publish all questions",
@@ -230,52 +230,52 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) final boolean preparationQuestionsOnly
 			) {
 		boolean p = publish == null || publish;
-		List<Question> questions;
+		List<Content> contents;
 
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
-			questionService.publishQuestions(sessionkey, p, questions);
+			contents = contentService.getLectureQuestions(sessionkey);
+			contentService.publishQuestions(sessionkey, p, contents);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
-			questionService.publishQuestions(sessionkey, p, questions);
+			contents = contentService.getPreparationQuestions(sessionkey);
+			contentService.publishQuestions(sessionkey, p, contents);
 		} else {
-			questionService.publishAll(sessionkey, p);
+			contentService.publishAll(sessionkey, p);
 		}
 	}
 
-	@ApiOperation(value = "Publish statistics from question with provided id",
+	@ApiOperation(value = "Publish statistics from content with provided id",
 			nickname = "publishStatistics")
 	@RequestMapping(value = "/{questionId}/publishstatistics", method = RequestMethod.POST)
 	public void publishStatistics(
 			@PathVariable final String questionId,
 			@RequestParam(required = false) final Boolean showStatistics,
-			@RequestBody final Question question
+			@RequestBody final Content content
 			) {
 		if (showStatistics != null) {
-			question.setShowStatistic(showStatistics);
+			content.setShowStatistic(showStatistics);
 		}
-		questionService.update(question);
+		contentService.update(content);
 	}
 
-	@ApiOperation(value = "Publish correct answer from question with provided id",
+	@ApiOperation(value = "Publish correct answer from content with provided id",
 			nickname = "publishCorrectAnswer")
 	@RequestMapping(value = "/{questionId}/publishcorrectanswer", method = RequestMethod.POST)
 	public void publishCorrectAnswer(
 			@PathVariable final String questionId,
 			@RequestParam(required = false) final Boolean showCorrectAnswer,
-			@RequestBody final Question question
+			@RequestBody final Content content
 			) {
 		if (showCorrectAnswer != null) {
-			question.setShowAnswer(showCorrectAnswer);
+			content.setShowAnswer(showCorrectAnswer);
 		}
-		questionService.update(question);
+		contentService.update(content);
 	}
 
 	@ApiOperation(value = "Get skill questions",
 			nickname = "getSkillQuestions")
 	@RequestMapping(value = "/", method = RequestMethod.GET)
 	@Pagination
-	public List<Question> getSkillQuestions(
+	public List<Content> getSkillQuestions(
 			@RequestParam final String sessionkey,
 			@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
 			@RequestParam(value = "flashcardsonly", defaultValue = "false") final boolean flashcardsOnly,
@@ -283,24 +283,24 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "requestImageData", defaultValue = "false") final boolean requestImageData,
 			final HttpServletResponse response
 			) {
-		List<Question> questions;
+		List<Content> contents;
 		if (lectureQuestionsOnly) {
-			questions = questionService.getLectureQuestions(sessionkey);
+			contents = contentService.getLectureQuestions(sessionkey);
 		} else if (flashcardsOnly) {
-			questions = questionService.getFlashcards(sessionkey);
+			contents = contentService.getFlashcards(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			questions = questionService.getPreparationQuestions(sessionkey);
+			contents = contentService.getPreparationQuestions(sessionkey);
 		} else {
-			questions = questionService.getSkillQuestions(sessionkey);
+			contents = contentService.getSkillQuestions(sessionkey);
 		}
-		if (questions == null || questions.isEmpty()) {
+		if (contents == null || contents.isEmpty()) {
 			response.setStatus(HttpStatus.NO_CONTENT.value());
 			return null;
 		} else if (!requestImageData) {
-			questions = questionService.replaceImageData(questions);
+			contents = contentService.replaceImageData(contents);
 		}
 
-		return new PaginationListDecorator<>(questions, offset, limit);
+		return new PaginationListDecorator<>(contents, offset, limit);
 	}
 
 	@ApiOperation(value = "Delete skill questions",
@@ -314,13 +314,13 @@ public class LecturerQuestionController extends PaginationController {
 			final HttpServletResponse response
 			) {
 		if (lectureQuestionsOnly) {
-			questionService.deleteLectureQuestions(sessionkey);
+			contentService.deleteLectureQuestions(sessionkey);
 		} else if (flashcardsOnly) {
-			questionService.deleteFlashcards(sessionkey);
+			contentService.deleteFlashcards(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			questionService.deletePreparationQuestions(sessionkey);
+			contentService.deletePreparationQuestions(sessionkey);
 		} else {
-			questionService.deleteAllQuestions(sessionkey);
+			contentService.deleteAllQuestions(sessionkey);
 		}
 	}
 
@@ -336,13 +336,13 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly
 			) {
 		if (lectureQuestionsOnly) {
-			return questionService.getLectureQuestionCount(sessionkey);
+			return contentService.getLectureQuestionCount(sessionkey);
 		} else if (flashcardsOnly) {
-			return questionService.getFlashcardCount(sessionkey);
+			return contentService.getFlashcardCount(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			return questionService.getPreparationQuestionCount(sessionkey);
+			return contentService.getPreparationQuestionCount(sessionkey);
 		} else {
-			return questionService.getSkillQuestionCount(sessionkey);
+			return contentService.getSkillQuestionCount(sessionkey);
 		}
 	}
 
@@ -352,7 +352,7 @@ public class LecturerQuestionController extends PaginationController {
 	public void deleteAnswersAndQuestion(
 			@PathVariable final String questionId
 			) {
-		questionService.deleteQuestion(questionId);
+		contentService.deleteQuestion(questionId);
 	}
 
 	@ApiOperation(value = "Get unanswered skill question ID by provided session ID",
@@ -367,11 +367,11 @@ public class LecturerQuestionController extends PaginationController {
 			) {
 		List<String> answers;
 		if (lectureQuestionsOnly) {
-			answers = questionService.getUnAnsweredLectureQuestionIds(sessionkey);
+			answers = contentService.getUnAnsweredLectureQuestionIds(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			answers = questionService.getUnAnsweredPreparationQuestionIds(sessionkey);
+			answers = contentService.getUnAnsweredPreparationQuestionIds(sessionkey);
 		} else {
-			answers = questionService.getUnAnsweredQuestionIds(sessionkey);
+			answers = contentService.getUnAnsweredQuestionIds(sessionkey);
 		}
 		if (answers == null || answers.isEmpty()) {
 			throw new NoContentException();
@@ -384,7 +384,7 @@ public class LecturerQuestionController extends PaginationController {
 	 * returns a JSON document which represents the given answer of a question.
 	 *
 	 * @param questionId
-	 *            CouchDB Question ID for which the given answer should be
+	 *            CouchDB Content ID for which the given answer should be
 	 *            retrieved
 	 * @return JSON Document of {@link Answer} or {@link NotFoundException}
 	 * @throws NotFoundException
@@ -402,7 +402,7 @@ public class LecturerQuestionController extends PaginationController {
 			@PathVariable final String questionId,
 			final HttpServletResponse response
 			) {
-		final Answer answer = questionService.getMyAnswer(questionId);
+		final Answer answer = contentService.getMyAnswer(questionId);
 		if (answer == null) {
 			response.setStatus(HttpStatus.NO_CONTENT.value());
 			return null;
@@ -418,7 +418,7 @@ public class LecturerQuestionController extends PaginationController {
 	 * properties are set
 	 *
 	 * @param questionId
-	 *            CouchDB Question ID for which the given answers should be
+	 *            CouchDB Content ID for which the given answers should be
 	 *            retrieved
 	 * @throws NotFoundException
 	 *             if wrong session, wrong question or no answers was given
@@ -436,16 +436,16 @@ public class LecturerQuestionController extends PaginationController {
 			) {
 		List<Answer> answers;
 		if (allAnswers) {
-			answers = questionService.getAllAnswers(questionId, -1, -1);
+			answers = contentService.getAllAnswers(questionId, -1, -1);
 		} else if (null == piRound) {
-			answers = questionService.getAnswers(questionId, offset, limit);
+			answers = contentService.getAnswers(questionId, offset, limit);
 		} else {
 			if (piRound < 1 || piRound > 2) {
 				response.setStatus(HttpStatus.BAD_REQUEST.value());
 
 				return null;
 			}
-			answers = questionService.getAnswers(questionId, piRound, offset, limit);
+			answers = contentService.getAnswers(questionId, piRound, offset, limit);
 		}
 		if (answers == null) {
 			return new ArrayList<>();
@@ -461,7 +461,7 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestBody final de.thm.arsnova.entities.transport.Answer answer,
 			final HttpServletResponse response
 			) {
-		return questionService.saveAnswer(questionId, answer);
+		return contentService.saveAnswer(questionId, answer);
 	}
 
 	@ApiOperation(value = "Update answer, provided in Request Body, identified by question ID and answer ID",
@@ -473,7 +473,7 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestBody final Answer answer,
 			final HttpServletResponse response
 			) {
-		return questionService.updateAnswer(answer);
+		return contentService.updateAnswer(answer);
 	}
 
 	@ApiOperation(value = "Get Image, identified by question ID and answer ID",
@@ -485,7 +485,7 @@ public class LecturerQuestionController extends PaginationController {
 			final HttpServletResponse response
 			) {
 
-		return questionService.getImage(questionId, answerId);
+		return contentService.getImage(questionId, answerId);
 	}
 
 	@ApiOperation(value = "Delete answer, identified by question ID and answer ID",
@@ -496,7 +496,7 @@ public class LecturerQuestionController extends PaginationController {
 			@PathVariable final String answerId,
 			final HttpServletResponse response
 			) {
-		questionService.deleteAnswer(questionId, answerId);
+		contentService.deleteAnswer(questionId, answerId);
 	}
 
 	@ApiOperation(value = "Delete answers from a question, identified by question ID",
@@ -506,7 +506,7 @@ public class LecturerQuestionController extends PaginationController {
 			@PathVariable final String questionId,
 			final HttpServletResponse response
 			) {
-		questionService.deleteAnswers(questionId);
+		contentService.deleteAnswers(questionId);
 	}
 
 	@ApiOperation(value = "Delete all answers and questions from a session, identified by sessionkey",
@@ -519,18 +519,18 @@ public class LecturerQuestionController extends PaginationController {
 			final HttpServletResponse response
 			) {
 		if (lectureQuestionsOnly) {
-			questionService.deleteAllLectureAnswers(sessionkey);
+			contentService.deleteAllLectureAnswers(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			questionService.deleteAllPreparationAnswers(sessionkey);
+			contentService.deleteAllPreparationAnswers(sessionkey);
 		} else {
-			questionService.deleteAllQuestionsAnswers(sessionkey);
+			contentService.deleteAllQuestionsAnswers(sessionkey);
 		}
 	}
 
 	/**
 	 *
 	 * @param questionId
-	 *            CouchDB Question ID for which the given answers should be
+	 *            CouchDB Content ID for which the given answers should be
 	 *            retrieved
 	 * @return count of answers for given question id
 	 * @throws NotFoundException
@@ -544,7 +544,7 @@ public class LecturerQuestionController extends PaginationController {
 	@Deprecated
 	@RequestMapping(value = "/{questionId}/answercount", method = RequestMethod.GET)
 	public int getAnswerCount(@PathVariable final String questionId) {
-		return questionService.getAnswerCount(questionId);
+		return contentService.getAnswerCount(questionId);
 	}
 
 	@ApiOperation(value = "Get the amount of answers for a question, identified by the question ID",
@@ -552,8 +552,8 @@ public class LecturerQuestionController extends PaginationController {
 	@RequestMapping(value = "/{questionId}/allroundanswercount", method = RequestMethod.GET)
 	public List<Integer> getAllAnswerCount(@PathVariable final String questionId) {
 		return Arrays.asList(
-			questionService.getAnswerCount(questionId, 1),
-			questionService.getAnswerCount(questionId, 2)
+			contentService.getAnswerCount(questionId, 1),
+			contentService.getAnswerCount(questionId, 2)
 		);
 	}
 
@@ -561,7 +561,7 @@ public class LecturerQuestionController extends PaginationController {
 			nickname = "getTotalAnswerCountByQuestion")
 	@RequestMapping(value = "/{questionId}/totalanswercount", method = RequestMethod.GET)
 	public int getTotalAnswerCountByQuestion(@PathVariable final String questionId) {
-		return questionService.getTotalAnswerCountByQuestion(questionId);
+		return contentService.getTotalAnswerCountByQuestion(questionId);
 	}
 
 	@ApiOperation(value = "Get the amount of answers and abstention answers by a question, identified by the question ID",
@@ -569,8 +569,8 @@ public class LecturerQuestionController extends PaginationController {
 	@RequestMapping(value = "/{questionId}/answerandabstentioncount", method = RequestMethod.GET)
 	public List<Integer> getAnswerAndAbstentionCount(@PathVariable final String questionId) {
 		return Arrays.asList(
-			questionService.getAnswerCount(questionId),
-			questionService.getAbstentionAnswerCount(questionId)
+			contentService.getAnswerCount(questionId),
+			contentService.getAbstentionAnswerCount(questionId)
 		);
 	}
 
@@ -579,7 +579,7 @@ public class LecturerQuestionController extends PaginationController {
 	@RequestMapping(value = "/{questionId}/freetextanswer/", method = RequestMethod.GET)
 	@Pagination
 	public List<Answer> getFreetextAnswers(@PathVariable final String questionId) {
-		return questionService.getFreetextAnswers(questionId, offset, limit);
+		return contentService.getFreetextAnswers(questionId, offset, limit);
 	}
 
 	@ApiOperation(value = "Get my answers of an session, identified by the sessionkey",
@@ -588,7 +588,7 @@ public class LecturerQuestionController extends PaginationController {
 	@Deprecated
 	@RequestMapping(value = "/myanswers", method = RequestMethod.GET)
 	public List<Answer> getMyAnswers(@RequestParam final String sessionkey) {
-		return questionService.getMyAnswers(sessionkey);
+		return contentService.getMyAnswers(sessionkey);
 	}
 
 	@ApiOperation(value = "Get the total amount of answers of an session, identified by the sessionkey",
@@ -602,11 +602,11 @@ public class LecturerQuestionController extends PaginationController {
 			@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly
 			) {
 		if (lectureQuestionsOnly) {
-			return questionService.countLectureQuestionAnswers(sessionkey);
+			return contentService.countLectureQuestionAnswers(sessionkey);
 		} else if (preparationQuestionsOnly) {
-			return questionService.countPreparationQuestionAnswers(sessionkey);
+			return contentService.countPreparationQuestionAnswers(sessionkey);
 		} else {
-			return questionService.getTotalAnswerCount(sessionkey);
+			return contentService.getTotalAnswerCount(sessionkey);
 		}
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/controller/LegacyController.java b/src/main/java/de/thm/arsnova/controller/LegacyController.java
index c9f809c63..a1d3a9de2 100644
--- a/src/main/java/de/thm/arsnova/controller/LegacyController.java
+++ b/src/main/java/de/thm/arsnova/controller/LegacyController.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.controller;
 
-import de.thm.arsnova.services.IQuestionService;
+import de.thm.arsnova.services.IContentService;
 import de.thm.arsnova.web.DeprecatedApi;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
@@ -33,7 +33,7 @@ import org.springframework.web.bind.annotation.ResponseBody;
 public class LegacyController extends AbstractController {
 
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
 	/* specific routes */
 
@@ -95,7 +95,7 @@ public class LegacyController extends AbstractController {
 	@RequestMapping(value = "/session/{sessionKey}/interposed", method = RequestMethod.DELETE)
 	@ResponseBody
 	public void deleteAllInterposedQuestions(@PathVariable final String sessionKey) {
-		questionService.deleteAllInterposedQuestions(sessionKey);
+		contentService.deleteAllInterposedQuestions(sessionKey);
 	}
 
 	@DeprecatedApi
diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
index d45937950..72ff1ff4f 100644
--- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
+++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
@@ -19,8 +19,6 @@ package de.thm.arsnova.dao;
 
 import com.fourspaces.couchdb.Database;
 import com.fourspaces.couchdb.Document;
-import com.fourspaces.couchdb.Results;
-import com.fourspaces.couchdb.RowResult;
 import com.fourspaces.couchdb.View;
 import com.fourspaces.couchdb.ViewResults;
 import com.google.common.collect.Lists;
@@ -30,16 +28,13 @@ import de.thm.arsnova.entities.*;
 import de.thm.arsnova.entities.transport.AnswerQueueElement;
 import de.thm.arsnova.events.NewAnswerEvent;
 import de.thm.arsnova.exceptions.NotFoundException;
+import de.thm.arsnova.persistance.ContentRepository;
 import de.thm.arsnova.persistance.LogEntryRepository;
 import de.thm.arsnova.persistance.MotdRepository;
 import de.thm.arsnova.persistance.SessionRepository;
 import de.thm.arsnova.services.ISessionService;
-import net.sf.ezmorph.Morpher;
-import net.sf.ezmorph.MorpherRegistry;
-import net.sf.ezmorph.bean.BeanMorpher;
 import net.sf.json.JSONArray;
 import net.sf.json.JSONObject;
-import net.sf.json.util.JSONUtils;
 import org.checkerframework.checker.nullness.qual.NonNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,7 +44,6 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.CachePut;
 import org.springframework.cache.annotation.Cacheable;
-import org.springframework.cache.annotation.Caching;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.ApplicationEventPublisherAware;
 import org.springframework.context.annotation.Profile;
@@ -59,7 +53,6 @@ import org.springframework.stereotype.Service;
 import java.io.IOException;
 import java.util.AbstractMap;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -75,7 +68,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
  *
  * This class makes use of Spring Framework's caching annotations. When you are about to add new functionality,
  * you should also think about the possibility of caching. Ideally, your methods should be dependent on domain
- * objects like Session or Question, which can be used as cache keys. Relying on plain String objects as a key, e.g.
+ * objects like Session or Content, which can be used as cache keys. Relying on plain String objects as a key, e.g.
  * by passing only a Session's keyword, will make your cache annotations less readable. You will also need to think
  * about cases where your cache needs to be updated and evicted.
  *
@@ -104,6 +97,9 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	@Autowired
 	private MotdRepository motdRepository;
 
+	@Autowired
+	private ContentRepository contentRepository;
+
 	private String databaseHost;
 	private int databasePort;
 	private String databaseName;
@@ -151,46 +147,6 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 		this.publisher = publisher;
 	}
 
-	@Cacheable("skillquestions")
-	@Override
-	public List<Question> getSkillQuestionsForUsers(final Session session) {
-		final List<Question> questions = new ArrayList<>();
-		final String viewName = "content/doc_by_sessionid_variant_active";
-		final View view1 = new View(viewName);
-		final View view2 = new View(viewName);
-		final View view3 = new View(viewName);
-		view1.setStartKey(session.getId(), "lecture", true);
-		view1.setEndKey(session.getId(), "lecture", true, "{}");
-		view2.setStartKey(session.getId(), "preparation", true);
-		view2.setEndKey(session.getId(), "preparation", true, "{}");
-		view3.setStartKey(session.getId(), "flashcard", true);
-		view3.setEndKey(session.getId(), "flashcard", true, "{}");
-		questions.addAll(getQuestions(view1, session));
-		questions.addAll(getQuestions(view2, session));
-		questions.addAll(getQuestions(view3, session));
-
-		return questions;
-	}
-
-	@Cacheable("skillquestions")
-	@Override
-	public List<Question> getSkillQuestionsForTeachers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKey(session.getId());
-		view.setEndKey(session.getId(), "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public int getSkillQuestionCount(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKey(session.getId());
-		view.setEndKey(session.getId(), "{}");
-
-		return getQuestionCount(view);
-	}
-
 	private Database getDatabase() {
 		if (database == null) {
 			try {
@@ -208,240 +164,6 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 		return database;
 	}
 
-	@Caching(evict = {@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session", condition = "#question.getQuestionVariant().equals('lecture')"),
-			@CacheEvict(value = "preparationquestions", key = "#session", condition = "#question.getQuestionVariant().equals('preparation')"),
-			@CacheEvict(value = "flashcardquestions", key = "#session", condition = "#question.getQuestionVariant().equals('flashcard')") },
-			put = {@CachePut(value = "questions", key = "#question._id")})
-	@Override
-	public Question saveQuestion(final Session session, final Question question) {
-		final Document q = toQuestionDocument(session, question);
-		try {
-			database.saveDocument(q);
-			question.set_id(q.getId());
-			question.set_rev(q.getRev());
-			return question;
-		} catch (final IOException e) {
-			logger.error("Could not save question {}.", question, e);
-		}
-		return null;
-	}
-
-	private Document toQuestionDocument(final Session session, final Question question) {
-		Document q = new Document();
-
-		question.updateRoundManagementState();
-		q.put("type", "skill_question");
-		q.put("questionType", question.getQuestionType());
-		q.put("ignoreCaseSensitive", question.isIgnoreCaseSensitive());
-		q.put("ignoreWhitespaces", question.isIgnoreWhitespaces());
-		q.put("ignorePunctuation", question.isIgnorePunctuation());
-		q.put("fixedAnswer", question.isFixedAnswer());
-		q.put("strictMode", question.isStrictMode());
-		q.put("rating", question.getRating());
-		q.put("correctAnswer", question.getCorrectAnswer());
-		q.put("questionVariant", question.getQuestionVariant());
-		q.put("sessionId", session.getId());
-		q.put("subject", question.getSubject());
-		q.put("text", question.getText());
-		q.put("active", question.isActive());
-		q.put("votingDisabled", question.isVotingDisabled());
-		q.put("number", 0); // TODO: This number is now unused. A clean up is necessary.
-		q.put("releasedFor", question.getReleasedFor());
-		q.put("possibleAnswers", question.getPossibleAnswers());
-		q.put("noCorrect", question.isNoCorrect());
-		q.put("piRound", question.getPiRound());
-		q.put("piRoundStartTime", question.getPiRoundStartTime());
-		q.put("piRoundEndTime", question.getPiRoundEndTime());
-		q.put("piRoundFinished", question.isPiRoundFinished());
-		q.put("piRoundActive", question.isPiRoundActive());
-		q.put("showStatistic", question.isShowStatistic());
-		q.put("showAnswer", question.isShowAnswer());
-		q.put("abstention", question.isAbstention());
-		q.put("image", question.getImage());
-		q.put("fcImage", question.getFcImage());
-		q.put("gridSize", question.getGridSize());
-		q.put("offsetX", question.getOffsetX());
-		q.put("offsetY", question.getOffsetY());
-		q.put("zoomLvl", question.getZoomLvl());
-		q.put("gridOffsetX", question.getGridOffsetX());
-		q.put("gridOffsetY", question.getGridOffsetY());
-		q.put("gridZoomLvl", question.getGridZoomLvl());
-		q.put("gridSizeX", question.getGridSizeX());
-		q.put("gridSizeY", question.getGridSizeY());
-		q.put("gridIsHidden", question.getGridIsHidden());
-		q.put("imgRotation", question.getImgRotation());
-		q.put("toggleFieldsLeft", question.getToggleFieldsLeft());
-		q.put("numClickableFields", question.getNumClickableFields());
-		q.put("thresholdCorrectAnswers", question.getThresholdCorrectAnswers());
-		q.put("cvIsColored", question.getCvIsColored());
-		q.put("gridLineColor", question.getGridLineColor());
-		q.put("numberOfDots", question.getNumberOfDots());
-		q.put("gridType", question.getGridType());
-		q.put("scaleFactor", question.getScaleFactor());
-		q.put("gridScaleFactor", question.getGridScaleFactor());
-		q.put("imageQuestion", question.isImageQuestion());
-		q.put("textAnswerEnabled", question.isTextAnswerEnabled());
-		q.put("timestamp", question.getTimestamp());
-		q.put("hint", question.getHint());
-		q.put("solution", question.getSolution());
-		return q;
-	}
-
-	/* TODO: Only evict cache entry for the question's session. This requires some refactoring. */
-	@Caching(evict = {@CacheEvict(value = "skillquestions", allEntries = true),
-			@CacheEvict(value = "lecturequestions", allEntries = true, condition = "#question.getQuestionVariant().equals('lecture')"),
-			@CacheEvict(value = "preparationquestions", allEntries = true, condition = "#question.getQuestionVariant().equals('preparation')"),
-			@CacheEvict(value = "flashcardquestions", allEntries = true, condition = "#question.getQuestionVariant().equals('flashcard')") },
-			put = {@CachePut(value = "questions", key = "#question._id")})
-	@Override
-	public Question updateQuestion(final Question question) {
-		try {
-			final Document q = database.getDocument(question.get_id());
-
-			question.updateRoundManagementState();
-			q.put("subject", question.getSubject());
-			q.put("text", question.getText());
-			q.put("active", question.isActive());
-			q.put("votingDisabled", question.isVotingDisabled());
-			q.put("releasedFor", question.getReleasedFor());
-			q.put("possibleAnswers", question.getPossibleAnswers());
-			q.put("noCorrect", question.isNoCorrect());
-			q.put("piRound", question.getPiRound());
-			q.put("piRoundStartTime", question.getPiRoundStartTime());
-			q.put("piRoundEndTime", question.getPiRoundEndTime());
-			q.put("piRoundFinished", question.isPiRoundFinished());
-			q.put("piRoundActive", question.isPiRoundActive());
-			q.put("showStatistic", question.isShowStatistic());
-			q.put("ignoreCaseSensitive", question.isIgnoreCaseSensitive());
-			q.put("ignoreWhitespaces", question.isIgnoreWhitespaces());
-			q.put("ignorePunctuation", question.isIgnorePunctuation());
-			q.put("fixedAnswer", question.isFixedAnswer());
-			q.put("strictMode", question.isStrictMode());
-			q.put("rating", question.getRating());
-			q.put("correctAnswer", question.getCorrectAnswer());
-			q.put("showAnswer", question.isShowAnswer());
-			q.put("abstention", question.isAbstention());
-			q.put("image", question.getImage());
-			q.put("fcImage", question.getFcImage());
-			q.put("gridSize", question.getGridSize());
-			q.put("offsetX", question.getOffsetX());
-			q.put("offsetY", question.getOffsetY());
-			q.put("zoomLvl", question.getZoomLvl());
-			q.put("gridOffsetX", question.getGridOffsetX());
-			q.put("gridOffsetY", question.getGridOffsetY());
-			q.put("gridZoomLvl", question.getGridZoomLvl());
-			q.put("gridSizeX", question.getGridSizeX());
-			q.put("gridSizeY", question.getGridSizeY());
-			q.put("gridIsHidden", question.getGridIsHidden());
-			q.put("imgRotation", question.getImgRotation());
-			q.put("toggleFieldsLeft", question.getToggleFieldsLeft());
-			q.put("numClickableFields", question.getNumClickableFields());
-			q.put("thresholdCorrectAnswers", question.getThresholdCorrectAnswers());
-			q.put("cvIsColored", question.getCvIsColored());
-			q.put("gridLineColor", question.getGridLineColor());
-			q.put("numberOfDots", question.getNumberOfDots());
-			q.put("gridType", question.getGridType());
-			q.put("scaleFactor", question.getScaleFactor());
-			q.put("gridScaleFactor", question.getGridScaleFactor());
-			q.put("imageQuestion", question.isImageQuestion());
-			q.put("hint", question.getHint());
-			q.put("solution", question.getSolution());
-
-			database.saveDocument(q);
-			question.set_rev(q.getRev());
-
-			return question;
-		} catch (final IOException e) {
-			logger.error("Could not update question {}.", question, e);
-		}
-
-		return null;
-	}
-
-	@Cacheable("questions")
-	@Override
-	public Question getQuestion(final String id) {
-		try {
-			final Document q = getDatabase().getDocument(id);
-			if (q == null) {
-				return null;
-			}
-			final Question question = (Question) JSONObject.toBean(q.getJSONObject(), Question.class);
-			final JSONArray possibleAnswers = q.getJSONObject().getJSONArray("possibleAnswers");
-			@SuppressWarnings("unchecked")
-			final Collection<PossibleAnswer> answers = JSONArray.toCollection(possibleAnswers, PossibleAnswer.class);
-
-			question.updateRoundManagementState();
-			question.setPossibleAnswers(new ArrayList<>(answers));
-			question.setSessionKeyword(sessionRepository.getSessionFromId(question.getSessionId()).getKeyword());
-			return question;
-		} catch (final IOException e) {
-			logger.error("Could not get question {}.", id, e);
-		}
-		return null;
-	}
-
-	@Override
-	public List<String> getQuestionIds(final Session session, final User user) {
-		View view = new View("content/by_sessionid_variant_active");
-		view.setKey(session.getId());
-		return collectQuestionIds(view);
-	}
-
-	/* TODO: Only evict cache entry for the question's session. This requires some refactoring. */
-	@Caching(evict = { @CacheEvict(value = "questions", key = "#question._id"),
-			@CacheEvict(value = "skillquestions", allEntries = true),
-			@CacheEvict(value = "lecturequestions", allEntries = true, condition = "#question.getQuestionVariant().equals('lecture')"),
-			@CacheEvict(value = "preparationquestions", allEntries = true, condition = "#question.getQuestionVariant().equals('preparation')"),
-			@CacheEvict(value = "flashcardquestions", allEntries = true, condition = "#question.getQuestionVariant().equals('flashcard')") })
-	@Override
-	public int deleteQuestionWithAnswers(final Question question) {
-		try {
-			int count = deleteAnswers(question);
-			deleteDocument(question.get_id());
-			dbLogger.log("delete", "type", "question", "answerCount", count);
-
-			return count;
-		} catch (final IOException e) {
-			logger.error("Could not delete question {}.", question.get_id(), e);
-		}
-
-		return 0;
-	}
-
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session"),
-			@CacheEvict(value = "preparationquestions", key = "#session"),
-			@CacheEvict(value = "flashcardquestions", key = "#session") })
-	@Override
-	public int[] deleteAllQuestionsWithAnswers(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId());
-		view.setEndKey(session.getId(), "{}");
-
-		return deleteAllQuestionDocumentsWithAnswers(view);
-	}
-
-	private int[] deleteAllQuestionDocumentsWithAnswers(final View view) {
-		final ViewResults results = getDatabase().view(view);
-
-		List<Question> questions = new ArrayList<>();
-		for (final Document d : results.getResults()) {
-			final Question q = new Question();
-			q.set_id(d.getId());
-			q.set_rev(d.getString("value"));
-			questions.add(q);
-		}
-
-		int[] count = deleteAllAnswersWithQuestions(questions);
-		dbLogger.log("delete", "type", "question", "questionCount", count[0]);
-		dbLogger.log("delete", "type", "answer", "answerCount", count[1]);
-
-		return count;
-	}
-
 	private void deleteDocument(final String documentId) throws IOException {
 		final Document d = getDatabase().getDocument(documentId);
 		getDatabase().deleteDocument(d);
@@ -449,10 +171,10 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 
 	@CacheEvict("answers")
 	@Override
-	public int deleteAnswers(final Question question) {
+	public int deleteAnswers(final Content content) {
 		try {
 			final View view = new View("answer/by_questionid");
-			view.setKey(question.get_id());
+			view.setKey(content.getId());
 			view.setIncludeDocs(true);
 			final ViewResults results = getDatabase().view(view);
 			final List<List<Document>> partitions = Lists.partition(results.getResults(), BULK_PARTITION_SIZE);
@@ -475,20 +197,12 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 
 			return count;
 		} catch (final IOException e) {
-			logger.error("Could not delete answers for question {}.", question.get_id(), e);
+			logger.error("Could not delete answers for content {}.", content.getId(), e);
 		}
 
 		return 0;
 	}
 
-	@Override
-	public List<String> getUnAnsweredQuestionIds(final Session session, final User user) {
-		final View view = new View("answer/questionid_by_user_sessionid_variant");
-		view.setStartKeyArray(user.getUsername(), session.getId());
-		view.setEndKeyArray(user.getUsername(), session.getId(), "{}");
-		return collectUnansweredQuestionIds(getQuestionIds(session, user), view);
-	}
-
 	@Override
 	public Answer getMyAnswer(final User me, final String questionId, final int piRound) {
 
@@ -526,8 +240,8 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<Answer> getAnswers(final Question question, final int piRound) {
-		final String questionId = question.get_id();
+	public List<Answer> getAnswers(final Content content, final int piRound) {
+		final String questionId = content.getId();
 		final View view = new View("answer/by_questionid_piround_text_subject");
 		if (2 == piRound) {
 			view.setStartKey(questionId, 2);
@@ -556,8 +270,8 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public List<Answer> getAllAnswers(final Question question) {
-		final String questionId = question.get_id();
+	public List<Answer> getAllAnswers(final Content content) {
+		final String questionId = content.getId();
 		final View view = new View("answer/by_questionid_piround_text_subject");
 		view.setStartKeyArray(questionId);
 		view.setEndKeyArray(questionId, "{}");
@@ -584,8 +298,8 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 
 	@Cacheable("answers")
 	@Override
-	public List<Answer> getAnswers(final Question question) {
-		return this.getAnswers(question, question.getPiRound());
+	public List<Answer> getAnswers(final Content content) {
+		return this.getAnswers(content, content.getPiRound());
 	}
 
 	@Override
@@ -602,10 +316,10 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public int getAnswerCount(final Question question, final int piRound) {
+	public int getAnswerCount(final Content content, final int piRound) {
 		final View view = new View("answer/by_questionid_piround_text_subject");
-		view.setStartKey(question.get_id(), piRound);
-		view.setEndKey(question.get_id(), piRound, "{}");
+		view.setStartKey(content.getId(), piRound);
+		view.setEndKey(content.getId(), piRound, "{}");
 		view.setGroup(true);
 		final ViewResults results = getDatabase().view(view);
 		if (results.getResults().isEmpty()) {
@@ -616,10 +330,10 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 	}
 
 	@Override
-	public int getTotalAnswerCountByQuestion(final Question question) {
+	public int getTotalAnswerCountByQuestion(final Content content) {
 		final View view = new View("answer/by_questionid_piround_text_subject");
-		view.setStartKeyArray(question.get_id());
-		view.setEndKeyArray(question.get_id(), "{}");
+		view.setStartKeyArray(content.getId());
+		view.setEndKeyArray(content.getId(), "{}");
 		view.setGroup(true);
 		final ViewResults results = getDatabase().view(view);
 
@@ -773,9 +487,9 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 		return stats;
 	}
 
-	@CacheEvict(value = "answers", key = "#question")
+	@CacheEvict(value = "answers", key = "#content")
 	@Override
-	public Answer saveAnswer(final Answer answer, final User user, final Question question, final Session session) {
+	public Answer saveAnswer(final Answer answer, final User user, final Content content, final Session session) {
 		final Document a = new Document();
 		a.put("type", "skill_question_answer");
 		a.put("sessionId", answer.getSessionId());
@@ -792,7 +506,7 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 		a.put("abstention", answer.isAbstention());
 		a.put("answerImage", answer.getAnswerImage());
 		a.put("answerThumbnailImage", answer.getAnswerThumbnailImage());
-		AnswerQueueElement answerQueueElement = new AnswerQueueElement(session, question, answer, user);
+		AnswerQueueElement answerQueueElement = new AnswerQueueElement(session, content, answer, user);
 		this.answerQueue.offer(new AbstractMap.SimpleEntry<>(a, answerQueueElement));
 		return answer;
 	}
@@ -937,131 +651,6 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 		return 0;
 	}
 
-	@Cacheable("lecturequestions")
-	@Override
-	public List<Question> getLectureQuestionsForUsers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "lecture", true);
-		view.setEndKeyArray(session.getId(), "lecture", true, "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public List<Question> getLectureQuestionsForTeachers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "lecture");
-		view.setEndKeyArray(session.getId(), "lecture", "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Cacheable("flashcardquestions")
-	@Override
-	public List<Question> getFlashcardsForUsers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "flashcard", true);
-		view.setEndKeyArray(session.getId(), "flashcard", true, "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public List<Question> getFlashcardsForTeachers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "flashcard");
-		view.setEndKeyArray(session.getId(), "flashcard", "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Cacheable("preparationquestions")
-	@Override
-	public List<Question> getPreparationQuestionsForUsers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "preparation", true);
-		view.setEndKeyArray(session.getId(), "preparation", true, "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public List<Question> getPreparationQuestionsForTeachers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "preparation");
-		view.setEndKeyArray(session.getId(), "preparation", "{}");
-
-		return getQuestions(view, session);
-	}
-
-	@Override
-	public List<Question> getAllSkillQuestions(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId());
-		view.setEndKeyArray(session.getId(), "{}");
-
-		return getQuestions(view, session);
-	}
-
-	private List<Question> getQuestions(final View view, final Session session) {
-		final ViewResults viewResults = getDatabase().view(view);
-		if (viewResults == null || viewResults.isEmpty()) {
-			return null;
-		}
-
-		final List<Question> questions = new ArrayList<>();
-
-		Results<Question> results = getDatabase().queryView(view, Question.class);
-		for (final RowResult<Question> row : results.getRows()) {
-			Question question = row.getValue();
-			question.updateRoundManagementState();
-			question.setSessionKeyword(session.getKeyword());
-			if (!"freetext".equals(question.getQuestionType()) && 0 == question.getPiRound()) {
-				/* needed for legacy questions whose piRound property has not been set */
-				question.setPiRound(1);
-			}
-
-			questions.add(question);
-		}
-		return questions;
-	}
-
-	@Override
-	public int getLectureQuestionCount(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "lecture");
-		view.setEndKeyArray(session.getId(), "lecture", "{}");
-
-		return getQuestionCount(view);
-	}
-
-	@Override
-	public int getFlashcardCount(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "flashcard");
-		view.setEndKeyArray(session.getId(), "flashcard", "{}");
-
-		return getQuestionCount(view);
-	}
-
-	@Override
-	public int getPreparationQuestionCount(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "preparation");
-		view.setEndKeyArray(session.getId(), "preparation", "{}");
-
-		return getQuestionCount(view);
-	}
-
-	private int getQuestionCount(final View view) {
-		view.setReduce(true);
-		final ViewResults results = getDatabase().view(view);
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			return 0;
-		}
-		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-	}
-
 	@Override
 	public int countLectureQuestionAnswers(final Session session) {
 		return countQuestionVariantAnswers(session, "lecture");
@@ -1083,257 +672,40 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
 	}
 
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict("skillquestions"),
-			@CacheEvict("lecturequestions"),
-			@CacheEvict(value = "answers", allEntries = true)})
-	@Override
-	public int[] deleteAllLectureQuestionsWithAnswers(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "lecture");
-		view.setEndKey(session.getId(), "lecture", "{}");
-
-		return deleteAllQuestionDocumentsWithAnswers(view);
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict("skillquestions"),
-			@CacheEvict("flashcardquestions"),
-			@CacheEvict(value = "answers", allEntries = true)})
-	@Override
-	public int[] deleteAllFlashcardsWithAnswers(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "flashcard");
-		view.setEndKey(session.getId(), "flashcard", "{}");
-
-		return deleteAllQuestionDocumentsWithAnswers(view);
-	}
-
-	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict("skillquestions"),
-			@CacheEvict("preparationquestions"),
-			@CacheEvict(value = "answers", allEntries = true)})
-	@Override
-	public int[] deleteAllPreparationQuestionsWithAnswers(final Session session) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "preparation");
-		view.setEndKey(session.getId(), "preparation", "{}");
-
-		return deleteAllQuestionDocumentsWithAnswers(view);
-	}
-
-	@Override
-	public List<String> getUnAnsweredLectureQuestionIds(final Session session, final User user) {
-		final View view = new View("answer/questionid_piround_by_user_sessionid_variant");
-		view.setKey(user.getUsername(), session.getId(), "lecture");
-		return collectUnansweredQuestionIdsByPiRound(getDatabaseDao().getLectureQuestionsForUsers(session), view);
-	}
-
-	@Override
-	public List<String> getUnAnsweredPreparationQuestionIds(final Session session, final User user) {
-		final View view = new View("answer/questionid_piround_by_user_sessionid_variant");
-		view.setKey(user.getUsername(), session.getId(), "preparation");
-		return collectUnansweredQuestionIdsByPiRound(getDatabaseDao().getPreparationQuestionsForUsers(session), view);
-	}
-
-	private List<String> collectUnansweredQuestionIds(
-			final List<String> questions,
-			final View view
-			) {
-		final ViewResults answeredQuestions = getDatabase().view(view);
-
-		final List<String> answered = new ArrayList<>();
-		for (final Document d : answeredQuestions.getResults()) {
-			answered.add(d.getString("value"));
-		}
-
-		final List<String> unanswered = new ArrayList<>();
-		for (final String questionId : questions) {
-			if (!answered.contains(questionId)) {
-				unanswered.add(questionId);
-			}
-		}
-		return unanswered;
-	}
-
-	private List<String> collectUnansweredQuestionIdsByPiRound(
-			final List<Question> questions,
-			final View view
-			) {
-		final ViewResults answeredQuestions = getDatabase().view(view);
-
-		final Map<String, Integer> answered = new HashMap<>();
-		for (final Document d : answeredQuestions.getResults()) {
-			answered.put(d.getJSONArray("value").getString(0), d.getJSONArray("value").getInt(1));
-		}
-
-		final List<String> unanswered = new ArrayList<>();
-
-		for (final Question question : questions) {
-			if (!"slide".equals(question.getQuestionType()) && (!answered.containsKey(question.get_id())
-					|| (answered.containsKey(question.get_id()) && answered.get(question.get_id()) != question.getPiRound()))) {
-				unanswered.add(question.get_id());
-			}
-		}
-
-		return unanswered;
-	}
-
-	private List<String> collectQuestionIds(final View view) {
-		final ViewResults results = getDatabase().view(view);
-		if (results.getResults().isEmpty()) {
-			return new ArrayList<>();
-		}
-		final List<String> ids = new ArrayList<>();
-		for (final Document d : results.getResults()) {
-			ids.add(d.getId());
-		}
-		return ids;
-	}
-
-	@Override
-	public List<Question> publishAllQuestions(final Session session, final boolean publish) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId());
-		view.setEndKeyArray(session.getId(), "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().publishQuestions(session, publish, questions);
-
-		return questions;
-	}
-
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session"),
-			@CacheEvict(value = "preparationquestions", key = "#session"),
-			@CacheEvict(value = "flashcardquestions", key = "#session") })
-	@Override
-	public void publishQuestions(final Session session, final boolean publish, List<Question> questions) {
-		for (final Question q : questions) {
-			q.setActive(publish);
-		}
-		final List<Document> documents = new ArrayList<>();
-		for (final Question q : questions) {
-			final Document d = toQuestionDocument(session, q);
-			d.setId(q.get_id());
-			d.setRev(q.get_rev());
-			documents.add(d);
-		}
-		try {
-			database.bulkSaveDocuments(documents.toArray(new Document[documents.size()]));
-		} catch (final IOException e) {
-			logger.error("Could not bulk publish all questions.", e);
-		}
-	}
-
-	@Override
-	public List<Question> setVotingAdmissionForAllQuestions(final Session session, final boolean disableVoting) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId());
-		view.setEndKeyArray(session.getId(), "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().setVotingAdmissions(session, disableVoting, questions);
-
-		return questions;
-	}
-
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session"),
-			@CacheEvict(value = "preparationquestions", key = "#session"),
-			@CacheEvict(value = "flashcardquestions", key = "#session") })
-	@Override
-	public void setVotingAdmissions(final Session session, final boolean disableVoting, List<Question> questions) {
-		for (final Question q : questions) {
-			if (!"flashcard".equals(q.getQuestionType())) {
-				q.setVotingDisabled(disableVoting);
-			}
-		}
-		final List<Document> documents = new ArrayList<>();
-		for (final Question q : questions) {
-			final Document d = toQuestionDocument(session, q);
-			d.setId(q.get_id());
-			d.setRev(q.get_rev());
-			documents.add(d);
-		}
-
-		try {
-			database.bulkSaveDocuments(documents.toArray(new Document[documents.size()]));
-		} catch (final IOException e) {
-			logger.error("Could not bulk set voting admission for all questions.", e);
-		}
-	}
-
 	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
 	@CacheEvict(value = "answers", allEntries = true)
 	@Override
 	public int deleteAllQuestionsAnswers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId());
-		view.setEndKeyArray(session.getId(), "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().resetQuestionsRoundState(session, questions);
+		final List<Content> contents = contentRepository.getQuestions(session.getId());
+		contentRepository.resetQuestionsRoundState(session, contents);
 
-		return deleteAllAnswersForQuestions(questions);
+		return deleteAllAnswersForQuestions(contents);
 	}
 
 	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
 	@CacheEvict(value = "answers", allEntries = true)
 	@Override
 	public int deleteAllPreparationAnswers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "preparation");
-		view.setEndKeyArray(session.getId(), "preparation", "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().resetQuestionsRoundState(session, questions);
+		final List<Content> contents = contentRepository.getQuestions(session.getId(), "preparation");
+		contentRepository.resetQuestionsRoundState(session, contents);
 
-		return deleteAllAnswersForQuestions(questions);
+		return deleteAllAnswersForQuestions(contents);
 	}
 
 	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
 	@CacheEvict(value = "answers", allEntries = true)
 	@Override
 	public int deleteAllLectureAnswers(final Session session) {
-		final View view = new View("content/doc_by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), "lecture");
-		view.setEndKeyArray(session.getId(), "lecture", "{}");
-		final List<Question> questions = getQuestions(view, session);
-		getDatabaseDao().resetQuestionsRoundState(session, questions);
+		final List<Content> contents = contentRepository.getQuestions(session.getId(), "lecture");
+		contentRepository.resetQuestionsRoundState(session, contents);
 
-		return deleteAllAnswersForQuestions(questions);
+		return deleteAllAnswersForQuestions(contents);
 	}
 
-	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
-			@CacheEvict(value = "skillquestions", key = "#session"),
-			@CacheEvict(value = "lecturequestions", key = "#session"),
-			@CacheEvict(value = "preparationquestions", key = "#session"),
-			@CacheEvict(value = "flashcardquestions", key = "#session") })
-	@Override
-	public void resetQuestionsRoundState(final Session session, List<Question> questions) {
-		for (final Question q : questions) {
-			q.resetQuestionState();
-		}
-		final List<Document> documents = new ArrayList<>();
-		for (final Question q : questions) {
-			final Document d = toQuestionDocument(session, q);
-			d.setId(q.get_id());
-			d.setRev(q.get_rev());
-			documents.add(d);
-		}
-		try {
-			database.bulkSaveDocuments(documents.toArray(new Document[documents.size()]));
-		} catch (final IOException e) {
-			logger.error("Could not bulk reset all questions round state.", e);
-		}
-	}
-
-	private int deleteAllAnswersForQuestions(List<Question> questions) {
+	public int deleteAllAnswersForQuestions(List<Content> contents) {
 		List<String> questionIds = new ArrayList<>();
-		for (Question q : questions) {
-			questionIds.add(q.get_id());
+		for (Content q : contents) {
+			questionIds.add(q.getId());
 		}
 		final View bulkView = new View("answer/by_questionid");
 		bulkView.setKeys(questionIds);
@@ -1356,15 +728,15 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 		return 0;
 	}
 
-	private int[] deleteAllAnswersWithQuestions(List<Question> questions) {
+	public int[] deleteAllAnswersWithQuestions(List<Content> contents) {
 		List<String> questionIds = new ArrayList<>();
 		final List<Document> allQuestions = new ArrayList<>();
-		for (Question q : questions) {
+		for (Content q : contents) {
 			final Document d = new Document();
-			d.put("_id", q.get_id());
-			d.put("_rev", q.get_rev());
+			d.put("_id", q.getId());
+			d.put("_rev", q.getRevision());
 			d.put("_deleted", true);
-			questionIds.add(q.get_id());
+			questionIds.add(q.getId());
 			allQuestions.add(d);
 		}
 		final View bulkView = new View("answer/by_questionid");
@@ -1386,7 +758,7 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 
 			return new int[] {deleteList.size(), result.size()};
 		} catch (IOException e) {
-			logger.error("Could not bulk delete questions and answers.", e);
+			logger.error("Could not bulk delete contents and answers.", e);
 		}
 
 		return new int[] {0, 0};
@@ -1433,92 +805,6 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
 		return courseScore;
 	}
 
-	@Override
-	public List<String> getSubjects(Session session, String questionVariant) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), questionVariant);
-		view.setEndKeyArray(session.getId(), questionVariant, "{}");
-		ViewResults results = this.getDatabase().view(view);
-
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			return null;
-		}
-
-		Set<String> uniqueSubjects = new HashSet<>();
-
-		for (final Document d : results.getResults()) {
-			uniqueSubjects.add(d.getJSONArray("key").getString(3));
-		}
-
-		return new ArrayList<>(uniqueSubjects);
-	}
-
-	/* TODO: remove if this method is no longer used */
-	@Override
-	public List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject) {
-		final View view = new View("content/by_sessionid_variant_active");
-		view.setStartKeyArray(session.getId(), questionVariant, 1,	subject);
-		view.setEndKeyArray(session.getId(), questionVariant, 1, subject, "{}");
-		ViewResults results = this.getDatabase().view(view);
-
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			return null;
-		}
-
-		List<String> qids = new ArrayList<>();
-
-		for (final Document d : results.getResults()) {
-			final String s = d.getId();
-			qids.add(s);
-		}
-
-		return qids;
-	}
-
-	@Override
-	public List<Question> getQuestionsByIds(List<String> ids, final Session session) {
-		View view = new View("_all_docs");
-		view.setKeys(ids);
-		view.setIncludeDocs(true);
-		final List<Document> questiondocs = getDatabase().view(view).getResults();
-		if (questiondocs == null || questiondocs.isEmpty()) {
-
-			return null;
-		}
-		final List<Question> result = new ArrayList<>();
-		final MorpherRegistry morpherRegistry = JSONUtils.getMorpherRegistry();
-		final Morpher dynaMorpher = new BeanMorpher(PossibleAnswer.class, morpherRegistry);
-		morpherRegistry.registerMorpher(dynaMorpher);
-		for (final Document document : questiondocs) {
-			if (!"".equals(document.optString("error"))) {
-				// Skip documents we could not load. Maybe they were deleted.
-				continue;
-			}
-			final Question question = (Question) JSONObject.toBean(
-					document.getJSONObject().getJSONObject("doc"),
-					Question.class
-					);
-			@SuppressWarnings("unchecked")
-			final Collection<PossibleAnswer> answers = JSONArray.toCollection(
-					document.getJSONObject().getJSONObject("doc").getJSONArray("possibleAnswers"),
-					PossibleAnswer.class
-					);
-			question.setPossibleAnswers(new ArrayList<>(answers));
-			question.setSessionKeyword(session.getKeyword());
-			if (!"freetext".equals(question.getQuestionType()) && 0 == question.getPiRound()) {
-				/* needed for legacy questions whose piRound property has not been set */
-				question.setPiRound(1);
-			}
-
-			if (question.getImage() != null) {
-				question.setImage("true");
-			}
-
-			result.add(question);
-		}
-		return result;
-	}
-
 	@Override
 	@Cacheable(cacheNames = "motdlist", key = "#p0")
 	public MotdList getMotdListForUser(final String username) {
diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
index 9d4849505..532729fdf 100644
--- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
+++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
@@ -26,35 +26,17 @@ import java.util.List;
  * All methods the database must support.
  */
 public interface IDatabaseDao {
-	Question saveQuestion(Session session, Question question);
-
-	Question getQuestion(String id);
-
-	List<Question> getSkillQuestionsForUsers(Session session);
-
-	List<Question> getSkillQuestionsForTeachers(Session session);
-
-	int getSkillQuestionCount(Session session);
-
-	List<String> getQuestionIds(Session session, User user);
-
-	int deleteQuestionWithAnswers(Question question);
-
-	int[] deleteAllQuestionsWithAnswers(Session session);
-
-	List<String> getUnAnsweredQuestionIds(Session session, User user);
-
 	Answer getMyAnswer(User me, String questionId, int piRound);
 
-	List<Answer> getAnswers(Question question, int piRound);
+	List<Answer> getAnswers(Content content, int piRound);
 
-	List<Answer> getAnswers(Question question);
+	List<Answer> getAnswers(Content content);
 
-	List<Answer> getAllAnswers(Question question);
+	List<Answer> getAllAnswers(Content content);
 
-	int getAnswerCount(Question question, int piRound);
+	int getAnswerCount(Content content, int piRound);
 
-	int getTotalAnswerCountByQuestion(Question question);
+	int getTotalAnswerCountByQuestion(Content content);
 
 	int getAbstentionAnswerCount(String questionId);
 
@@ -64,11 +46,9 @@ public interface IDatabaseDao {
 
 	int getTotalAnswerCount(String sessionKey);
 
-	Question updateQuestion(Question question);
+	int deleteAnswers(Content content);
 
-	int deleteAnswers(Question question);
-
-	Answer saveAnswer(Answer answer, User user, Question question, Session session);
+	Answer saveAnswer(Answer answer, User user, Content content, Session session);
 
 	Answer updateAnswer(Answer answer);
 
@@ -76,44 +56,10 @@ public interface IDatabaseDao {
 
 	int deleteInactiveGuestVisitedSessionLists(long lastActivityBefore);
 
-	List<Question> getLectureQuestionsForUsers(Session session);
-
-	List<Question> getLectureQuestionsForTeachers(Session session);
-
-	List<Question> getFlashcardsForUsers(Session session);
-
-	List<Question> getFlashcardsForTeachers(Session session);
-
-	List<Question> getPreparationQuestionsForUsers(Session session);
-
-	List<Question> getPreparationQuestionsForTeachers(Session session);
-
-	List<Question> getAllSkillQuestions(Session session);
-
-	int getLectureQuestionCount(Session session);
-
-	int getFlashcardCount(Session session);
-
-	int getPreparationQuestionCount(Session session);
-
 	int countLectureQuestionAnswers(Session session);
 
 	int countPreparationQuestionAnswers(Session session);
 
-	int[] deleteAllLectureQuestionsWithAnswers(Session session);
-
-	int[] deleteAllFlashcardsWithAnswers(Session session);
-
-	int[] deleteAllPreparationQuestionsWithAnswers(Session session);
-
-	List<String> getUnAnsweredLectureQuestionIds(Session session, User user);
-
-	List<String> getUnAnsweredPreparationQuestionIds(Session session, User user);
-
-	void publishQuestions(Session session, boolean publish, List<Question> questions);
-
-	List<Question> publishAllQuestions(Session session, boolean publish);
-
 	int deleteAllQuestionsAnswers(Session session);
 
 	CourseScore getLearningProgress(Session session);
@@ -124,21 +70,11 @@ public interface IDatabaseDao {
 
 	Statistics getStatistics();
 
-	List<String> getSubjects(Session session, String questionVariant);
-
-	List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject);
-
-	List<Question> getQuestionsByIds(List<String> ids, Session session);
-
-	void resetQuestionsRoundState(Session session, List<Question> questions);
-
-	void setVotingAdmissions(Session session, boolean disableVoting, List<Question> questions);
-
-	List<Question> setVotingAdmissionForAllQuestions(Session session, boolean disableVoting);
-
 	<T> T getObjectFromId(String documentId, Class<T> klass);
 
 	MotdList getMotdListForUser(final String username);
 
 	MotdList createOrUpdateMotdList(MotdList motdlist);
+
+	int[] deleteAllAnswersWithQuestions(List<Content> contents);
 }
diff --git a/src/main/java/de/thm/arsnova/entities/Question.java b/src/main/java/de/thm/arsnova/entities/Content.java
similarity index 75%
rename from src/main/java/de/thm/arsnova/entities/Question.java
rename to src/main/java/de/thm/arsnova/entities/Content.java
index a99670287..653a64551 100644
--- a/src/main/java/de/thm/arsnova/entities/Question.java
+++ b/src/main/java/de/thm/arsnova/entities/Content.java
@@ -17,20 +17,21 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
-import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
 
 /**
  * A question the teacher is asking.
  */
-@ApiModel(value = "lecturerquestion", description = "the question entity")
-public class Question implements Serializable {
-
-	private String type;
+@ApiModel(value = "content", description = "the content entity")
+public class Content implements Entity {
+	private String id;
+	private String rev;
 	private String questionType;
 	private String questionVariant;
 	private String subject;
@@ -62,8 +63,6 @@ public class Question implements Serializable {
 	private boolean strictMode;
 	private int rating;
 	private String correctAnswer;
-	private String _id;
-	private String _rev;
 
 	private String image;
 	private String fcImage;
@@ -92,56 +91,78 @@ public class Question implements Serializable {
 	private String hint;
 	private String solution;
 
-	@ApiModelProperty(required = true, value = "the type")
-	public final String getType() {
-		return type;
+	@ApiModelProperty(required = true, value = "the couchDB ID")
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getId() {
+		return id;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setId(final String id) {
+		this.id = id;
+	}
+
+	@JsonView({View.Persistence.class, View.Public.class})
+	public void setRevision(final String rev) {
+		this.rev = rev;
 	}
 
-	public final void setType(final String type) {
-		this.type = type;
+	@JsonView({View.Persistence.class, View.Public.class})
+	public String getRevision() {
+		return rev;
 	}
 
 	@ApiModelProperty(required = true, value = "the question type")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getQuestionType() {
 		return questionType;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setQuestionType(final String questionType) {
 		this.questionType = questionType;
 	}
 
 	@ApiModelProperty(required = true, value = "either lecture or preparation")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getQuestionVariant() {
 		return questionVariant;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setQuestionVariant(final String questionVariant) {
 		this.questionVariant = questionVariant;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display subject")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getSubject() {
 		return subject;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setSubject(final String subject) {
 		this.subject = subject;
 	}
 
 	@ApiModelProperty(required = true, value = "the text")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getText() {
 		return text;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setText(final String text) {
 		this.text = text;
 	}
 
 	@ApiModelProperty(required = true, value = "true for active question")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final boolean isActive() {
 		return active;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setActive(final boolean active) {
 		this.active = active;
 	}
@@ -156,10 +177,12 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "list of possible answers")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final List<PossibleAnswer> getPossibleAnswers() {
 		return possibleAnswers;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setPossibleAnswers(final List<PossibleAnswer> possibleAnswers) {
 		this.possibleAnswers = possibleAnswers;
 	}
@@ -174,10 +197,12 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "couchDB ID of the session, the question is assigned to")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getSessionId() {
 		return sessionId;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setSessionId(final String sessionId) {
 		this.sessionId = sessionId;
 	}
@@ -201,10 +226,12 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "creation date timestamp")
+	@JsonView(View.Persistence.class)
 	public final long getTimestamp() {
 		return timestamp;
 	}
 
+	@JsonView(View.Persistence.class)
 	public final void setTimestamp(final long timestamp) {
 		this.timestamp = timestamp;
 	}
@@ -219,46 +246,56 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display duration")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final int getDuration() {
 		return duration;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
+	public final void setDuration(final int duration) {
+		this.duration = duration;
+	}
+
 	@ApiModelProperty(required = true, value = "true for image question")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final boolean isImageQuestion() {
 		return imageQuestion;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setImageQuestion(boolean imageQuestion) {
 		this.imageQuestion = imageQuestion;
 	}
 
-	public final void setDuration(final int duration) {
-		this.duration = duration;
-	}
-
 	@ApiModelProperty(required = true, value = "the peer instruction round no.")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getPiRound() {
 		return piRound;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPiRound(final int piRound) {
 		this.piRound = piRound;
 	}
 
 	@ApiModelProperty(required = true, value = "the peer instruction round end timestamp")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public long getPiRoundEndTime() {
 		return piRoundEndTime;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPiRoundEndTime(long piRoundEndTime) {
 		this.piRoundEndTime = piRoundEndTime;
 	}
 
 	@ApiModelProperty(required = true, value = "the peer instruction round start timestamp")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public long getPiRoundStartTime() {
 		return piRoundStartTime;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setPiRoundStartTime(long piRoundStartTime) {
 		this.piRoundStartTime = piRoundStartTime;
 	}
@@ -282,340 +319,395 @@ public class Question implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "used to display showStatistic")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isShowStatistic() {
 		return showStatistic;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setShowStatistic(final boolean showStatistic) {
 		this.showStatistic = showStatistic;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display cvIsColored")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean getCvIsColored() {
 		return cvIsColored;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setCvIsColored(boolean cvIsColored) {
 		this.cvIsColored = cvIsColored;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display showAnswer")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isShowAnswer() {
 		return showAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setShowAnswer(final boolean showAnswer) {
 		this.showAnswer = showAnswer;
 	}
 
 	@ApiModelProperty(required = true, value = "used to display abstention")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isAbstention() {
 		return abstention;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setAbstention(final boolean abstention) {
 		this.abstention = abstention;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isIgnoreCaseSensitive() {
 		return ignoreCaseSensitive;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setIgnoreCaseSensitive(final boolean ignoreCaseSensitive) {
 		this.ignoreCaseSensitive = ignoreCaseSensitive;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isIgnoreWhitespaces() {
 		return ignoreWhitespaces;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setIgnoreWhitespaces(final boolean ignoreWhitespaces) {
 		this.ignoreWhitespaces = ignoreWhitespaces;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isIgnorePunctuation() {
 		return ignorePunctuation;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setIgnorePunctuation(final boolean ignorePunctuation) {
 		this.ignorePunctuation = ignorePunctuation;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isFixedAnswer() {
 		return this.fixedAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFixedAnswer(final boolean fixedAnswer) {
 		this.fixedAnswer = fixedAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isStrictMode() {
 		return this.strictMode;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setStrictMode(final boolean strictMode) {
 		this.strictMode = strictMode;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final int getRating() {
 		return this.rating;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setRating(final int rating) {
 		this.rating = rating;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final String getCorrectAnswer() {
 		return correctAnswer;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public final void setCorrectAnswer(final String correctAnswer) {
 		this.correctAnswer = correctAnswer;
 	}
 
-	@ApiModelProperty(required = true, value = "the couchDB ID")
-	public final String get_id() {
-		return _id;
-	}
-
-	public final void set_id(final String _id) {
-		this._id = _id;
-	}
-
-	public final String get_rev() {
-		return _rev;
-	}
-
-	public final void set_rev(final String _rev) {
-		this._rev = _rev;
-	}
-
 	@ApiModelProperty(required = true, value = "the image")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getImage() {
 		return image;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setImage(final String image) {
 		this.image = image;
 	}
 
 	@ApiModelProperty(required = true, value = "the fcImage")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getFcImage() {
 		return fcImage;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setFcImage(final String fcImage) {
 		this.fcImage = fcImage;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid size")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridSize() {
 		return gridSize;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridSize(final int gridSize) {
 		this.gridSize = gridSize;
 	}
 
 	@ApiModelProperty(required = true, value = "the image X offset")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getOffsetX() {
 		return offsetX;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setOffsetX(final int offsetX) {
 		this.offsetX = offsetX;
 	}
 
 	@ApiModelProperty(required = true, value = "the image Y offset")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getOffsetY() {
 		return offsetY;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setOffsetY(final int offsetY) {
 		this.offsetY = offsetY;
 	}
 
 	@ApiModelProperty(required = true, value = "the image zoom level")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getZoomLvl() {
 		return zoomLvl;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setZoomLvl(final int zoomLvl) {
 		this.zoomLvl = zoomLvl;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid X offset")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridOffsetX() {
 		return gridOffsetX;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridOffsetX(int gridOffsetX) {
 		this.gridOffsetX = gridOffsetX;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid Y offset")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridOffsetY() {
 		return gridOffsetY;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridOffsetY(int gridOffsetY) {
 		this.gridOffsetY = gridOffsetY;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid zoom lvl")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridZoomLvl() {
 		return gridZoomLvl;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridZoomLvl(int gridZoomLvl) {
 		this.gridZoomLvl = gridZoomLvl;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid X size")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridSizeX() {
 		return gridSizeX;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridSizeX(int gridSizeX) {
 		this.gridSizeX = gridSizeX;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid Y size")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getGridSizeY() {
 		return gridSizeY;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridSizeY(int gridSizeY) {
 		this.gridSizeY = gridSizeY;
 	}
 
 	@ApiModelProperty(required = true, value = "true for hidden grid")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean getGridIsHidden() {
 		return gridIsHidden;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridIsHidden(boolean gridIsHidden) {
 		this.gridIsHidden = gridIsHidden;
 	}
 
 	@ApiModelProperty(required = true, value = "the image rotation")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getImgRotation() {
 		return imgRotation;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setImgRotation(int imgRotation) {
 		this.imgRotation = imgRotation;
 	}
 
 	@ApiModelProperty(required = true, value = "the toggled left fields")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean getToggleFieldsLeft() {
 		return toggleFieldsLeft;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setToggleFieldsLeft(boolean toggleFieldsLeft) {
 		this.toggleFieldsLeft = toggleFieldsLeft;
 	}
 
 	@ApiModelProperty(required = true, value = "the number of clickable fields")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getNumClickableFields() {
 		return numClickableFields;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setNumClickableFields(int numClickableFields) {
 		this.numClickableFields = numClickableFields;
 	}
 
 	@ApiModelProperty(required = true, value = "the threshold of correct answers")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getThresholdCorrectAnswers() {
 		return thresholdCorrectAnswers;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setThresholdCorrectAnswers(int thresholdCorrectAnswers) {
 		this.thresholdCorrectAnswers = thresholdCorrectAnswers;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid line color")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getGridLineColor() {
 		return gridLineColor;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridLineColor(String gridLineColor) {
 		this.gridLineColor = gridLineColor;
 	}
 
 	@ApiModelProperty(required = true, value = "the number of dots")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getNumberOfDots() {
 		return numberOfDots;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setNumberOfDots(int numberOfDots) {
 		this.numberOfDots = numberOfDots;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid type")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getGridType() {
 		return gridType;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridType(String gridType) {
 		this.gridType = gridType;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setScaleFactor(String scaleFactor) {
 		this.scaleFactor = scaleFactor;
 	}
 
 	@ApiModelProperty(required = true, value = "the image scale factor")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getScaleFactor() {
 		return this.scaleFactor;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setGridScaleFactor(String scaleFactor) {
 		this.gridScaleFactor = scaleFactor;
 	}
 
 	@ApiModelProperty(required = true, value = "the grid scale factor")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getGridScaleFactor() {
 		return this.gridScaleFactor;
 	}
 
 	@ApiModelProperty(required = true, value = "true for a question that can be answered via text")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isTextAnswerEnabled() {
 		return this.textAnswerEnabled;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setTextAnswerEnabled(boolean textAnswerEnabled) {
 		this.textAnswerEnabled = textAnswerEnabled;
 	}
 
 	@ApiModelProperty(required = true, value = "true for disabled voting")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isVotingDisabled() {
 		return votingDisabled;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setVotingDisabled(boolean votingDisabled) {
 		this.votingDisabled = votingDisabled;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getHint() {
 		return hint;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setHint(String hint) {
 		this.hint = hint;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getSolution() {
 		return solution;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setSolution(String solution) {
 		this.solution = solution;
 	}
 
 	@Override
 	public final String toString() {
-		return "Question type '" + type + "': " + subject + ";\n" + text + possibleAnswers;
+		return "Content type '" + questionType + "': " + subject + ";\n" + text + possibleAnswers;
 	}
 
 	@Override
@@ -623,7 +715,7 @@ public class Question implements Serializable {
 		// auto generated!
 		final int prime = 31;
 		int result = 1;
-		result = prime * result + ((_id == null) ? 0 : _id.hashCode());
+		result = prime * result + ((id == null) ? 0 : id.hashCode());
 		return result;
 	}
 
@@ -639,12 +731,12 @@ public class Question implements Serializable {
 		if (getClass() != obj.getClass()) {
 			return false;
 		}
-		Question other = (Question) obj;
-		if (_id == null) {
-			if (other._id != null) {
+		Content other = (Content) obj;
+		if (id == null) {
+			if (other.id != null) {
 				return false;
 			}
-		} else if (!_id.equals(other._id)) {
+		} else if (!id.equals(other.id)) {
 			return false;
 		}
 		return true;
diff --git a/src/main/java/de/thm/arsnova/entities/PossibleAnswer.java b/src/main/java/de/thm/arsnova/entities/PossibleAnswer.java
index 0b7fa7983..f3bfb039a 100644
--- a/src/main/java/de/thm/arsnova/entities/PossibleAnswer.java
+++ b/src/main/java/de/thm/arsnova/entities/PossibleAnswer.java
@@ -17,6 +17,8 @@
  */
 package de.thm.arsnova.entities;
 
+import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 
@@ -43,28 +45,34 @@ public class PossibleAnswer implements Serializable {
 	}
 
 	@ApiModelProperty(required = true, value = "the text")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public String getText() {
 		return text;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setText(String text) {
 		this.text = text;
 	}
 
 	@ApiModelProperty(required = true, value = "true for a correct answer")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public boolean isCorrect() {
 		return correct;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setCorrect(boolean correct) {
 		this.correct = correct;
 	}
 
 	@ApiModelProperty(required = true, value = "the value")
+	@JsonView({View.Persistence.class, View.Public.class})
 	public int getValue() {
 		return value;
 	}
 
+	@JsonView({View.Persistence.class, View.Public.class})
 	public void setValue(int value) {
 		this.value = value;
 	}
diff --git a/src/main/java/de/thm/arsnova/entities/serialization/CouchDbTypeFieldConverter.java b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbTypeFieldConverter.java
index 8622e2f1a..7141f02f4 100644
--- a/src/main/java/de/thm/arsnova/entities/serialization/CouchDbTypeFieldConverter.java
+++ b/src/main/java/de/thm/arsnova/entities/serialization/CouchDbTypeFieldConverter.java
@@ -25,6 +25,7 @@ import de.thm.arsnova.entities.DbUser;
 import de.thm.arsnova.entities.Entity;
 import de.thm.arsnova.entities.LogEntry;
 import de.thm.arsnova.entities.Motd;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
@@ -39,6 +40,7 @@ public class CouchDbTypeFieldConverter implements Converter<Class<? extends Enti
 		typeMapping.put(Motd.class, "motd");
 		typeMapping.put(Session.class, "session");
 		typeMapping.put(Comment.class, "interposed_question");
+		typeMapping.put(Content.class, "skill_question");
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/entities/transport/Answer.java b/src/main/java/de/thm/arsnova/entities/transport/Answer.java
index 5b6d24429..de074d3e1 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/Answer.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/Answer.java
@@ -19,7 +19,7 @@ package de.thm.arsnova.entities.transport;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonView;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.entities.serialization.View;
 import io.swagger.annotations.ApiModel;
@@ -126,28 +126,28 @@ public class Answer implements Serializable {
 		this.abstention = abstention;
 	}
 
-	public de.thm.arsnova.entities.Answer generateAnswerEntity(final User user, final Question question) {
+	public de.thm.arsnova.entities.Answer generateAnswerEntity(final User user, final Content content) {
 		// rewrite all fields so that no manipulated data gets written
 		// only answerText, answerSubject, and abstention are allowed
 		de.thm.arsnova.entities.Answer theAnswer = new de.thm.arsnova.entities.Answer();
 		theAnswer.setAnswerSubject(this.getAnswerSubject());
 		theAnswer.setAnswerText(this.getAnswerText());
 		theAnswer.setAnswerTextRaw(this.getAnswerTextRaw());
-		theAnswer.setSessionId(question.getSessionId());
+		theAnswer.setSessionId(content.getSessionId());
 		theAnswer.setUser(user.getUsername());
-		theAnswer.setQuestionId(question.get_id());
+		theAnswer.setQuestionId(content.getId());
 		theAnswer.setTimestamp(new Date().getTime());
-		theAnswer.setQuestionVariant(question.getQuestionVariant());
+		theAnswer.setQuestionVariant(content.getQuestionVariant());
 		theAnswer.setAbstention(this.isAbstention());
 		// calculate learning progress value after all properties are set
-		theAnswer.setQuestionValue(question.calculateValue(theAnswer));
+		theAnswer.setQuestionValue(content.calculateValue(theAnswer));
 		theAnswer.setAnswerImage(this.getAnswerImage());
 		theAnswer.setSuccessfulFreeTextAnswer(this.isSuccessfulFreeTextAnswer());
 
-		if ("freetext".equals(question.getQuestionType())) {
+		if ("freetext".equals(content.getQuestionType())) {
 			theAnswer.setPiRound(0);
 		} else {
-			theAnswer.setPiRound(question.getPiRound());
+			theAnswer.setPiRound(content.getPiRound());
 		}
 
 		return theAnswer;
diff --git a/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java b/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java
index 92b8b4950..694de60fc 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/AnswerQueueElement.java
@@ -18,7 +18,7 @@
 package de.thm.arsnova.entities.transport;
 
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 
@@ -30,15 +30,15 @@ public class AnswerQueueElement {
 
 	private final Session session;
 
-	private final Question question;
+	private final Content content;
 
 	private final Answer answer;
 
 	private final User user;
 
-	public AnswerQueueElement(Session session, Question question, Answer answer, User user) {
+	public AnswerQueueElement(Session session, Content content, Answer answer, User user) {
 		this.session = session;
-		this.question = question;
+		this.content = content;
 		this.answer = answer;
 		this.user = user;
 	}
@@ -47,8 +47,8 @@ public class AnswerQueueElement {
 		return session;
 	}
 
-	public Question getQuestion() {
-		return question;
+	public Content getQuestion() {
+		return content;
 	}
 
 	public Answer getAnswer() {
diff --git a/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java b/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java
index 10c46dac2..1ef60232f 100644
--- a/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java
+++ b/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java
@@ -18,8 +18,8 @@
 package de.thm.arsnova.entities.transport;
 
 import com.fasterxml.jackson.annotation.JsonView;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Motd;
-import de.thm.arsnova.entities.Question;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.SessionFeature;
 import de.thm.arsnova.entities.SessionInfo;
@@ -40,7 +40,7 @@ public class ImportExportSession {
 
 	private ImportExportSesssion session;
 
-	private List<ImportExportQuestion> questions;
+	private List<ImportExportContent> questions;
 
 	private List<Comment> feedbackQuestions;
 
@@ -69,11 +69,11 @@ public class ImportExportSession {
 
 	@ApiModelProperty(required = true, value = "used to display questions")
 	@JsonView(View.Public.class)
-	public List<ImportExportQuestion> getQuestions() {
+	public List<ImportExportContent> getQuestions() {
 		return questions;
 	}
 
-	public void setQuestions(List<ImportExportQuestion> questions) {
+	public void setQuestions(List<ImportExportContent> questions) {
 		this.questions = questions;
 	}
 
@@ -126,8 +126,8 @@ public class ImportExportSession {
 		session = iesession;
 	}
 
-	public void addQuestionWithAnswers(Question q, List<Answer> aL) {
-		ImportExportQuestion ieq = new ImportExportQuestion(q);
+	public void addQuestionWithAnswers(Content q, List<Answer> aL) {
+		ImportExportContent ieq = new ImportExportContent(q);
 		ieq.setAnswers(aL);
 		questions.add(ieq);
 	}
@@ -158,16 +158,15 @@ public class ImportExportSession {
 		return s;
 	}
 
-	public static class ImportExportQuestion extends Question {
+	public static class ImportExportContent extends Content {
 
 		private List<Answer> answers;
 
-		public ImportExportQuestion() {
+		public ImportExportContent() {
 
 		}
 
-		public ImportExportQuestion(Question q) {
-			setType(q.getType());
+		public ImportExportContent(Content q) {
 			setQuestionType(q.getQuestionType());
 			setQuestionVariant(q.getQuestionVariant());
 			setSubject(q.getSubject());
diff --git a/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java b/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
index 8d400e928..55970e207 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteAnswerEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
@@ -27,11 +27,11 @@ public class DeleteAnswerEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public DeleteAnswerEvent(Object source, Session session, Question question) {
+	public DeleteAnswerEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
 	@Override
@@ -39,7 +39,7 @@ public class DeleteAnswerEvent extends SessionEvent {
 		visitor.visit(this);
 	}
 
-	public Question getQuestion() {
-		return question;
+	public Content getQuestion() {
+		return content;
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java b/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
index 2f3cfd71b..0204ee5b7 100644
--- a/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/DeleteQuestionEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a question is deleted.
+ * Fires whenever a content is deleted.
  */
 public class DeleteQuestionEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public DeleteQuestionEvent(Object source, Session session, Question question) {
+	public DeleteQuestionEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
-	public Question getQuestion() {
-		return this.question;
+	public Content getQuestion() {
+		return this.content;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java b/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
index 389b3ea97..8371c8b01 100644
--- a/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockQuestionEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a question is disabled, i.e., it is hidden from students.
+ * Fires whenever a content is disabled, i.e., it is hidden from students.
  */
 public class LockQuestionEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public LockQuestionEvent(Object source, Session session, Question question) {
+	public LockQuestionEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
-	public Question getQuestion() {
-		return this.question;
+	public Content getQuestion() {
+		return this.content;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java b/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
index 9ee93fa3b..2981f4e18 100644
--- a/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockQuestionsEvent.java
@@ -17,27 +17,27 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.List;
 
 /**
- * Fires whenever a set of questions are disabled, i.e., they are hidden from students.
+ * Fires whenever a set of contents are disabled, i.e., they are hidden from students.
  */
 public class LockQuestionsEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private List<Question> questions;
+	private List<Content> contents;
 
-	public LockQuestionsEvent(Object source, Session session, List<Question> questions) {
+	public LockQuestionsEvent(Object source, Session session, List<Content> contents) {
 		super(source, session);
-		this.questions = questions;
+		this.contents = contents;
 	}
 
-	public List<Question> getQuestions() {
-		return this.questions;
+	public List<Content> getQuestions() {
+		return this.contents;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/LockVoteEvent.java b/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
index b33922f4f..e3e9f000f 100644
--- a/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockVoteEvent.java
@@ -17,36 +17,36 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Fires whenever voting on a question is disabled.
+ * Fires whenever voting on a content is disabled.
  */
 public class LockVoteEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public LockVoteEvent(Object source, Session session, Question question) {
+	public LockVoteEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
 	public String getQuestionId() {
-		return this.question.get_id();
+		return this.content.getId();
 	}
 
 	public String getQuestionVariant() {
-		return this.question.getQuestionVariant();
+		return this.content.getQuestionVariant();
 	}
 
 	public Boolean getVotingDisabled() {
-		return this.question.isVotingDisabled();
+		return this.content.isVotingDisabled();
 	}
 
 	public Map<String, Object> getVotingAdmission() {
diff --git a/src/main/java/de/thm/arsnova/events/LockVotesEvent.java b/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
index fb5acd7c1..6f8043c18 100644
--- a/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
+++ b/src/main/java/de/thm/arsnova/events/LockVotesEvent.java
@@ -17,27 +17,27 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.List;
 
 /**
- * Fires whenever voting of multiple questions is disabled.
+ * Fires whenever voting of multiple contents is disabled.
  */
 public class LockVotesEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private List<Question> questions;
+	private List<Content> contents;
 
-	public LockVotesEvent(Object source, Session session, List<Question> questions) {
+	public LockVotesEvent(Object source, Session session, List<Content> contents) {
 		super(source, session);
-		this.questions = questions;
+		this.contents = contents;
 	}
 
-	public List<Question> getQuestions() {
-		return this.questions;
+	public List<Content> getQuestions() {
+		return this.contents;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java b/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
index c2fd68a52..9172a5b5a 100644
--- a/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewAnswerEvent.java
@@ -18,7 +18,7 @@
 package de.thm.arsnova.events;
 
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 
@@ -33,13 +33,13 @@ public class NewAnswerEvent extends SessionEvent {
 
 	private final User user;
 
-	private final Question question;
+	private final Content content;
 
-	public NewAnswerEvent(Object source, Session session, Answer answer, User user, Question question) {
+	public NewAnswerEvent(Object source, Session session, Answer answer, User user, Content content) {
 		super(source, session);
 		this.answer = answer;
 		this.user = user;
-		this.question = question;
+		this.content = content;
 	}
 
 	@Override
@@ -55,7 +55,7 @@ public class NewAnswerEvent extends SessionEvent {
 		return user;
 	}
 
-	public Question getQuestion() {
-		return question;
+	public Content getContent() {
+		return content;
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java b/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
index 6ac4be12e..1a9e1616e 100644
--- a/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/NewQuestionEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a new question is added.
+ * Fires whenever a new content is added.
  */
 public class NewQuestionEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public NewQuestionEvent(Object source, Session session, Question question) {
+	public NewQuestionEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
-	public Question getQuestion() {
-		return question;
+	public Content getQuestion() {
+		return content;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
index 86ff69ae3..64b51aea8 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundCancelEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
@@ -27,8 +27,8 @@ public class PiRoundCancelEvent extends PiRoundEndEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	public PiRoundCancelEvent(Object source, Session session, Question question) {
-		super(source, session, question);
+	public PiRoundCancelEvent(Object source, Session session, Content content) {
+		super(source, session, content);
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
index 6fdc587e3..dfaed2bb4 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
@@ -36,13 +36,13 @@ public class PiRoundDelayedStartEvent extends SessionEvent {
 	private final String questionVariant;
 	private int piRound;
 
-	public PiRoundDelayedStartEvent(Object source, Session session, Question question) {
+	public PiRoundDelayedStartEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.questionId = question.get_id();
-		this.startTime = question.getPiRoundStartTime();
-		this.endTime = question.getPiRoundEndTime();
-		this.questionVariant = question.getQuestionVariant();
-		this.piRound = question.getPiRound();
+		this.questionId = content.getId();
+		this.startTime = content.getPiRoundStartTime();
+		this.endTime = content.getPiRoundEndTime();
+		this.questionVariant = content.getQuestionVariant();
+		this.piRound = content.getPiRound();
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
index b87d12590..335345185 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
@@ -33,10 +33,10 @@ public class PiRoundEndEvent extends SessionEvent {
 	private final String questionId;
 	private final String questionVariant;
 
-	public PiRoundEndEvent(Object source, Session session, Question question) {
+	public PiRoundEndEvent(Object source, Session session, Content content) {
 		super(source, session);
-		questionId = question.get_id();
-		questionVariant = question.getQuestionVariant();
+		questionId = content.getId();
+		questionVariant = content.getQuestionVariant();
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
index 429aeb274..96bd50df0 100644
--- a/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
+++ b/src/main/java/de/thm/arsnova/events/PiRoundResetEvent.java
@@ -17,7 +17,7 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
@@ -33,10 +33,10 @@ public class PiRoundResetEvent extends SessionEvent {
 	private final String questionId;
 	private final String questionVariant;
 
-	public PiRoundResetEvent(Object source, Session session, Question question) {
+	public PiRoundResetEvent(Object source, Session session, Content content) {
 		super(source, session);
-		questionId = question.get_id();
-		questionVariant = question.getQuestionVariant();
+		questionId = content.getId();
+		questionVariant = content.getQuestionVariant();
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java b/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
index d04c709e8..1d8e24c61 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockQuestionEvent.java
@@ -17,25 +17,25 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 /**
- * Fires whenever a question is enabled, i.e., it becomes visible to students.
+ * Fires whenever a content is enabled, i.e., it becomes visible to students.
  */
 public class UnlockQuestionEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public UnlockQuestionEvent(Object source, Session session, Question question) {
+	public UnlockQuestionEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
-	public Question getQuestion() {
-		return this.question;
+	public Content getQuestion() {
+		return this.content;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java b/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
index b760b4ce5..153b702a6 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockQuestionsEvent.java
@@ -17,27 +17,27 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.List;
 
 /**
- * Fires whenever a set of questions are enabled, i.e., they become visible to students.
+ * Fires whenever a set of contents are enabled, i.e., they become visible to students.
  */
 public class UnlockQuestionsEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private List<Question> questions;
+	private List<Content> contents;
 
-	public UnlockQuestionsEvent(Object source, Session session, List<Question> questions) {
+	public UnlockQuestionsEvent(Object source, Session session, List<Content> contents) {
 		super(source, session);
-		this.questions = questions;
+		this.contents = contents;
 	}
 
-	public List<Question> getQuestions() {
-		return this.questions;
+	public List<Content> getQuestions() {
+		return this.contents;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java b/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
index 69a1f4571..00bf47a4d 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockVoteEvent.java
@@ -17,36 +17,36 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * Fires whenever voting on a question is enabled.
+ * Fires whenever voting on a content is enabled.
  */
 public class UnlockVoteEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private final Question question;
+	private final Content content;
 
-	public UnlockVoteEvent(Object source, Session session, Question question) {
+	public UnlockVoteEvent(Object source, Session session, Content content) {
 		super(source, session);
-		this.question = question;
+		this.content = content;
 	}
 
 	public String getQuestionId() {
-		return this.question.get_id();
+		return this.content.getId();
 	}
 
 	public String getQuestionVariant() {
-		return this.question.getQuestionVariant();
+		return this.content.getQuestionVariant();
 	}
 
 	public Boolean getVotingDisabled() {
-		return this.question.isVotingDisabled();
+		return this.content.isVotingDisabled();
 	}
 
 	public Map<String, Object> getVotingAdmission() {
diff --git a/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java b/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
index c489aefcf..2dd3d4671 100644
--- a/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
+++ b/src/main/java/de/thm/arsnova/events/UnlockVotesEvent.java
@@ -17,27 +17,27 @@
  */
 package de.thm.arsnova.events;
 
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 
 import java.util.List;
 
 /**
- * Fires whenever voting of multiple questions is enabled.
+ * Fires whenever voting of multiple contents is enabled.
  */
 public class UnlockVotesEvent extends SessionEvent {
 
 	private static final long serialVersionUID = 1L;
 
-	private List<Question> questions;
+	private List<Content> contents;
 
-	public UnlockVotesEvent(Object source, Session session, List<Question> questions) {
+	public UnlockVotesEvent(Object source, Session session, List<Content> contents) {
 		super(source, session);
-		this.questions = questions;
+		this.contents = contents;
 	}
 
-	public List<Question> getQuestions() {
-		return this.questions;
+	public List<Content> getQuestions() {
+		return this.contents;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/persistance/ContentRepository.java b/src/main/java/de/thm/arsnova/persistance/ContentRepository.java
new file mode 100644
index 000000000..9c49aa789
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/ContentRepository.java
@@ -0,0 +1,44 @@
+package de.thm.arsnova.persistance;
+
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+
+import java.util.List;
+
+public interface ContentRepository {
+	List<Content> getQuestions(Object... keys);
+	Content getQuestion(String id);
+	Content saveQuestion(Session session, Content content);
+	List<Content> getSkillQuestionsForUsers(Session session);
+	List<Content> getSkillQuestionsForTeachers(Session session);
+	int getSkillQuestionCount(Session session);
+	List<String> getQuestionIds(Session session, User user);
+	int deleteQuestionWithAnswers(Content content);
+	int[] deleteAllQuestionsWithAnswers(Session session);
+	List<String> getUnAnsweredQuestionIds(Session session, User user);
+	Content updateQuestion(Content content);
+	List<Content> getLectureQuestionsForUsers(Session session);
+	List<Content> getLectureQuestionsForTeachers(Session session);
+	List<Content> getFlashcardsForUsers(Session session);
+	List<Content> getFlashcardsForTeachers(Session session);
+	List<Content> getPreparationQuestionsForUsers(Session session);
+	List<Content> getPreparationQuestionsForTeachers(Session session);
+	List<Content> getAllSkillQuestions(Session session);
+	int getLectureQuestionCount(Session session);
+	int getFlashcardCount(Session session);
+	int getPreparationQuestionCount(Session session);
+	void publishQuestions(Session session, boolean publish, List<Content> contents);
+	List<Content> publishAllQuestions(Session session, boolean publish);
+	List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject);
+	List<Content> getQuestionsByIds(List<String> ids, Session session);
+	void resetQuestionsRoundState(Session session, List<Content> contents);
+	void setVotingAdmissions(Session session, boolean disableVoting, List<Content> contents);
+	List<Content> setVotingAdmissionForAllQuestions(Session session, boolean disableVoting);
+	int[] deleteAllLectureQuestionsWithAnswers(Session session);
+	int[] deleteAllFlashcardsWithAnswers(Session session);
+	int[] deleteAllPreparationQuestionsWithAnswers(Session session);
+	List<String> getSubjects(Session session, String questionVariant);
+	List<String> getUnAnsweredLectureQuestionIds(Session session, User user);
+	List<String> getUnAnsweredPreparationQuestionIds(Session session, User user);
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbContentRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbContentRepository.java
new file mode 100644
index 000000000..ba83397e9
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbContentRepository.java
@@ -0,0 +1,514 @@
+package de.thm.arsnova.persistance.couchdb;
+
+import de.thm.arsnova.dao.IDatabaseDao;
+import de.thm.arsnova.entities.Content;
+import de.thm.arsnova.entities.Session;
+import de.thm.arsnova.entities.User;
+import de.thm.arsnova.persistance.ContentRepository;
+import de.thm.arsnova.persistance.LogEntryRepository;
+import org.ektorp.ComplexKey;
+import org.ektorp.CouchDbConnector;
+import org.ektorp.DbAccessException;
+import org.ektorp.DocumentNotFoundException;
+import org.ektorp.UpdateConflictException;
+import org.ektorp.ViewQuery;
+import org.ektorp.ViewResult;
+import org.ektorp.support.CouchDbRepositorySupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.Caching;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class CouchDbContentRepository extends CouchDbRepositorySupport<Content> implements ContentRepository {
+	private static final Logger logger = LoggerFactory.getLogger(CouchDbContentRepository.class);
+
+	@Autowired
+	private LogEntryRepository dbLogger;
+
+	@Autowired
+	private IDatabaseDao databaseDao;
+
+	public CouchDbContentRepository(Class<Content> type, CouchDbConnector db, boolean createIfNotExists) {
+		super(type, db, createIfNotExists);
+	}
+
+	@Cacheable("skillquestions")
+	@Override
+	public List<Content> getSkillQuestionsForUsers(final Session session) {
+		final List<Content> contents = new ArrayList<>();
+		final List<Content> questions1 = getQuestions(session.getId(), "lecture", true);
+		final List<Content> questions2 = getQuestions(session.getId(), "preparation", true);
+		final List<Content> questions3 = getQuestions(session.getId(), "flashcard", true);
+		contents.addAll(questions1);
+		contents.addAll(questions2);
+		contents.addAll(questions3);
+
+		return contents;
+	}
+
+	@Cacheable("skillquestions")
+	@Override
+	public List<Content> getSkillQuestionsForTeachers(final Session session) {
+		return getQuestions(new Object[] {session.getId()}, session);
+	}
+
+	@Override
+	public int getSkillQuestionCount(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId()))
+				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject())));
+
+		return result.getSize();
+	}
+
+	@Caching(evict = {@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session", condition = "#content.getQuestionVariant().equals('lecture')"),
+			@CacheEvict(value = "preparationquestions", key = "#session", condition = "#content.getQuestionVariant().equals('preparation')"),
+			@CacheEvict(value = "flashcardquestions", key = "#session", condition = "#content.getQuestionVariant().equals('flashcard')") },
+			put = {@CachePut(value = "questions", key = "#content.id")})
+	@Override
+	public Content saveQuestion(final Session session, final Content content) {
+		content.setSessionId(session.getId());
+		try {
+			db.create(content);
+
+			return content;
+		} catch (final IllegalArgumentException e) {
+			logger.error("Could not save content {}.", content, e);
+		}
+
+		return null;
+	}
+
+	/* TODO: Only evict cache entry for the content's session. This requires some refactoring. */
+	@Caching(evict = {@CacheEvict(value = "skillquestions", allEntries = true),
+			@CacheEvict(value = "lecturequestions", allEntries = true, condition = "#content.getQuestionVariant().equals('lecture')"),
+			@CacheEvict(value = "preparationquestions", allEntries = true, condition = "#content.getQuestionVariant().equals('preparation')"),
+			@CacheEvict(value = "flashcardquestions", allEntries = true, condition = "#content.getQuestionVariant().equals('flashcard')") },
+			put = {@CachePut(value = "questions", key = "#content.id")})
+	@Override
+	public Content updateQuestion(final Content content) {
+		try {
+			/* TODO: Make sure that sessionId is valid before so the content does not need to be retrieved. */
+			final Content oldContent = get(content.getId());
+			content.setId(oldContent.getId());
+			content.setRevision(oldContent.getRevision());
+			content.updateRoundManagementState();
+			update(content);
+
+			return content;
+		} catch (final UpdateConflictException e) {
+			logger.error("Could not update content {}.", content, e);
+		}
+
+		return null;
+	}
+
+	@Cacheable("questions")
+	@Override
+	public Content getQuestion(final String id) {
+		try {
+			final Content content = get(id);
+			content.updateRoundManagementState();
+			//content.setSessionKeyword(sessionRepository.getSessionFromId(content.getSessionId()).getKeyword());
+
+			return content;
+		} catch (final DocumentNotFoundException e) {
+			logger.error("Could not get question {}.", id, e);
+		}
+
+		return null;
+	}
+
+	@Override
+	public List<String> getQuestionIds(final Session session, final User user) {
+		return collectQuestionIds(db.queryView(createQuery("by_sessionid_variant_active").key(session.getId())));
+	}
+
+	/* TODO: Only evict cache entry for the content's session. This requires some refactoring. */
+	@Caching(evict = { @CacheEvict(value = "questions", key = "#content.id"),
+			@CacheEvict(value = "skillquestions", allEntries = true),
+			@CacheEvict(value = "lecturequestions", allEntries = true, condition = "#content.getQuestionVariant().equals('lecture')"),
+			@CacheEvict(value = "preparationquestions", allEntries = true, condition = "#content.getQuestionVariant().equals('preparation')"),
+			@CacheEvict(value = "flashcardquestions", allEntries = true, condition = "#content.getQuestionVariant().equals('flashcard')") })
+	@Override
+	public int deleteQuestionWithAnswers(final Content content) {
+		try {
+			int count = databaseDao.deleteAnswers(content);
+			db.delete(content);
+			dbLogger.log("delete", "type", "content", "answerCount", count);
+
+			return count;
+		} catch (final IllegalArgumentException e) {
+			logger.error("Could not delete content {}.", content.getId(), e);
+		}
+
+		return 0;
+	}
+
+	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
+			@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session"),
+			@CacheEvict(value = "preparationquestions", key = "#session"),
+			@CacheEvict(value = "flashcardquestions", key = "#session") })
+	@Override
+	public int[] deleteAllQuestionsWithAnswers(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId()))
+				.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject()))
+				.reduce(false));
+
+		return deleteAllQuestionDocumentsWithAnswers(result);
+	}
+
+	private int[] deleteAllQuestionDocumentsWithAnswers(final ViewResult viewResult) {
+		List<Content> contents = new ArrayList<>();
+		for (final ViewResult.Row row : viewResult.getRows()) {
+			final Content q = new Content();
+			q.setId(row.getId());
+			q.setRevision(row.getValueAsNode().get("_rev").asText());
+			contents.add(q);
+		}
+
+		int[] count = databaseDao.deleteAllAnswersWithQuestions(contents);
+		dbLogger.log("delete", "type", "question", "questionCount", count[0]);
+		dbLogger.log("delete", "type", "answer", "answerCount", count[1]);
+
+		return count;
+	}
+
+	@Override
+	public List<String> getUnAnsweredQuestionIds(final Session session, final User user) {
+		final ViewResult result = db.queryView(createQuery("questionid_by_user_sessionid_variant")
+				.designDocId("_design/answer")
+				.startKey(ComplexKey.of(user.getUsername(), session.getId()))
+				.endKey(ComplexKey.of(user.getUsername(), session.getId(), ComplexKey.emptyObject())));
+		List<String> answeredIds = new ArrayList<>();
+		for (ViewResult.Row row : result.getRows()) {
+			answeredIds.add(row.getId());
+		}
+		return collectUnansweredQuestionIds(getQuestionIds(session, user), answeredIds);
+	}
+
+	@Override
+	public List<String> getUnAnsweredLectureQuestionIds(final Session session, final User user) {
+		final ViewResult result = db.queryView(createQuery("questionid_piround_by_user_sessionid_variant")
+				.designDocId("_design/answer")
+				.key(ComplexKey.of(user.getUsername(), session.getId(), "lecture")));
+		Map<String, Integer> answeredQuestions = new HashMap<>();
+		for (ViewResult.Row row : result.getRows()) {
+			answeredQuestions.put(row.getId(), row.getKeyAsNode().get(2).asInt());
+		}
+
+		return collectUnansweredQuestionIdsByPiRound(getLectureQuestionsForUsers(session), answeredQuestions);
+	}
+
+	@Override
+	public List<String> getUnAnsweredPreparationQuestionIds(final Session session, final User user) {
+		final ViewResult result = db.queryView(createQuery("questionid_piround_by_user_sessionid_variant")
+				.designDocId("_design/answer")
+				.key(ComplexKey.of(user.getUsername(), session.getId(), "preparation")));
+		Map<String, Integer> answeredQuestions = new HashMap<>();
+		for (ViewResult.Row row : result.getRows()) {
+			answeredQuestions.put(row.getId(), row.getKeyAsNode().get(2).asInt());
+		}
+
+		return collectUnansweredQuestionIdsByPiRound(getPreparationQuestionsForUsers(session), answeredQuestions);
+	}
+
+	@Cacheable("lecturequestions")
+	@Override
+	public List<Content> getLectureQuestionsForUsers(final Session session) {
+		return getQuestions(session.getId(), "lecture", true);
+	}
+
+	@Override
+	public List<Content> getLectureQuestionsForTeachers(final Session session) {
+		return getQuestions(session.getId(), "lecture");
+	}
+
+	@Cacheable("flashcardquestions")
+	@Override
+	public List<Content> getFlashcardsForUsers(final Session session) {
+		return getQuestions(session.getId(), "flashcard", true);
+	}
+
+	@Override
+	public List<Content> getFlashcardsForTeachers(final Session session) {
+		return getQuestions(session.getId(), "flashcard");
+	}
+
+	@Cacheable("preparationquestions")
+	@Override
+	public List<Content> getPreparationQuestionsForUsers(final Session session) {
+		return getQuestions(session.getId(), "preparation", true);
+	}
+
+	@Override
+	public List<Content> getPreparationQuestionsForTeachers(final Session session) {
+		return getQuestions(session.getId(), "preparation");
+	}
+
+	@Override
+	public List<Content> getAllSkillQuestions(final Session session) {
+		return getQuestions(session.getId());
+	}
+
+	@Override
+	public List<Content> getQuestions(final Object... keys) {
+		Object[] endKeys = Arrays.copyOf(keys, keys.length + 1);
+		endKeys[keys.length] = ComplexKey.emptyObject();
+		final List<Content> contents = db.queryView(createQuery("by_sessionid_variant_active")
+						.includeDocs(true)
+						.reduce(false)
+						.startKey(ComplexKey.of(keys))
+						.endKey(ComplexKey.of(endKeys)),
+				Content.class);
+		for (Content content : contents) {
+			content.updateRoundManagementState();
+			//content.setSessionKeyword(session.getKeyword());
+		}
+
+		return contents;
+	}
+
+	@Override
+	public int getLectureQuestionCount(final Session session) {
+		/* TODO: reduce code duplication */
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "lecture"))
+				.endKey(ComplexKey.of(session.getId(), "lecture", ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	@Override
+	public int getFlashcardCount(final Session session) {
+		/* TODO: reduce code duplication */
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "flashcard"))
+				.endKey(ComplexKey.of(session.getId(), "flashcard", ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	@Override
+	public int getPreparationQuestionCount(final Session session) {
+		/* TODO: reduce code duplication */
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "preparation"))
+				.endKey(ComplexKey.of(session.getId(), "preparation", ComplexKey.emptyObject())));
+
+		return result.isEmpty() ? 0 : result.getRows().get(0).getValueAsInt();
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
+			@CacheEvict("skillquestions"),
+			@CacheEvict("lecturequestions"),
+			@CacheEvict(value = "answers", allEntries = true)})
+	@Override
+	public int[] deleteAllLectureQuestionsWithAnswers(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "lecture"))
+				.endKey(ComplexKey.of(session.getId(), "lecture", ComplexKey.emptyObject()))
+				.reduce(false));
+
+		return deleteAllQuestionDocumentsWithAnswers(result);
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
+			@CacheEvict("skillquestions"),
+			@CacheEvict("flashcardquestions"),
+			@CacheEvict(value = "answers", allEntries = true)})
+	@Override
+	public int[] deleteAllFlashcardsWithAnswers(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "flashcard"))
+				.endKey(ComplexKey.of(session.getId(), "flashcard", ComplexKey.emptyObject()))
+				.reduce(false));
+
+		return deleteAllQuestionDocumentsWithAnswers(result);
+	}
+
+	/* TODO: Only evict cache entry for the answer's question. This requires some refactoring. */
+	@Caching(evict = { @CacheEvict(value = "questions", allEntries = true),
+			@CacheEvict("skillquestions"),
+			@CacheEvict("preparationquestions"),
+			@CacheEvict(value = "answers", allEntries = true)})
+	@Override
+	public int[] deleteAllPreparationQuestionsWithAnswers(final Session session) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), "preparation"))
+				.endKey(ComplexKey.of(session.getId(), "preparation", ComplexKey.emptyObject()))
+				.reduce(false));
+
+		return deleteAllQuestionDocumentsWithAnswers(result);
+	}
+
+	private List<String> collectUnansweredQuestionIds(
+			final List<String> questions,
+			final List<String> answeredQuestions
+	) {
+		final List<String> unanswered = new ArrayList<>();
+		for (final String questionId : questions) {
+			if (!answeredQuestions.contains(questionId)) {
+				unanswered.add(questionId);
+			}
+		}
+		return unanswered;
+	}
+
+	private List<String> collectUnansweredQuestionIdsByPiRound(
+			final List<Content> contents,
+			final Map<String, Integer> answeredQuestions
+	) {
+		final List<String> unanswered = new ArrayList<>();
+
+		for (final Content content : contents) {
+			if (!"slide".equals(content.getQuestionType()) && (!answeredQuestions.containsKey(content.getId())
+					|| (answeredQuestions.containsKey(content.getId()) && answeredQuestions.get(content.getId()) != content.getPiRound()))) {
+				unanswered.add(content.getId());
+			}
+		}
+
+		return unanswered;
+	}
+
+	private List<String> collectQuestionIds(final ViewResult viewResult) {
+		final List<String> ids = new ArrayList<>();
+		for (final ViewResult.Row row : viewResult.getRows()) {
+			ids.add(row.getId());
+		}
+		return ids;
+	}
+
+	@Override
+	public List<Content> publishAllQuestions(final Session session, final boolean publish) {
+		final List<Content> contents = db.queryView(createQuery("by_sessionid_variant_active")
+						.startKey(ComplexKey.of(session.getId()))
+						.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject())),
+				Content.class);
+		/* FIXME: caching */
+		publishQuestions(session, publish, contents);
+
+		return contents;
+	}
+
+	@Caching(evict = { @CacheEvict(value = "contents", allEntries = true),
+			@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session"),
+			@CacheEvict(value = "preparationquestions", key = "#session"),
+			@CacheEvict(value = "flashcardquestions", key = "#session") })
+	@Override
+	public void publishQuestions(final Session session, final boolean publish, List<Content> contents) {
+		for (final Content content : contents) {
+			content.setActive(publish);
+		}
+		try {
+			db.executeBulk(contents);
+		} catch (final DbAccessException e) {
+			logger.error("Could not bulk publish all contents.", e);
+		}
+	}
+
+	@Override
+	public List<Content> setVotingAdmissionForAllQuestions(final Session session, final boolean disableVoting) {
+		final List<Content> contents = db.queryView(createQuery("by_sessionid_variant_active")
+						.startKey(ComplexKey.of(session.getId()))
+						.endKey(ComplexKey.of(session.getId(), ComplexKey.emptyObject()))
+						.includeDocs(true),
+				Content.class);
+		/* FIXME: caching */
+		setVotingAdmissions(session, disableVoting, contents);
+
+		return contents;
+	}
+
+	@Caching(evict = { @CacheEvict(value = "contents", allEntries = true),
+			@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session"),
+			@CacheEvict(value = "preparationquestions", key = "#session"),
+			@CacheEvict(value = "flashcardquestions", key = "#session") })
+	@Override
+	public void setVotingAdmissions(final Session session, final boolean disableVoting, List<Content> contents) {
+		for (final Content q : contents) {
+			if (!"flashcard".equals(q.getQuestionType())) {
+				q.setVotingDisabled(disableVoting);
+			}
+		}
+
+		try {
+			db.executeBulk(contents);
+		} catch (final DbAccessException e) {
+			logger.error("Could not bulk set voting admission for all contents.", e);
+		}
+	}
+
+	/* TODO: remove if this method is no longer used */
+	@Override
+	public List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), questionVariant, 1, subject))
+				.endKey(ComplexKey.of(session.getId(), questionVariant, 1, subject, ComplexKey.emptyObject())));
+
+		List<String> qids = new ArrayList<>();
+
+		for (final ViewResult.Row row : result.getRows()) {
+			final String s = row.getId();
+			qids.add(s);
+		}
+
+		return qids;
+	}
+
+	@Override
+	public List<Content> getQuestionsByIds(List<String> ids, final Session session) {
+		return db.queryView(new ViewQuery().allDocs().keys(ids).includeDocs(true), Content.class);
+	}
+
+	@Override
+	public List<String> getSubjects(Session session, String questionVariant) {
+		final ViewResult result = db.queryView(createQuery("by_sessionid_variant_active")
+				.startKey(ComplexKey.of(session.getId(), questionVariant))
+				.endKey(ComplexKey.of(session.getId(), questionVariant, ComplexKey.emptyObject())));
+
+		Set<String> uniqueSubjects = new HashSet<>();
+
+		for (final ViewResult.Row row : result.getRows()) {
+			uniqueSubjects.add(row.getKeyAsNode().get(3).asText());
+		}
+
+		return new ArrayList<>(uniqueSubjects);
+	}
+
+	@Caching(evict = { @CacheEvict(value = "contents", allEntries = true),
+			@CacheEvict(value = "skillquestions", key = "#session"),
+			@CacheEvict(value = "lecturequestions", key = "#session"),
+			@CacheEvict(value = "preparationquestions", key = "#session"),
+			@CacheEvict(value = "flashcardquestions", key = "#session") })
+	@Override
+	public void resetQuestionsRoundState(final Session session, List<Content> contents) {
+		for (final Content q : contents) {
+			q.setSessionId(session.getId());
+			q.resetQuestionState();
+		}
+		try {
+			db.executeBulk(contents);
+		} catch (final DbAccessException e) {
+			logger.error("Could not bulk reset all contents round state.", e);
+		}
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java
index e23d2ca29..0d0da5868 100644
--- a/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java
+++ b/src/main/java/de/thm/arsnova/persistance/couchdb/CouchDbSessionRepository.java
@@ -292,7 +292,7 @@ public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session>
 //		// We need to remember which answers belong to which question.
 //		// The answers need a questionId, so we first store the questions to get the IDs.
 //		// Then we update the answer objects and store them as well.
-//		Map<Document, ImportExportSession.ImportExportQuestion> mapping = new HashMap<>();
+//		Map<Document, ImportExportSession.ImportExportContent> mapping = new HashMap<>();
 //		// Later, generate all answer documents
 //		List<Document> answers = new ArrayList<>();
 //		// We can then push answers together with comments in one large bulk request
@@ -301,7 +301,7 @@ public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session>
 //		List<Document> motds = new ArrayList<>();
 //		try {
 //			// add session id to all questions and generate documents
-//			for (ImportExportSession.ImportExportQuestion question : importSession.getQuestions()) {
+//			for (ImportExportSession.ImportExportContent question : importSession.getQuestions()) {
 //				Document doc = toQuestionDocument(session, question);
 //				question.setSessionId(session.getId());
 //				questions.add(doc);
@@ -310,11 +310,11 @@ public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session>
 //			database.bulkSaveDocuments(questions.toArray(new Document[questions.size()]));
 //
 //			// bulk import answers together with interposed questions
-//			for (Map.Entry<Document, ImportExportSession.ImportExportQuestion> entry : mapping.entrySet()) {
+//			for (Map.Entry<Document, ImportExportSession.ImportExportContent> entry : mapping.entrySet()) {
 //				final Document doc = entry.getKey();
-//				final ImportExportSession.ImportExportQuestion question = entry.getValue();
+//				final ImportExportSession.ImportExportContent question = entry.getValue();
 //				question.setId(doc.getId());
-//				question.set_rev(doc.getRev());
+//				question.setRevision(doc.getRev());
 //				for (de.thm.arsnova.entities.transport.Answer answer : question.getAnswers()) {
 //					final Answer a = answer.generateAnswerEntity(user, question);
 //					final Document answerDoc = new Document();
@@ -379,8 +379,8 @@ public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session>
 //		ImportExportSession importExportSession = new ImportExportSession();
 //		Session session = getDatabaseDao().getSessionFromKeyword(sessionkey);
 //		importExportSession.setSessionFromSessionObject(session);
-//		List<Question> questionList = getDatabaseDao().getAllSkillQuestions(session);
-//		for (Question question : questionList) {
+//		List<Content> questionList = getDatabaseDao().getAllSkillQuestions(session);
+//		for (Content question : questionList) {
 //			List<de.thm.arsnova.entities.transport.Answer> answerList = new ArrayList<>();
 //			if (withAnswers) {
 //				for (Answer a : this.getDatabaseDao().getAllAnswers(question)) {
@@ -424,7 +424,7 @@ public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session>
 				unreadComments++;
 			}
 		}
-		for (ImportExportSession.ImportExportQuestion question : importExportSession.getQuestions()) {
+		for (ImportExportSession.ImportExportContent question : importExportSession.getQuestions()) {
 			numAnswers += question.getAnswers().size();
 			if (question.getAnswers().isEmpty()) {
 				numUnanswered++;
@@ -506,7 +506,7 @@ public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session>
 	private List<SessionInfo> getInfosForSessions(final List<Session> sessions) {
 		/* TODO: migrate to new view */
 		List<String> sessionIds = sessions.stream().map(Session::getId).collect(Collectors.toList());
-		final ViewQuery questionCountView = createQuery("by_sessionid").designDocId("_design/content")
+		final ViewQuery questionCountView = createQuery("by_sessionid").designDocId("_design/Content")
 				.group(true).keys(sessionIds);
 		final ViewQuery answerCountView = createQuery("by_sessionid").designDocId("_design/answer")
 				.group(true).keys(sessionIds);
@@ -521,7 +521,7 @@ public class CouchDbSessionRepository extends CouchDbRepositorySupport<Session>
 	private List<SessionInfo> getInfosForVisitedSessions(final List<Session> sessions, final User user) {
 		final ViewQuery answeredQuestionsView = createQuery("by_user_sessionid").designDocId("_design/answer")
 				.keys(sessions.stream().map(session -> ComplexKey.of(user.getUsername(), session.getId())).collect(Collectors.toList()));
-		final ViewQuery questionIdsView = createQuery("by_sessionid").designDocId("_design/content")
+		final ViewQuery questionIdsView = createQuery("by_sessionid").designDocId("_design/Content")
 				.keys(sessions.stream().map(Session::getId).collect(Collectors.toList()));
 
 		return getVisitedSessionInfoData(sessions, answeredQuestionsView, questionIdsView);
diff --git a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
index 9a18beb46..7b65c1c95 100644
--- a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
+++ b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java
@@ -19,11 +19,12 @@ package de.thm.arsnova.security;
 
 import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.Comment;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.exceptions.UnauthorizedException;
 import de.thm.arsnova.persistance.CommentRepository;
+import de.thm.arsnova.persistance.ContentRepository;
 import de.thm.arsnova.persistance.SessionRepository;
 import org.pac4j.oauth.profile.facebook.FacebookProfile;
 import org.pac4j.oauth.profile.google2.Google2Profile;
@@ -55,6 +56,9 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 	@Autowired
 	private CommentRepository commentRepository;
 
+	@Autowired
+	private ContentRepository contentRepository;
+
 	@Override
 	public boolean hasPermission(
 			final Authentication authentication,
@@ -88,7 +92,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 				&& checkSessionPermission(username, targetId, permission)) {
 			return true;
 		} else if (
-				"question".equals(targetType)
+				"content".equals(targetType)
 				&& checkQuestionPermission(username, targetId, permission)
 				) {
 			return true;
@@ -125,9 +129,9 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
 			final Object permission
 			) {
 		if (permission instanceof String && "owner".equals(permission)) {
-			final Question question = dao.getQuestion(targetId.toString());
-			if (question != null) {
-				final Session session = sessionRepository.getSessionFromId(question.getSessionId());
+			final Content content = contentRepository.getQuestion(targetId.toString());
+			if (content != null) {
+				final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
 				return session != null && session.getCreator().equals(username);
 			}
diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/ContentService.java
similarity index 69%
rename from src/main/java/de/thm/arsnova/services/QuestionService.java
rename to src/main/java/de/thm/arsnova/services/ContentService.java
index 2ba43ee68..c08d1ccbf 100644
--- a/src/main/java/de/thm/arsnova/services/QuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/ContentService.java
@@ -22,7 +22,7 @@ import de.thm.arsnova.dao.IDatabaseDao;
 import de.thm.arsnova.entities.Answer;
 import de.thm.arsnova.entities.Comment;
 import de.thm.arsnova.entities.CommentReadingCount;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 import de.thm.arsnova.events.*;
@@ -31,6 +31,7 @@ import de.thm.arsnova.exceptions.ForbiddenException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
 import de.thm.arsnova.persistance.CommentRepository;
+import de.thm.arsnova.persistance.ContentRepository;
 import de.thm.arsnova.persistance.SessionRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,7 +54,7 @@ import java.util.TimerTask;
  * Performs all question, comment, and answer related operations.
  */
 @Service
-public class QuestionService implements IQuestionService, ApplicationEventPublisherAware {
+public class ContentService implements IContentService, ApplicationEventPublisherAware {
 
 	@Autowired
 	private IDatabaseDao databaseDao;
@@ -67,6 +68,9 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Autowired
 	private CommentRepository commentRepository;
 
+	@Autowired
+	private ContentRepository contentRepository;
+
 	@Autowired
 	private ImageUtils imageUtils;
 
@@ -75,7 +79,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	private ApplicationEventPublisher publisher;
 
-	private static final Logger logger = LoggerFactory.getLogger(QuestionService.class);
+	private static final Logger logger = LoggerFactory.getLogger(ContentService.class);
 
 	private HashMap<String, Timer> timerList = new HashMap<>();
 
@@ -85,13 +89,13 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getSkillQuestions(final String sessionkey) {
+	public List<Content> getSkillQuestions(final String sessionkey) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getSkillQuestionsForTeachers(session);
+			return contentRepository.getSkillQuestionsForTeachers(session);
 		} else {
-			return databaseDao.getSkillQuestionsForUsers(session);
+			return contentRepository.getSkillQuestionsForUsers(session);
 		}
 	}
 
@@ -99,33 +103,34 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated()")
 	public int getSkillQuestionCount(final String sessionkey) {
 		final Session session = sessionRepository.getSessionFromKeyword(sessionkey);
-		return databaseDao.getSkillQuestionCount(session);
+		return contentRepository.getSkillQuestionCount(session);
 	}
 
+	/* FIXME: #content.getSessionKeyword() cannot be checked since keyword is no longer set for content. */
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#question.getSessionKeyword(), 'session', 'owner')")
-	public Question saveQuestion(final Question question) {
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
-		question.setSessionId(session.getId());
-		question.setTimestamp(System.currentTimeMillis() / 1000L);
+	@PreAuthorize("isAuthenticated() and hasPermission(#content.getSessionKeyword(), 'session', 'owner')")
+	public Content saveQuestion(final Content content) {
+		final Session session = sessionRepository.getSessionFromKeyword(content.getSessionKeyword());
+		content.setSessionId(session.getId());
+		content.setTimestamp(System.currentTimeMillis() / 1000L);
 
-		if ("freetext".equals(question.getQuestionType())) {
-			question.setPiRound(0);
-		} else if (question.getPiRound() < 1 || question.getPiRound() > 2) {
-			question.setPiRound(1);
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
+		} else if (content.getPiRound() < 1 || content.getPiRound() > 2) {
+			content.setPiRound(1);
 		}
 
 		// convert imageurl to base64 if neccessary
-		if ("grid".equals(question.getQuestionType()) && !question.getImage().startsWith("http")) {
+		if ("grid".equals(content.getQuestionType()) && !content.getImage().startsWith("http")) {
 			// base64 adds offset to filesize, formula taken from: http://en.wikipedia.org/wiki/Base64#MIME
-			final int fileSize = (int) ((question.getImage().length() - 814) / 1.37);
+			final int fileSize = (int) ((content.getImage().length() - 814) / 1.37);
 			if (fileSize > uploadFileSizeByte) {
 				logger.error("Could not save file. File is too large with {} Byte.", fileSize);
 				throw new BadRequestException();
 			}
 		}
 
-		final Question result = databaseDao.saveQuestion(session, question);
+		final Content result = contentRepository.saveQuestion(session, content);
 
 		final NewQuestionEvent event = new NewQuestionEvent(this, session, result);
 		this.publisher.publishEvent(event);
@@ -149,8 +154,8 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public Question getQuestion(final String id) {
-		final Question result = databaseDao.getQuestion(id);
+	public Content getQuestion(final String id) {
+		final Content result = contentRepository.getQuestion(id);
 		if (result == null) {
 			return null;
 		}
@@ -163,20 +168,20 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void deleteQuestion(final String questionId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		if (question == null) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
 
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 		if (session == null) {
 			throw new UnauthorizedException();
 		}
-		databaseDao.deleteQuestionWithAnswers(question);
+		contentRepository.deleteQuestionWithAnswers(content);
 
-		final DeleteQuestionEvent event = new DeleteQuestionEvent(this, session, question);
+		final DeleteQuestionEvent event = new DeleteQuestionEvent(this, session, content);
 		this.publisher.publishEvent(event);
 	}
 
@@ -184,17 +189,17 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated() and hasPermission(#sessionKeyword, 'session', 'owner')")
 	public void deleteAllQuestions(final String sessionKeyword) {
 		final Session session = getSessionWithAuthCheck(sessionKeyword);
-		databaseDao.deleteAllQuestionsWithAnswers(session);
+		contentRepository.deleteAllQuestionsWithAnswers(session);
 
 		final DeleteAllQuestionsEvent event = new DeleteAllQuestionsEvent(this, session);
 		this.publisher.publishEvent(event);
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void startNewPiRound(final String questionId, User user) {
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
 		if (null == user) {
 			user = userService.getCurrentUser();
@@ -202,57 +207,57 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 		cancelDelayedPiRoundChange(questionId);
 
-		question.setPiRoundEndTime(0);
-		question.setVotingDisabled(true);
-		question.updateRoundManagementState();
-		update(question, user);
+		content.setPiRoundEndTime(0);
+		content.setVotingDisabled(true);
+		content.updateRoundManagementState();
+		update(content, user);
 
-		this.publisher.publishEvent(new PiRoundEndEvent(this, session, question));
+		this.publisher.publishEvent(new PiRoundEndEvent(this, session, content));
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void startNewPiRoundDelayed(final String questionId, final int time) {
-		final IQuestionService questionService = this;
+		final IContentService contentService = this;
 		final User user = userService.getCurrentUser();
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
 		final Date date = new Date();
 		final Timer timer = new Timer();
 		final Date endDate = new Date(date.getTime() + (time * 1000));
-		question.updateRoundStartVariables(date, endDate);
-		update(question);
+		content.updateRoundStartVariables(date, endDate);
+		update(content);
 
-		this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, session, question));
+		this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, session, content));
 		timerList.put(questionId, timer);
 
 		timer.schedule(new TimerTask() {
 			@Override
 			public void run() {
-				questionService.startNewPiRound(questionId, user);
+				contentService.startNewPiRound(questionId, user);
 			}
 		}, endDate);
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void cancelPiRoundChange(final String questionId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 
 		cancelDelayedPiRoundChange(questionId);
-		question.resetRoundManagementState();
+		content.resetRoundManagementState();
 
-		if (0 == question.getPiRound() || 1 == question.getPiRound()) {
-			question.setPiRoundFinished(false);
+		if (0 == content.getPiRound() || 1 == content.getPiRound()) {
+			content.setPiRoundFinished(false);
 		} else {
-			question.setPiRound(1);
-			question.setPiRoundFinished(true);
+			content.setPiRound(1);
+			content.setPiRoundFinished(true);
 		}
 
-		update(question);
-		this.publisher.publishEvent(new PiRoundCancelEvent(this, session, question));
+		update(content);
+		this.publisher.publishEvent(new PiRoundCancelEvent(this, session, content));
 	}
 
 	@Override
@@ -267,60 +272,60 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void resetPiRoundState(final String questionId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 		cancelDelayedPiRoundChange(questionId);
 
-		if ("freetext".equals(question.getQuestionType())) {
-			question.setPiRound(0);
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
 		} else {
-			question.setPiRound(1);
+			content.setPiRound(1);
 		}
 
-		question.resetRoundManagementState();
-		databaseDao.deleteAnswers(question);
-		update(question);
-		this.publisher.publishEvent(new PiRoundResetEvent(this, session, question));
+		content.resetRoundManagementState();
+		databaseDao.deleteAnswers(content);
+		update(content);
+		this.publisher.publishEvent(new PiRoundResetEvent(this, session, content));
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void setVotingAdmission(final String questionId, final boolean disableVoting) {
-		final Question question = databaseDao.getQuestion(questionId);
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
-		question.setVotingDisabled(disableVoting);
+		final Content content = contentRepository.getQuestion(questionId);
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		content.setVotingDisabled(disableVoting);
 
-		if (!disableVoting && !question.isActive()) {
-			question.setActive(true);
-			update(question);
+		if (!disableVoting && !content.isActive()) {
+			content.setActive(true);
+			update(content);
 		} else {
-			databaseDao.updateQuestion(question);
+			contentRepository.updateQuestion(content);
 		}
 		NovaEvent event;
 		if (disableVoting) {
-			event = new LockVoteEvent(this, session, question);
+			event = new LockVoteEvent(this, session, content);
 		} else {
-			event = new UnlockVoteEvent(this, session, question);
+			event = new UnlockVoteEvent(this, session, content);
 		}
 		this.publisher.publishEvent(event);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public void setVotingAdmissions(final String sessionkey, final boolean disableVoting, List<Question> questions) {
+	public void setVotingAdmissions(final String sessionkey, final boolean disableVoting, List<Content> contents) {
 		final User user = getCurrentUser();
 		final Session session = getSession(sessionkey);
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		databaseDao.setVotingAdmissions(session, disableVoting, questions);
+		contentRepository.setVotingAdmissions(session, disableVoting, contents);
 		NovaEvent event;
 		if (disableVoting) {
-			event = new LockVotesEvent(this, session, questions);
+			event = new LockVotesEvent(this, session, contents);
 		} else {
-			event = new UnlockVotesEvent(this, session, questions);
+			event = new UnlockVotesEvent(this, session, contents);
 		}
 		this.publisher.publishEvent(event);
 	}
@@ -333,12 +338,12 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		final List<Question> questions = databaseDao.setVotingAdmissionForAllQuestions(session, disableVoting);
+		final List<Content> contents = contentRepository.setVotingAdmissionForAllQuestions(session, disableVoting);
 		NovaEvent event;
 		if (disableVoting) {
-			event = new LockVotesEvent(this, session, questions);
+			event = new LockVotesEvent(this, session, contents);
 		} else {
-			event = new UnlockVotesEvent(this, session, questions);
+			event = new UnlockVotesEvent(this, session, contents);
 		}
 		this.publisher.publishEvent(event);
 	}
@@ -382,12 +387,12 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	}
 
 	@Override
-	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')")
+	@PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'content', 'owner')")
 	public void deleteAnswers(final String questionId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		question.resetQuestionState();
-		databaseDao.updateQuestion(question);
-		databaseDao.deleteAnswers(question);
+		final Content content = contentRepository.getQuestion(questionId);
+		content.resetQuestionState();
+		contentRepository.updateQuestion(content);
+		databaseDao.deleteAnswers(content);
 	}
 
 	@Override
@@ -395,7 +400,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	public List<String> getUnAnsweredQuestionIds(final String sessionKey) {
 		final User user = getCurrentUser();
 		final Session session = getSession(sessionKey);
-		return databaseDao.getUnAnsweredQuestionIds(session, user);
+		return contentRepository.getUnAnsweredQuestionIds(session, user);
 	}
 
 	private User getCurrentUser() {
@@ -409,11 +414,11 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public Answer getMyAnswer(final String questionId) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
-		return databaseDao.getMyAnswer(userService.getCurrentUser(), questionId, question.getPiRound());
+		return databaseDao.getMyAnswer(userService.getCurrentUser(), questionId, content.getPiRound());
 	}
 
 	@Override
@@ -435,74 +440,74 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getAnswers(final String questionId, final int piRound, final int offset, final int limit) {
-		final Question question = databaseDao.getQuestion(questionId);
-		if (question == null) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
-		return "freetext".equals(question.getQuestionType())
+		return "freetext".equals(content.getQuestionType())
 				? getFreetextAnswers(questionId, offset, limit)
-						: databaseDao.getAnswers(question, piRound);
+						: databaseDao.getAnswers(content, piRound);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getAnswers(final String questionId, final int offset, final int limit) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
-		if ("freetext".equals(question.getQuestionType())) {
+		if ("freetext".equals(content.getQuestionType())) {
 			return getFreetextAnswers(questionId, offset, limit);
 		} else {
-			return databaseDao.getAnswers(question);
+			return databaseDao.getAnswers(content);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getAllAnswers(final String questionId, final int offset, final int limit) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
-		if ("freetext".equals(question.getQuestionType())) {
+		if ("freetext".equals(content.getQuestionType())) {
 			return getFreetextAnswers(questionId, offset, limit);
 		} else {
-			return databaseDao.getAllAnswers(question);
+			return databaseDao.getAllAnswers(content);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getAnswerCount(final String questionId) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			return 0;
 		}
 
-		if ("freetext".equals(question.getQuestionType())) {
-			return databaseDao.getTotalAnswerCountByQuestion(question);
+		if ("freetext".equals(content.getQuestionType())) {
+			return databaseDao.getTotalAnswerCountByQuestion(content);
 		} else {
-			return databaseDao.getAnswerCount(question, question.getPiRound());
+			return databaseDao.getAnswerCount(content, content.getPiRound());
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getAnswerCount(final String questionId, final int piRound) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			return 0;
 		}
 
-		return databaseDao.getAnswerCount(question, piRound);
+		return databaseDao.getAnswerCount(content, piRound);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getAbstentionAnswerCount(final String questionId) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			return 0;
 		}
 
@@ -512,12 +517,12 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getTotalAnswerCountByQuestion(final String questionId) {
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			return 0;
 		}
 
-		return databaseDao.getTotalAnswerCountByQuestion(question);
+		return databaseDao.getTotalAnswerCountByQuestion(content);
 	}
 
 	@Override
@@ -539,29 +544,29 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated()")
 	public List<Answer> getMyAnswers(final String sessionKey) {
 		final Session session = getSession(sessionKey);
-		// Load questions first because we are only interested in answers of the latest piRound.
-		final List<Question> questions = databaseDao.getSkillQuestionsForUsers(session);
-		final Map<String, Question> questionIdToQuestion = new HashMap<>();
-		for (final Question question : questions) {
-			questionIdToQuestion.put(question.get_id(), question);
+		// Load contents first because we are only interested in answers of the latest piRound.
+		final List<Content> contents = contentRepository.getSkillQuestionsForUsers(session);
+		final Map<String, Content> questionIdToQuestion = new HashMap<>();
+		for (final Content content : contents) {
+			questionIdToQuestion.put(content.getId(), content);
 		}
 
 		/* filter answers by active piRound per question */
 		final List<Answer> answers = databaseDao.getMyAnswers(userService.getCurrentUser(), session);
 		final List<Answer> filteredAnswers = new ArrayList<>();
 		for (final Answer answer : answers) {
-			final Question question = questionIdToQuestion.get(answer.getQuestionId());
-			if (question == null) {
-				// Question is not present. Most likely it has been locked by the
+			final Content content = questionIdToQuestion.get(answer.getQuestionId());
+			if (content == null) {
+				// Content is not present. Most likely it has been locked by the
 				// Session's creator. Locked Questions do not appear in this list.
 				continue;
 			}
-			if (0 == answer.getPiRound() && !"freetext".equals(question.getQuestionType())) {
+			if (0 == answer.getPiRound() && !"freetext".equals(content.getQuestionType())) {
 				answer.setPiRound(1);
 			}
 
-			// discard all answers that aren't in the same piRound as the question
-			if (answer.getPiRound() == question.getPiRound()) {
+			// discard all answers that aren't in the same piRound as the content
+			if (answer.getPiRound() == content.getPiRound()) {
 				filteredAnswers.add(answer);
 			}
 		}
@@ -641,36 +646,36 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public Question update(final Question question) {
+	public Content update(final Content content) {
 		final User user = userService.getCurrentUser();
-		return update(question, user);
+		return update(content, user);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public Question update(final Question question, User user) {
-		final Question oldQuestion = databaseDao.getQuestion(question.get_id());
-		if (null == oldQuestion) {
+	public Content update(final Content content, User user) {
+		final Content oldContent = contentRepository.getQuestion(content.getId());
+		if (null == oldContent) {
 			throw new NotFoundException();
 		}
 
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 		if (user == null || session == null || !session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
 
-		if ("freetext".equals(question.getQuestionType())) {
-			question.setPiRound(0);
-		} else if (question.getPiRound() < 1 || question.getPiRound() > 2) {
-			question.setPiRound(oldQuestion.getPiRound() > 0 ? oldQuestion.getPiRound() : 1);
+		if ("freetext".equals(content.getQuestionType())) {
+			content.setPiRound(0);
+		} else if (content.getPiRound() < 1 || content.getPiRound() > 2) {
+			content.setPiRound(oldContent.getPiRound() > 0 ? oldContent.getPiRound() : 1);
 		}
 
-		final Question result = databaseDao.updateQuestion(question);
+		final Content result = contentRepository.updateQuestion(content);
 
-		if (!oldQuestion.isActive() && question.isActive()) {
+		if (!oldContent.isActive() && content.isActive()) {
 			final UnlockQuestionEvent event = new UnlockQuestionEvent(this, session, result);
 			this.publisher.publishEvent(event);
-		} else if (oldQuestion.isActive() && !question.isActive()) {
+		} else if (oldContent.isActive() && !content.isActive()) {
 			final LockQuestionEvent event = new LockQuestionEvent(this, session, result);
 			this.publisher.publishEvent(event);
 		}
@@ -681,26 +686,26 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@PreAuthorize("isAuthenticated()")
 	public Answer saveAnswer(final String questionId, final de.thm.arsnova.entities.transport.Answer answer) {
 		final User user = getCurrentUser();
-		final Question question = getQuestion(questionId);
-		if (question == null) {
+		final Content content = getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
 
-		Answer theAnswer = answer.generateAnswerEntity(user, question);
-		if ("freetext".equals(question.getQuestionType())) {
+		Answer theAnswer = answer.generateAnswerEntity(user, content);
+		if ("freetext".equals(content.getQuestionType())) {
 			imageUtils.generateThumbnailImage(theAnswer);
-			if (question.isFixedAnswer() && question.getText() != null) {
+			if (content.isFixedAnswer() && content.getText() != null) {
 				theAnswer.setAnswerTextRaw(theAnswer.getAnswerText());
 
-				if (question.isStrictMode()) {
-					question.checkTextStrictOptions(theAnswer);
+				if (content.isStrictMode()) {
+					content.checkTextStrictOptions(theAnswer);
 				}
-				theAnswer.setQuestionValue(question.evaluateCorrectAnswerFixedText(theAnswer.getAnswerTextRaw()));
-				theAnswer.setSuccessfulFreeTextAnswer(question.isSuccessfulFreeTextAnswer(theAnswer.getAnswerTextRaw()));
+				theAnswer.setQuestionValue(content.evaluateCorrectAnswerFixedText(theAnswer.getAnswerTextRaw()));
+				theAnswer.setSuccessfulFreeTextAnswer(content.isSuccessfulFreeTextAnswer(theAnswer.getAnswerTextRaw()));
 			}
 		}
 
-		return databaseDao.saveAnswer(theAnswer, user, question, getSession(question.getSessionKeyword()));
+		return databaseDao.saveAnswer(theAnswer, user, content, sessionRepository.getSessionFromId(content.getSessionId()));
 	}
 
 	@Override
@@ -712,14 +717,14 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 			throw new UnauthorizedException();
 		}
 
-		final Question question = getQuestion(answer.getQuestionId());
-		if ("freetext".equals(question.getQuestionType())) {
+		final Content content = getQuestion(answer.getQuestionId());
+		if ("freetext".equals(content.getQuestionType())) {
 			imageUtils.generateThumbnailImage(realAnswer);
-			question.checkTextStrictOptions(realAnswer);
+			content.checkTextStrictOptions(realAnswer);
 		}
 		final Answer result = databaseDao.updateAnswer(realAnswer);
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
-		this.publisher.publishEvent(new NewAnswerEvent(this, session, result, user, question));
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
+		this.publisher.publishEvent(new NewAnswerEvent(this, session, result, user, content));
 
 		return result;
 	}
@@ -727,66 +732,66 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteAnswer(final String questionId, final String answerId) {
-		final Question question = databaseDao.getQuestion(questionId);
-		if (question == null) {
+		final Content content = contentRepository.getQuestion(questionId);
+		if (content == null) {
 			throw new NotFoundException();
 		}
 		final User user = userService.getCurrentUser();
-		final Session session = sessionRepository.getSessionFromKeyword(question.getSessionKeyword());
+		final Session session = sessionRepository.getSessionFromId(content.getSessionId());
 		if (user == null || session == null || !session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
 		databaseDao.deleteAnswer(answerId);
 
-		this.publisher.publishEvent(new DeleteAnswerEvent(this, session, question));
+		this.publisher.publishEvent(new DeleteAnswerEvent(this, session, content));
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getLectureQuestions(final String sessionkey) {
+	public List<Content> getLectureQuestions(final String sessionkey) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getLectureQuestionsForTeachers(session);
+			return contentRepository.getLectureQuestionsForTeachers(session);
 		} else {
-			return databaseDao.getLectureQuestionsForUsers(session);
+			return contentRepository.getLectureQuestionsForUsers(session);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getFlashcards(final String sessionkey) {
+	public List<Content> getFlashcards(final String sessionkey) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getFlashcardsForTeachers(session);
+			return contentRepository.getFlashcardsForTeachers(session);
 		} else {
-			return databaseDao.getFlashcardsForUsers(session);
+			return contentRepository.getFlashcardsForUsers(session);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> getPreparationQuestions(final String sessionkey) {
+	public List<Content> getPreparationQuestions(final String sessionkey) {
 		final Session session = getSession(sessionkey);
 		final User user = userService.getCurrentUser();
 		if (session.isCreator(user)) {
-			return databaseDao.getPreparationQuestionsForTeachers(session);
+			return contentRepository.getPreparationQuestionsForTeachers(session);
 		} else {
-			return databaseDao.getPreparationQuestionsForUsers(session);
+			return contentRepository.getPreparationQuestionsForUsers(session);
 		}
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public List<Question> replaceImageData(final List<Question> questions) {
-		for (Question q : questions) {
+	public List<Content> replaceImageData(final List<Content> contents) {
+		for (Content q : contents) {
 			if (q.getImage() != null && q.getImage().startsWith("data:image/")) {
 				q.setImage("true");
 			}
 		}
 
-		return questions;
+		return contents;
 	}
 
 	private Session getSession(final String sessionkey) {
@@ -800,19 +805,19 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getLectureQuestionCount(final String sessionkey) {
-		return databaseDao.getLectureQuestionCount(getSession(sessionkey));
+		return contentRepository.getLectureQuestionCount(getSession(sessionkey));
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getFlashcardCount(final String sessionkey) {
-		return databaseDao.getFlashcardCount(getSession(sessionkey));
+		return contentRepository.getFlashcardCount(getSession(sessionkey));
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public int getPreparationQuestionCount(final String sessionkey) {
-		return databaseDao.getPreparationQuestionCount(getSession(sessionkey));
+		return contentRepository.getPreparationQuestionCount(getSession(sessionkey));
 	}
 
 	@Override
@@ -832,15 +837,15 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	public Map<String, Object> getAnswerAndAbstentionCountInternal(final String questionId) {
-		final Question question = getQuestion(questionId);
+		final Content content = getQuestion(questionId);
 		HashMap<String, Object> map = new HashMap<>();
 
-		if (question == null) {
+		if (content == null) {
 			return null;
 		}
 
 		map.put("_id", questionId);
-		map.put("answers", databaseDao.getAnswerCount(question, question.getPiRound()));
+		map.put("answers", databaseDao.getAnswerCount(content, content.getPiRound()));
 		map.put("abstentions", databaseDao.getAbstentionAnswerCount(questionId));
 
 		return map;
@@ -867,28 +872,28 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	 */
 	@Override
 	public int countFlashcardsForUserInternal(final String sessionkey) {
-		return databaseDao.getFlashcardsForUsers(getSession(sessionkey)).size();
+		return contentRepository.getFlashcardsForUsers(getSession(sessionkey)).size();
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteLectureQuestions(final String sessionkey) {
 		final Session session = getSessionWithAuthCheck(sessionkey);
-		databaseDao.deleteAllLectureQuestionsWithAnswers(session);
+		contentRepository.deleteAllLectureQuestionsWithAnswers(session);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deleteFlashcards(final String sessionkey) {
 		final Session session = getSessionWithAuthCheck(sessionkey);
-		databaseDao.deleteAllFlashcardsWithAnswers(session);
+		contentRepository.deleteAllFlashcardsWithAnswers(session);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public void deletePreparationQuestions(final String sessionkey) {
 		final Session session = getSessionWithAuthCheck(sessionkey);
-		databaseDao.deleteAllPreparationQuestionsWithAnswers(session);
+		contentRepository.deleteAllPreparationQuestionsWithAnswers(session);
 	}
 
 	@Override
@@ -901,7 +906,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	public List<String> getUnAnsweredLectureQuestionIds(final String sessionkey, final User user) {
 		final Session session = getSession(sessionkey);
-		return databaseDao.getUnAnsweredLectureQuestionIds(session, user);
+		return contentRepository.getUnAnsweredLectureQuestionIds(session, user);
 	}
 
 	@Override
@@ -914,7 +919,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 	@Override
 	public List<String> getUnAnsweredPreparationQuestionIds(final String sessionkey, final User user) {
 		final Session session = getSession(sessionkey);
-		return databaseDao.getUnAnsweredPreparationQuestionIds(session, user);
+		return contentRepository.getUnAnsweredPreparationQuestionIds(session, user);
 	}
 
 	@Override
@@ -925,30 +930,30 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		final List<Question> questions = databaseDao.publishAllQuestions(session, publish);
+		final List<Content> contents = contentRepository.publishAllQuestions(session, publish);
 		NovaEvent event;
 		if (publish) {
-			event = new UnlockQuestionsEvent(this, session, questions);
+			event = new UnlockQuestionsEvent(this, session, contents);
 		} else {
-			event = new LockQuestionsEvent(this, session, questions);
+			event = new LockQuestionsEvent(this, session, contents);
 		}
 		this.publisher.publishEvent(event);
 	}
 
 	@Override
 	@PreAuthorize("isAuthenticated()")
-	public void publishQuestions(final String sessionkey, final boolean publish, List<Question> questions) {
+	public void publishQuestions(final String sessionkey, final boolean publish, List<Content> contents) {
 		final User user = getCurrentUser();
 		final Session session = getSession(sessionkey);
 		if (!session.isCreator(user)) {
 			throw new UnauthorizedException();
 		}
-		databaseDao.publishQuestions(session, publish, questions);
+		contentRepository.publishQuestions(session, publish, contents);
 		NovaEvent event;
 		if (publish) {
-			event = new UnlockQuestionsEvent(this, session, questions);
+			event = new UnlockQuestionsEvent(this, session, contents);
 		} else {
-			event = new LockQuestionsEvent(this, session, questions);
+			event = new LockQuestionsEvent(this, session, contents);
 		}
 		this.publisher.publishEvent(event);
 	}
@@ -1010,8 +1015,8 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	public String getQuestionImage(String questionId) {
-		Question question = databaseDao.getQuestion(questionId);
-		String imageData = question.getImage();
+		Content content = contentRepository.getQuestion(questionId);
+		String imageData = content.getImage();
 
 		if (imageData == null) {
 			imageData = "";
@@ -1022,8 +1027,8 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis
 
 	@Override
 	public String getQuestionFcImage(String questionId) {
-		Question question = databaseDao.getQuestion(questionId);
-		String imageData = question.getFcImage();
+		Content content = contentRepository.getQuestion(questionId);
+		String imageData = content.getFcImage();
 
 		if (imageData == null) {
 			imageData = "";
diff --git a/src/main/java/de/thm/arsnova/services/IQuestionService.java b/src/main/java/de/thm/arsnova/services/IContentService.java
similarity index 88%
rename from src/main/java/de/thm/arsnova/services/IQuestionService.java
rename to src/main/java/de/thm/arsnova/services/IContentService.java
index 4e257c395..9b4e4f97b 100644
--- a/src/main/java/de/thm/arsnova/services/IQuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/IContentService.java
@@ -20,7 +20,7 @@ package de.thm.arsnova.services;
 import de.thm.arsnova.entities.Answer;
 import de.thm.arsnova.entities.Comment;
 import de.thm.arsnova.entities.CommentReadingCount;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.entities.User;
 
 import java.util.List;
@@ -29,14 +29,14 @@ import java.util.Map;
 /**
  * The functionality the question service should provide.
  */
-public interface IQuestionService {
-	Question saveQuestion(Question question);
+public interface IContentService {
+	Content saveQuestion(Content content);
 
 	boolean saveQuestion(Comment comment);
 
-	Question getQuestion(String id);
+	Content getQuestion(String id);
 
-	List<Question> getSkillQuestions(String sessionkey);
+	List<Content> getSkillQuestions(String sessionkey);
 
 	int getSkillQuestionCount(String sessionkey);
 
@@ -88,9 +88,9 @@ public interface IQuestionService {
 
 	Comment readInterposedQuestionInternal(String commentId, User user);
 
-	Question update(Question question);
+	Content update(Content content);
 
-	Question update(Question question, User user);
+	Content update(Content content, User user);
 
 	void deleteAnswers(String questionId);
 
@@ -102,11 +102,11 @@ public interface IQuestionService {
 
 	void deleteInterposedQuestion(String commentId);
 
-	List<Question> getLectureQuestions(String sessionkey);
+	List<Content> getLectureQuestions(String sessionkey);
 
-	List<Question> getFlashcards(String sessionkey);
+	List<Content> getFlashcards(String sessionkey);
 
-	List<Question> getPreparationQuestions(String sessionkey);
+	List<Content> getPreparationQuestions(String sessionkey);
 
 	int getLectureQuestionCount(String sessionkey);
 
@@ -144,7 +144,7 @@ public interface IQuestionService {
 
 	void publishAll(String sessionkey, boolean publish);
 
-	void publishQuestions(String sessionkey, boolean publish, List<Question> questions);
+	void publishQuestions(String sessionkey, boolean publish, List<Content> contents);
 
 	void deleteAllQuestionsAnswers(String sessionkey);
 
@@ -158,7 +158,7 @@ public interface IQuestionService {
 
 	void setVotingAdmission(String questionId, boolean disableVoting);
 
-	void setVotingAdmissions(String sessionkey, boolean disableVoting, List<Question> questions);
+	void setVotingAdmissions(String sessionkey, boolean disableVoting, List<Content> contents);
 
 	void setVotingAdmissionForAllQuestions(String sessionkey, boolean disableVoting);
 
@@ -166,6 +166,6 @@ public interface IQuestionService {
 
 	String getQuestionFcImage(String questionId);
 
-	List<Question> replaceImageData(List<Question> questions);
+	List<Content> replaceImageData(List<Content> contents);
 
 }
diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
index 26057c7ec..b348dc32c 100644
--- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
+++ b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java
@@ -36,11 +36,11 @@ import de.thm.arsnova.exceptions.NoContentException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
 import de.thm.arsnova.services.IFeedbackService;
-import de.thm.arsnova.services.IQuestionService;
+import de.thm.arsnova.services.IContentService;
 import de.thm.arsnova.services.ISessionService;
 import de.thm.arsnova.services.IUserService;
 import de.thm.arsnova.socket.message.Feedback;
-import de.thm.arsnova.socket.message.Question;
+import de.thm.arsnova.socket.message.Content;
 import de.thm.arsnova.socket.message.Session;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -76,7 +76,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	private ISessionService sessionService;
 
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
 	private static final Logger logger = LoggerFactory.getLogger(ARSnovaSocketIOServer.class);
 
@@ -191,7 +191,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 					AckRequest ackRequest) {
 				final User user = userService.getUser2SocketId(client.getSessionId());
 				try {
-					questionService.readInterposedQuestionInternal(comment.getId(), user);
+					contentService.readInterposedQuestionInternal(comment.getId(), user);
 				} catch (NotFoundException | UnauthorizedException e) {
 					logger.error("Loading of comment {} failed for user {} with exception {}", comment.getId(), user, e.getMessage());
 				}
@@ -203,7 +203,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 			public void onData(SocketIOClient client, String answerId, AckRequest ackRequest) {
 				final User user = userService.getUser2SocketId(client.getSessionId());
 				try {
-					questionService.readFreetextAnswer(answerId, user);
+					contentService.readFreetextAnswer(answerId, user);
 				} catch (NotFoundException | UnauthorizedException e) {
 					logger.error("Marking answer {} as read failed for user {} with exception {}", answerId, user, e.getMessage());
 				}
@@ -360,17 +360,17 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 		final de.thm.arsnova.entities.Session session = sessionService.getSessionInternal(sessionKey, user);
 		final de.thm.arsnova.entities.SessionFeature features = sessionService.getSessionFeatures(sessionKey);
 
-		client.sendEvent("unansweredLecturerQuestions", questionService.getUnAnsweredLectureQuestionIds(sessionKey, user));
-		client.sendEvent("unansweredPreparationQuestions", questionService.getUnAnsweredPreparationQuestionIds(sessionKey, user));
-		client.sendEvent("countLectureQuestionAnswers", questionService.countLectureQuestionAnswersInternal(sessionKey));
-		client.sendEvent("countPreparationQuestionAnswers", questionService.countPreparationQuestionAnswersInternal(sessionKey));
+		client.sendEvent("unansweredLecturerQuestions", contentService.getUnAnsweredLectureQuestionIds(sessionKey, user));
+		client.sendEvent("unansweredPreparationQuestions", contentService.getUnAnsweredPreparationQuestionIds(sessionKey, user));
+		client.sendEvent("countLectureQuestionAnswers", contentService.countLectureQuestionAnswersInternal(sessionKey));
+		client.sendEvent("countPreparationQuestionAnswers", contentService.countPreparationQuestionAnswersInternal(sessionKey));
 		client.sendEvent("activeUserCountData", sessionService.activeUsers(sessionKey));
 		client.sendEvent("learningProgressOptions", session.getLearningProgressOptions());
 		final de.thm.arsnova.entities.Feedback fb = feedbackService.getFeedback(sessionKey);
 		client.sendEvent("feedbackData", fb.getValues());
 
 		if (features.isFlashcard() || features.isFlashcardFeature()) {
-			client.sendEvent("countFlashcards", questionService.countFlashcardsForUserInternal(sessionKey));
+			client.sendEvent("countFlashcards", contentService.countFlashcardsForUserInternal(sessionKey));
 			client.sendEvent("flipFlashcards", session.getFlipFlashcards());
 		}
 
@@ -421,8 +421,8 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 		broadcastInSession(sessionKey, "activeUserCountData", count);
 	}
 
-	public void reportAnswersToLecturerQuestionAvailable(final de.thm.arsnova.entities.Session session, final Question lecturerQuestion) {
-		broadcastInSession(session.getKeyword(), "answersToLecQuestionAvail", lecturerQuestion.get_id());
+	public void reportAnswersToLecturerQuestionAvailable(final de.thm.arsnova.entities.Session session, final Content content) {
+		broadcastInSession(session.getKeyword(), "answersToLecQuestionAvail", content.get_id());
 	}
 
 	public void reportAudienceQuestionAvailable(final de.thm.arsnova.entities.Session session, final Comment audienceQuestion) {
@@ -430,25 +430,25 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 		broadcastInSession(session.getKeyword(), "audQuestionAvail", audienceQuestion.getId());
 	}
 
-	public void reportLecturerQuestionAvailable(final de.thm.arsnova.entities.Session session, final List<de.thm.arsnova.entities.Question> qs) {
-		List<Question> questions = new ArrayList<>();
-		for (de.thm.arsnova.entities.Question q : qs) {
-			questions.add(new Question(q));
+	public void reportLecturerQuestionAvailable(final de.thm.arsnova.entities.Session session, final List<de.thm.arsnova.entities.Content> qs) {
+		List<Content> contents = new ArrayList<>();
+		for (de.thm.arsnova.entities.Content q : qs) {
+			contents.add(new Content(q));
 		}
 
 		/* TODO role handling implementation, send this only to users with role audience */
 		if (!qs.isEmpty()) {
-			broadcastInSession(session.getKeyword(), "lecQuestionAvail", questions.get(0).get_id()); // deprecated!
+			broadcastInSession(session.getKeyword(), "lecQuestionAvail", contents.get(0).get_id()); // deprecated!
 		}
-		broadcastInSession(session.getKeyword(), "lecturerQuestionAvailable", questions);
+		broadcastInSession(session.getKeyword(), "lecturerQuestionAvailable", contents);
 	}
 
-	public void reportLecturerQuestionsLocked(final de.thm.arsnova.entities.Session session, final List<de.thm.arsnova.entities.Question> qs) {
-		List<Question> questions = new ArrayList<>();
-		for (de.thm.arsnova.entities.Question q : qs) {
-			questions.add(new Question(q));
+	public void reportLecturerQuestionsLocked(final de.thm.arsnova.entities.Session session, final List<de.thm.arsnova.entities.Content> qs) {
+		List<Content> contents = new ArrayList<>();
+		for (de.thm.arsnova.entities.Content q : qs) {
+			contents.add(new Content(q));
 		}
-		broadcastInSession(session.getKeyword(), "lecturerQuestionLocked", questions);
+		broadcastInSession(session.getKeyword(), "lecturerQuestionLocked", contents);
 	}
 
 	public void reportSessionStatus(final String sessionKey, final boolean active) {
@@ -505,17 +505,17 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	@Timed(name = "visit.NewAnswerEvent")
 	public void visit(NewAnswerEvent event) {
 		final String sessionKey = event.getSession().getKeyword();
-		this.reportAnswersToLecturerQuestionAvailable(event.getSession(), new Question(event.getQuestion()));
-		broadcastInSession(sessionKey, "countQuestionAnswersByQuestionId", questionService.getAnswerAndAbstentionCountInternal(event.getQuestion().get_id()));
-		broadcastInSession(sessionKey, "countLectureQuestionAnswers", questionService.countLectureQuestionAnswersInternal(sessionKey));
-		broadcastInSession(sessionKey, "countPreparationQuestionAnswers", questionService.countPreparationQuestionAnswersInternal(sessionKey));
-
-		// Update the unanswered count for the question variant that was answered.
-		final de.thm.arsnova.entities.Question question = event.getQuestion();
-		if ("lecture".equals(question.getQuestionVariant())) {
-			sendToUser(event.getUser(), "unansweredLecturerQuestions", questionService.getUnAnsweredLectureQuestionIds(sessionKey, event.getUser()));
-		} else if ("preparation".equals(question.getQuestionVariant())) {
-			sendToUser(event.getUser(), "unansweredPreparationQuestions", questionService.getUnAnsweredPreparationQuestionIds(sessionKey, event.getUser()));
+		this.reportAnswersToLecturerQuestionAvailable(event.getSession(), new Content(event.getContent()));
+		broadcastInSession(sessionKey, "countQuestionAnswersByQuestionId", contentService.getAnswerAndAbstentionCountInternal(event.getContent().getId()));
+		broadcastInSession(sessionKey, "countLectureQuestionAnswers", contentService.countLectureQuestionAnswersInternal(sessionKey));
+		broadcastInSession(sessionKey, "countPreparationQuestionAnswers", contentService.countPreparationQuestionAnswersInternal(sessionKey));
+
+		// Update the unanswered count for the content variant that was answered.
+		final de.thm.arsnova.entities.Content content = event.getContent();
+		if ("lecture".equals(content.getQuestionVariant())) {
+			sendToUser(event.getUser(), "unansweredLecturerQuestions", contentService.getUnAnsweredLectureQuestionIds(sessionKey, event.getUser()));
+		} else if ("preparation".equals(content.getQuestionVariant())) {
+			sendToUser(event.getUser(), "unansweredPreparationQuestions", contentService.getUnAnsweredPreparationQuestionIds(sessionKey, event.getUser()));
 		}
 	}
 
@@ -524,10 +524,10 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 	@Timed(name = "visit.DeleteAnswerEvent")
 	public void visit(DeleteAnswerEvent event) {
 		final String sessionKey = event.getSession().getKeyword();
-		this.reportAnswersToLecturerQuestionAvailable(event.getSession(), new Question(event.getQuestion()));
+		this.reportAnswersToLecturerQuestionAvailable(event.getSession(), new Content(event.getQuestion()));
 		// We do not know which user's answer was deleted, so we can't update his 'unanswered' list of questions...
-		broadcastInSession(sessionKey, "countLectureQuestionAnswers", questionService.countLectureQuestionAnswersInternal(sessionKey));
-		broadcastInSession(sessionKey, "countPreparationQuestionAnswers", questionService.countPreparationQuestionAnswersInternal(sessionKey));
+		broadcastInSession(sessionKey, "countLectureQuestionAnswers", contentService.countLectureQuestionAnswersInternal(sessionKey));
+		broadcastInSession(sessionKey, "countPreparationQuestionAnswers", contentService.countPreparationQuestionAnswersInternal(sessionKey));
 	}
 
 	@Async
@@ -574,20 +574,20 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 
 	@Override
 	public void visit(LockVotesEvent event) {
-		List<Question> questions = new ArrayList<>();
-		for (de.thm.arsnova.entities.Question q : event.getQuestions()) {
-			questions.add(new Question(q));
+		List<Content> contents = new ArrayList<>();
+		for (de.thm.arsnova.entities.Content q : event.getQuestions()) {
+			contents.add(new Content(q));
 		}
-		broadcastInSession(event.getSession().getKeyword(), "lockVotes", questions);
+		broadcastInSession(event.getSession().getKeyword(), "lockVotes", contents);
 	}
 
 	@Override
 	public void visit(UnlockVotesEvent event) {
-		List<Question> questions = new ArrayList<>();
-		for (de.thm.arsnova.entities.Question q : event.getQuestions()) {
-			questions.add(new Question(q));
+		List<Content> contents = new ArrayList<>();
+		for (de.thm.arsnova.entities.Content q : event.getQuestions()) {
+			contents.add(new Content(q));
 		}
-		broadcastInSession(event.getSession().getKeyword(), "unlockVotes", questions);
+		broadcastInSession(event.getSession().getKeyword(), "unlockVotes", contents);
 	}
 
 	@Override
@@ -597,7 +597,7 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor {
 		broadcastInSession(sessionKey, "featureChange", features);
 
 		if (features.isFlashcard() || features.isFlashcardFeature()) {
-			broadcastInSession(sessionKey, "countFlashcards", questionService.countFlashcardsForUserInternal(sessionKey));
+			broadcastInSession(sessionKey, "countFlashcards", contentService.countFlashcardsForUserInternal(sessionKey));
 			broadcastInSession(sessionKey, "flipFlashcards", event.getSession().getFlipFlashcards());
 		}
 	}
diff --git a/src/main/java/de/thm/arsnova/socket/message/Question.java b/src/main/java/de/thm/arsnova/socket/message/Content.java
similarity index 85%
rename from src/main/java/de/thm/arsnova/socket/message/Question.java
rename to src/main/java/de/thm/arsnova/socket/message/Content.java
index c8660e3db..f462baa7b 100644
--- a/src/main/java/de/thm/arsnova/socket/message/Question.java
+++ b/src/main/java/de/thm/arsnova/socket/message/Content.java
@@ -20,14 +20,14 @@ package de.thm.arsnova.socket.message;
 /**
  * Represents a question.
  */
-public class Question {
+public class Content {
 
 	private final String _id;
 	private final String variant;
 
-	public Question(de.thm.arsnova.entities.Question question) {
-		this._id = question.get_id();
-		this.variant = question.getQuestionVariant();
+	public Content(de.thm.arsnova.entities.Content content) {
+		this._id = content.getId();
+		this.variant = content.getQuestionVariant();
 	}
 
 	public String get_id() {
diff --git a/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java b/src/test/java/de/thm/arsnova/controller/ContentControllerTest.java
similarity index 96%
rename from src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java
rename to src/test/java/de/thm/arsnova/controller/ContentControllerTest.java
index 47a9b29c0..ec09fcca0 100644
--- a/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java
+++ b/src/test/java/de/thm/arsnova/controller/ContentControllerTest.java
@@ -31,13 +31,13 @@ import org.springframework.web.context.WebApplicationContext;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
-public class LecturerQuestionControllerTest extends AbstractControllerTest {
+public class ContentControllerTest extends AbstractControllerTest {
 
 	@Autowired
 	private StubUserService userService;
 
 	@Autowired
-	private LecturerQuestionController questionController;
+	private ContentController questionController;
 
 	private MockMvc mockMvc;
 
diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
index cbfb86d55..6dc8bb931 100644
--- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
+++ b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
@@ -33,7 +33,7 @@ public class StubDatabaseDao implements IDatabaseDao {
 
 	private static Map<String, Session> stubSessions = new ConcurrentHashMap<>();
 	private static Map<String, Feedback> stubFeedbacks = new ConcurrentHashMap<>();
-	private static Map<String, List<Question>> stubQuestions = new ConcurrentHashMap<>();
+	private static Map<String, List<Content>> stubQuestions = new ConcurrentHashMap<>();
 	private static Map<String, User> stubUsers = new ConcurrentHashMap<>();
 
 	public Comment comment;
@@ -91,29 +91,9 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	private void fillWithDummyQuestions() {
-		List<Question> questions = new ArrayList<>();
-		questions.add(new Question());
-		stubQuestions.put("12345678", questions);
-	}
-
-	@Override
-	public Question saveQuestion(Session session, Question question) {
-		List<Question> questions = stubQuestions.get(session.getKeyword());
-		questions.add(question);
-		stubQuestions.put(session.getId(), questions);
-
-		return question;
-	}
-
-	@Override
-	public Question getQuestion(String id) {
-		// Simply ... no such question ;-)
-		return null;
-	}
-
-	@Override
-	public int getSkillQuestionCount(Session session) {
-		return stubQuestions.get(session.getKeyword()).size();
+		List<Content> contents = new ArrayList<>();
+		contents.add(new Content());
+		stubQuestions.put("12345678", contents);
 	}
 
 	@Override
@@ -123,7 +103,7 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public int getAnswerCount(Question question, int piRound) {
+	public int getAnswerCount(Content content, int piRound) {
 		// TODO Auto-generated method stub
 		return 0;
 	}
@@ -146,31 +126,7 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<String> getQuestionIds(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<String> getUnAnsweredQuestionIds(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Question updateQuestion(Question question) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public int deleteQuestionWithAnswers(Question question) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int deleteAnswers(Question question) {
+	public int deleteAnswers(Content content) {
 		// TODO Auto-generated method stub
 		return 0;
 	}
@@ -192,29 +148,6 @@ public class StubDatabaseDao implements IDatabaseDao {
 		return 0;
 	}
 
-	@Override
-	public int[] deleteAllQuestionsWithAnswers(Session session) {
-		return new int[] { 0, 0 };
-	}
-
-	@Override
-	public int getLectureQuestionCount(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int getFlashcardCount(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
-	@Override
-	public int getPreparationQuestionCount(Session session) {
-		// TODO Auto-generated method stub
-		return 0;
-	}
-
 	@Override
 	public int countLectureQuestionAnswers(Session session) {
 		// TODO Auto-generated method stub
@@ -227,45 +160,6 @@ public class StubDatabaseDao implements IDatabaseDao {
 		return 0;
 	}
 
-	@Override
-	public int[] deleteAllLectureQuestionsWithAnswers(Session session) {
-		return new int[] { 0, 0 };
-	}
-
-	@Override
-	public int[] deleteAllFlashcardsWithAnswers(Session session) {
-		return new int[] { 0, 0 };
-	}
-
-	@Override
-	public int[] deleteAllPreparationQuestionsWithAnswers(Session session) {
-		return new int[] { 0, 0 };
-	}
-
-	@Override
-	public List<String> getUnAnsweredLectureQuestionIds(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<String> getUnAnsweredPreparationQuestionIds(Session session, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public void publishQuestions(Session session, boolean publish, List<Question> questions) {
-		// TODO Auto-generated method stub
-
-	}
-
-	@Override
-	public List<Question> publishAllQuestions(Session session, boolean publish) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
 	@Override
 	public int deleteAllQuestionsAnswers(Session session) {
 		// TODO Auto-generated method stub
@@ -297,73 +191,19 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<Question> getSkillQuestionsForUsers(Session session) {
+	public List<Answer> getAnswers(Content content, int piRound) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getSkillQuestionsForTeachers(Session session) {
+	public List<Answer> getAnswers(Content content) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public List<Question> getLectureQuestionsForUsers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getLectureQuestionsForTeachers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getFlashcardsForUsers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getFlashcardsForTeachers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getPreparationQuestionsForUsers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getPreparationQuestionsForTeachers(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getAllSkillQuestions(Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Answer> getAnswers(Question question, int piRound) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Answer> getAnswers(Question question) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public Answer saveAnswer(Answer answer, User user, Question question, Session session) {
+	public Answer saveAnswer(Answer answer, User user, Content content, Session session) {
 		// TODO Auto-generated method stub
 		return null;
 	}
@@ -380,69 +220,38 @@ public class StubDatabaseDao implements IDatabaseDao {
 	}
 
 	@Override
-	public List<String> getSubjects(Session session, String questionVariant) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<String> getQuestionIdsBySubject(Session session, String questionVariant, String subject) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Question> getQuestionsByIds(List<String> ids, Session session) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
-	@Override
-	public List<Answer> getAllAnswers(Question question) {
+	public List<Answer> getAllAnswers(Content content) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public int getTotalAnswerCountByQuestion(Question question) {
+	public int getTotalAnswerCountByQuestion(Content content) {
 		// TODO Auto-generated method stub
 		return 0;
 	}
 
 	@Override
-	public void resetQuestionsRoundState(Session session,
-			List<Question> questions) {
-		// TODO Auto-generated method stub
-
-	}
-
-	@Override
-	public void setVotingAdmissions(Session session, boolean disableVoting, List<Question> questions) {
-		// TODO Auto-generated method stub
-
-	}
-
-	@Override
-	public List<Question> setVotingAdmissionForAllQuestions(Session session, boolean disableVoting) {
+	public <T> T getObjectFromId(String documentId, Class<T> klass) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public <T> T getObjectFromId(String documentId, Class<T> klass) {
+	public MotdList getMotdListForUser(final String username) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public MotdList getMotdListForUser(final String username) {
+	public MotdList createOrUpdateMotdList(MotdList motdlist) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public MotdList createOrUpdateMotdList(MotdList motdlist) {
+	public int[] deleteAllAnswersWithQuestions(List<Content> contents) {
 		// TODO Auto-generated method stub
-		return null;
+		return new int[0];
 	}
 }
diff --git a/src/test/java/de/thm/arsnova/entities/QuestionTest.java b/src/test/java/de/thm/arsnova/entities/ContentTest.java
similarity index 96%
rename from src/test/java/de/thm/arsnova/entities/QuestionTest.java
rename to src/test/java/de/thm/arsnova/entities/ContentTest.java
index 57569e6f2..fc6297c8d 100644
--- a/src/test/java/de/thm/arsnova/entities/QuestionTest.java
+++ b/src/test/java/de/thm/arsnova/entities/ContentTest.java
@@ -23,7 +23,7 @@ import java.util.ArrayList;
 
 import static org.junit.Assert.assertEquals;
 
-public class QuestionTest {
+public class ContentTest {
 
 	@SuppressWarnings("serial")
 	@Test
@@ -36,7 +36,7 @@ public class QuestionTest {
 		p2.setText("No");
 		p2.setCorrect(false);
 		p2.setValue(-10);
-		Question q = new Question();
+		Content q = new Content();
 		q.setQuestionType("yesno");
 		q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{
 			add(p1);
@@ -62,7 +62,7 @@ public class QuestionTest {
 		p2.setText("No");
 		p2.setCorrect(false);
 		p2.setValue(-10);
-		Question q = new Question();
+		Content q = new Content();
 		q.setAbstention(true);
 		q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{
 			add(p1);
@@ -89,7 +89,7 @@ public class QuestionTest {
 		p3.setText("Maybe");
 		p3.setCorrect(true);
 		p3.setValue(10);
-		Question q = new Question();
+		Content q = new Content();
 		q.setQuestionType("mc");
 		q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{
 			add(p1);
@@ -134,7 +134,7 @@ public class QuestionTest {
 		p4.setText("1;1");
 		p4.setCorrect(true);
 		p4.setValue(10);
-		Question q = new Question();
+		Content q = new Content();
 		q.setQuestionType("grid");
 		q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{
 			add(p1);
diff --git a/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java b/src/test/java/de/thm/arsnova/services/ContentServiceTest.java
similarity index 86%
rename from src/test/java/de/thm/arsnova/services/QuestionServiceTest.java
rename to src/test/java/de/thm/arsnova/services/ContentServiceTest.java
index daa1820e7..80ec08da1 100644
--- a/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java
+++ b/src/test/java/de/thm/arsnova/services/ContentServiceTest.java
@@ -22,7 +22,7 @@ import de.thm.arsnova.config.TestAppConfig;
 import de.thm.arsnova.config.TestSecurityConfig;
 import de.thm.arsnova.dao.StubDatabaseDao;
 import de.thm.arsnova.entities.Comment;
-import de.thm.arsnova.entities.Question;
+import de.thm.arsnova.entities.Content;
 import de.thm.arsnova.exceptions.NotFoundException;
 import org.junit.After;
 import org.junit.Before;
@@ -48,10 +48,10 @@ import static org.junit.Assert.*;
 @WebAppConfiguration
 @ContextConfiguration(classes = {AppConfig.class, TestAppConfig.class, TestSecurityConfig.class})
 @ActiveProfiles("test")
-public class QuestionServiceTest {
+public class ContentServiceTest {
 
 	@Autowired
-	private IQuestionService questionService;
+	private IContentService contentService;
 
 	@Autowired
 	private StubUserService userService;
@@ -83,19 +83,19 @@ public class QuestionServiceTest {
 	@Test(expected = AuthenticationCredentialsNotFoundException.class)
 	public void testShouldNotReturnQuestionsIfNotAuthenticated() {
 		setAuthenticated(false, "nobody");
-		questionService.getSkillQuestions("12345678");
+		contentService.getSkillQuestions("12345678");
 	}
 
 	@Test(expected = NotFoundException.class)
 	public void testShouldFindQuestionsForNonExistantSession() {
 		setAuthenticated(true, "ptsr00");
-		questionService.getSkillQuestions("00000000");
+		contentService.getSkillQuestions("00000000");
 	}
 
 	@Test
 	public void testShouldFindQuestions() {
 		setAuthenticated(true, "ptsr00");
-		assertEquals(1, questionService.getSkillQuestionCount("12345678"));
+		assertEquals(1, contentService.getSkillQuestionCount("12345678"));
 	}
 
 	@Test
@@ -107,7 +107,7 @@ public class QuestionServiceTest {
 		comment.setSessionId("12345678");
 		databaseDao.comment = comment;
 
-		questionService.readInterposedQuestion(comment.getId());
+		contentService.readInterposedQuestion(comment.getId());
 
 		assertTrue(comment.isRead());
 	}
@@ -122,7 +122,7 @@ public class QuestionServiceTest {
 		comment.setCreator("regular user");
 		databaseDao.comment = comment;
 
-		questionService.readInterposedQuestion(comment.getId());
+		contentService.readInterposedQuestion(comment.getId());
 
 		assertFalse(comment.isRead());
 	}
@@ -130,21 +130,21 @@ public class QuestionServiceTest {
 	@Test(expected = AccessDeniedException.class)
 	public void testShouldSaveQuestion() throws Exception{
 		setAuthenticated(true, "regular user");
-		final Question question = new Question();
-		question.setSessionKeyword("12345678");
-		question.setQuestionVariant("freetext");
-		questionService.saveQuestion(question);
+		final Content content = new Content();
+		content.setSessionKeyword("12345678");
+		content.setQuestionVariant("freetext");
+		contentService.saveQuestion(content);
 	}
 
 	@Test(expected = AccessDeniedException.class)
 	public void testShouldNotDeleteQuestion() throws Exception{
 		setAuthenticated(true, "otheruser");
-		questionService.deleteQuestion("a1a2a3a4a5a6a7a8a9a");
+		contentService.deleteQuestion("a1a2a3a4a5a6a7a8a9a");
 	}
 
 	@Test(expected = AccessDeniedException.class)
 	public void testShouldNotDeleteInterposedQuestion() throws Exception{
 		setAuthenticated(true, "otheruser");
-		questionService.deleteInterposedQuestion("a1a2a3a4a5a6a7a8a9a");
+		contentService.deleteInterposedQuestion("a1a2a3a4a5a6a7a8a9a");
 	}
 }
-- 
GitLab