diff --git a/README.md b/README.md
index d6d63e6add5e162dad365ed5cf30ba762b66b328..0e1831feda20e2492b7014bc6c7e43f388e883f9 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,51 @@
-# ARSnova 2
+# ARSnova
 
-This project really brings you *two* different versions of ARSnova: `arsnova-js` (ARSnova 2) and `arsnova-legacy-js` (ARSnova 1).
+ARSnova is a modern approach to Audience Response Systems (ARS). It is released under the GPLv3 license, and is offered as a Software as a Service free of charge. Head over to [arsnova.thm.de](https://arsnova.thm.de/) to see it in action.
 
-The first one is currently under heavy development and is not ready for production use. The second one is the tried-and-true ARSnova for your mobile device. However, `arsnova-legacy-js` will not receive any major updates and is nearing its end of life. It will be superseded by `arsnova-js`.
+![ARSnova](src/site/resources/showcase.png)
+
+ARSnova consists of two projects: the mobile client and the server. This repository contains the server code. You will find the client at thm-projects/arsnova-st2-js. However, you do not need to download both respositories in order to get started.
 
 ## Getting started
 
-Both versions of ARSnova will be deployed alongside each other, so you get to choose which one you would like to use. By default, `arsnova-legacy-js` is served via `index.html` and optionally via `developer.html`. If you want to get your hands dirty, you should open `dojo-index.html` and try out the redesigned ARSnova 2. It will work on any major browser instead of being for Webkit browsers only.
+This is the main repository. Almost all dependencies (including the mobile client) are managed for you by Maven.  The mobile client is served via `index.html`, and optionally via `developer.html`. 
 
-## Deployment
+## Configuration
 
 You will need to do some configuration work upfront.
 
- * Add a new directory "arsnova" in /etc and create a copy of arsnova.properties.example named arsnova.properties in this directory.
+ * Add a new directory "arsnova" in `/etc`, and create a copy of arsnova.properties.example named arsnova.properties in this directory.
  * Change settings to match your environment
 
-## Server configuration
+### Server
+
+In order to build up a full featured server installation containing ARSnova and CouchDB you have to install at least the following services:
 
-In order to build up a full featured server installation containing ARSnova2 and CouchDB you have to install at least the following services:
  * Apache Tomcat 7.0.29 (or newer)
- * Apache Webserver 2.2 or newer with buildin mod_proxy, mod_proxy_ajp and mod_proxy_http
+ * Apache Webserver 2.2 or newer with builtin modules `mod_proxy`, `mod_proxy_ajp` and `mod_proxy_http`
  * Apache CouchDB
  
-Make sure all services are installed. Next step is to configure the Apache Webserver. Find the configuration file or create a new one for use with a virtal host. This depends on your needs. At least you should have a configuration containing these settings:
+Make sure all services are installed. Next step is to configure the Apache Webserver. Find the configuration file or create a new one for use with a virtual host. This depends on your needs. At least you should have a configuration containing these settings:
+
+	<Location />
+		ProxyPass ajp://127.0.0.1:8009/
+		ProxyPassReverse ajp://127.0.0.1:8009/
+	</Location>
+
+All requests will be sent to your Apache Tomcat servlet container, using AJP running on port 8009.
+
+To enable the required Apache Webserver modules simply type:
 
-<Location /couchdb/>
- ProxyPass http://127.0.0.1:5984/
- ProxyPassReverse http://127.0.0.1:5984/
-</Location> 
-<Location />
- ProxyPass ajp://127.0.0.1:8009/
- ProxyPassReverse ajp://127.0.0.1:8009/
-</Location>
+	# a2enmod proxy
+	# a2enmod proxy_ajp
+	# a2enmod proxy_http
 
-This will redirect all requests for "/couchdb/..." to your Apache CouchDB server, running on port 5984.
-All other requests will be send to your Apache Tomcat servelt container, using AJP running on port 8009.
+The configuration is ready for development usage. Finally, you should (re)start all services. ARSnova is now listening on HTTP port 80.
 
-To enable the needed Apache Webserver simply type:
+### Database
 
-# a2enmod proxy
-# a2enmod proxy_ajp
-# a2enmod proxy_http
+We provide a script that will set up all database essentials. This "Setup Tool" is located at <https://scm.thm.de/arsnova/setuptool>. Make sure you have configured your database credentials inside the ARSnova configuration file: you will need to have the entries `couchdb.username` and `couchdb.password`.
 
-The configuration is ready for development usage. 
+## Credits
 
-Finally you should (re)start all services. ARSnova2 is now listening on HTTP port 80.
\ No newline at end of file
+ARSnova is powered by Technische Hochschule Mittelhessen - University of Applied Sciences.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b653c6c418112cafc414aaca90c52fd4c7224d9c..0e2110d6785daddb370a08d3884f7ba03b555165 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,12 +19,6 @@
 			<organization>Technische Hochschule Mittelhessen</organization>
 			<organizationUrl>http://www.thm.de</organizationUrl>
 		</developer>
-		<developer>
-			<name>Julian Hochstetter</name>
-			<organization>Technische Hochschule Mittelhessen</organization>
-			<organizationUrl>http://www.thm.de</organizationUrl>
-			<email>julian.hochstetter@mni.thm.de</email>
-		</developer>
 		<developer>
 			<name>Jan Kammer</name>
 			<organization>Technische Hochschule Mittelhessen</organization>
@@ -86,15 +80,19 @@
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-project-info-reports-plugin</artifactId>
-				<version>2.4</version>
-				<configuration></configuration>
+				<version>2.7</version>
+				<configuration>
+					<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
+					<dependencyDetailsEnabled>false</dependencyDetailsEnabled>
+				</configuration>
 			</plugin>
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-javadoc-plugin</artifactId>
-				<version>2.9</version>
+				<version>2.9.1</version>
 				<configuration></configuration>
 			</plugin>
+			<!--
 			<plugin>
 				<groupId>org.codehaus.mojo</groupId>
 				<artifactId>findbugs-maven-plugin</artifactId>
@@ -104,11 +102,12 @@
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-checkstyle-plugin</artifactId>
-				<version>2.9.1</version>
+				<version>2.10</version>
 				<configuration>
 					<configLocation>ARSnova-checkstyle-checker.xml</configLocation>
 				</configuration>
 			</plugin>
+			-->
 		</plugins>
 	</reporting>
 
@@ -136,18 +135,6 @@
 	</repositories>
 
 	<dependencies>
-		<dependency>
-			<groupId>de.thm.arsnova</groupId>
-			<artifactId>arsnova-js</artifactId>
-			<version>0.0.1-SNAPSHOT</version>
-			<type>war</type>
-		</dependency>
-		<dependency>
-			<groupId>de.thm.arsnova</groupId>
-			<artifactId>arsnova-legacy-js</artifactId>
-			<version>1.0.0-SNAPSHOT</version>
-			<type>war</type>
-		</dependency>
 		<dependency>
 			<groupId>de.thm.arsnova</groupId>
 			<artifactId>arsnova-light-js</artifactId>
@@ -271,15 +258,10 @@
 			<artifactId>spring-security-oauth-client</artifactId>
 			<version>1.0.0</version>
 		</dependency>
-		<dependency>
-			<groupId>io.netty</groupId>
-			<artifactId>netty</artifactId>
-			<version>3.5.7.Final</version>
-		</dependency>
 		<dependency>
 			<groupId>com.corundumstudio.socketio</groupId>
 			<artifactId>netty-socketio</artifactId>
-			<version>1.0.0-SNAPSHOT</version>
+			<version>1.0.1</version>
 		</dependency>
 		<dependency>
 			<groupId>javax.inject</groupId>
@@ -290,7 +272,7 @@
 		<dependency>
 			<groupId>com.fasterxml.jackson.core</groupId>
 			<artifactId>jackson-databind</artifactId>
-			<version>2.1.0</version>
+			<version>2.2.2</version>
 		</dependency>
 		<dependency>
 			<groupId>org.springframework</groupId>
@@ -412,7 +394,7 @@
 			<plugin>
 				<groupId>org.codehaus.mojo</groupId>
 				<artifactId>cobertura-maven-plugin</artifactId>
-				<version>2.5.1</version>
+				<version>2.5.2</version>
 				<configuration>
 					<formats>
 						<format>xml</format>
@@ -428,7 +410,7 @@
 			<plugin>
 				<groupId>org.apache.maven.plugins</groupId>
 				<artifactId>maven-checkstyle-plugin</artifactId>
-				<version>2.9.1</version>
+				<version>2.10</version>
 				<configuration>
 					<configLocation>ARSnova-checkstyle-checker.xml</configLocation>
 				</configuration>
diff --git a/src/main/java/de/thm/arsnova/aop/UserSessionAspect.java b/src/main/java/de/thm/arsnova/aop/UserSessionAspect.java
index 1cc0eea7ada5747ccd0d0894e44a1bfa0edf25be..56f1158cd966511b8efb2b74a0b0cb3163ef079d 100644
--- a/src/main/java/de/thm/arsnova/aop/UserSessionAspect.java
+++ b/src/main/java/de/thm/arsnova/aop/UserSessionAspect.java
@@ -8,8 +8,6 @@ import org.aspectj.lang.annotation.Aspect;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import de.thm.arsnova.entities.Session;
-import de.thm.arsnova.events.Publisher;
-import de.thm.arsnova.services.IUserService;
 import de.thm.arsnova.services.UserSessionService;
 
 @Aspect
@@ -18,12 +16,6 @@ public class UserSessionAspect {
 	@Autowired
 	private UserSessionService userSessionService;
 
-	@Autowired
-	private IUserService userService;
-
-	@Autowired
-	private Publisher publisher;
-
 	/** Sets current user and ARSnova session in session scoped UserSessionService 
 	 * 
 	 * @param jp
@@ -35,7 +27,6 @@ public class UserSessionAspect {
 		returning="session"
 	)
 	public final void joinSessionAdvice(final JoinPoint jp, final String keyword, final Session session) {
-		userSessionService.setUser(userService.getCurrentUser());
 		userSessionService.setSession(session);
 	}
 
@@ -46,13 +37,14 @@ public class UserSessionAspect {
 	 * @param socketId
 	 * @param session
 	 */
+	/* FIXME This is not working because of scoping problems
 	@AfterReturning(
 		pointcut="execution(public * de.thm.arsnova.services.SessionService.joinSession(..)) && args(keyword, socketId)",
 		returning="session"
 	)
 	public final void joinSessionAdviceWithWebsocket(final JoinPoint jp, final String keyword, final UUID socketId, final Session session) {
-		userSessionService.setUser(userService.getCurrentUser());
 		userSessionService.setSession(session);
 		userSessionService.setSocketId(socketId);
 	}
+	*/
 }
diff --git a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
index 5fa519c1c8b76807a5d66d2fac8f025a5e3b93d1..fec00c97a4bb5dbe430e1b7a7b96179d81c1fdfd 100644
--- a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
+++ b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java
@@ -146,6 +146,12 @@ public class LecturerQuestionController extends AbstractController {
 		return questions;
 	}
 
+	@RequestMapping(value = { "/" }, method = RequestMethod.DELETE)
+	@ResponseBody
+	public final void deleteSkillQuestions(@RequestParam final String sessionkey, final HttpServletResponse response) {
+		this.questionService.deleteAllQuestions(sessionkey);
+	}
+
 	@RequestMapping(value = "/count", method = RequestMethod.GET)
 	@ResponseBody
 	public final int getSkillQuestionCount(@RequestParam final String sessionkey, final HttpServletResponse response) {
@@ -154,22 +160,6 @@ public class LecturerQuestionController extends AbstractController {
 		return questionService.getSkillQuestionCount(sessionkey);
 	}
 
-	/*
-	 * TODO is this used anywhere?
-	@RequestMapping(value = "/ids", method = RequestMethod.GET)
-	@ResponseBody
-	public final List<String> getQuestionIds(
-			@RequestParam final String sessionkey,
-			final HttpServletResponse response
-	) {
-		List<String> questions = questionService.getQuestionIds(sessionkey);
-		if (questions == null || questions.isEmpty()) {
-			throw new NotFoundException();
-		}
-		return questions;
-	}
-	*/
-
 	@RequestMapping(value = "/{questionId}", method = RequestMethod.DELETE)
 	@ResponseBody
 	public final void deleteAnswersAndQuestion(
diff --git a/src/main/java/de/thm/arsnova/controller/LoginController.java b/src/main/java/de/thm/arsnova/controller/LoginController.java
index a15924681869865f11db1a7b251e72ce837020fb..2ef3ee7982aa2550710e1e1a56dc603fc668b18a 100644
--- a/src/main/java/de/thm/arsnova/controller/LoginController.java
+++ b/src/main/java/de/thm/arsnova/controller/LoginController.java
@@ -58,6 +58,7 @@ import org.springframework.web.servlet.view.RedirectView;
 
 import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
+import de.thm.arsnova.exceptions.UnauthorizedException;
 import de.thm.arsnova.services.IUserService;
 import de.thm.arsnova.services.UserSessionService;
 
@@ -97,9 +98,12 @@ public class LoginController extends AbstractController {
 			@RequestParam(value = "referer", required = false) final String forcedReferer,
 			@RequestParam(value = "successurl", required = false) final String successUrl,
 			@RequestParam(value = "failureurl", required = false) final String failureUrl,
+			@RequestParam(value = "role", required = false) UserSessionService.Role role,
 			final HttpServletRequest request,
 			final HttpServletResponse response
 	) throws IOException, ServletException {
+		userSessionService.setRole(role);
+		
 		String referer = request.getHeader("referer");
 		if (null != forcedReferer && null != referer && !UrlUtils.isAbsoluteUrl(referer)) {
 			/* Use a url from a request parameter as referer as long as the url is not absolute (to prevent
@@ -117,17 +121,19 @@ public class LoginController extends AbstractController {
 			null == failureUrl ? referer : failureUrl
 		);
 
+		View result = null;
+		
 		if ("cas".equals(type)) {
 			casEntryPoint.commence(request, response, null);
 		} else if ("twitter".equals(type)) {
 			String authUrl = twitterProvider.getAuthorizationUrl(new HttpUserSession(request));
-			return new RedirectView(authUrl);
+			result = new RedirectView(authUrl);
 		} else if ("facebook".equals(type)) {
 			String authUrl = facebookProvider.getAuthorizationUrl(new HttpUserSession(request));
-			return new RedirectView(authUrl);
+			result = new RedirectView(authUrl);
 		} else if ("google".equals(type)) {
 			String authUrl = googleProvider.getAuthorizationUrl(new HttpUserSession(request));
-			return new RedirectView(authUrl);
+			result = new RedirectView(authUrl);
 		} else if ("guest".equals(type)) {
 			List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
 			authorities.add(new SimpleGrantedAuthority("ROLE_GUEST"));
@@ -146,9 +152,10 @@ public class LoginController extends AbstractController {
 			SecurityContextHolder.getContext().setAuthentication(token);
 			request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
 					SecurityContextHolder.getContext());
-			return new RedirectView(null == successUrl ? referer + "#auth/checkLogin" : successUrl);
+			result = new RedirectView(null == successUrl ? referer + "#auth/checkLogin" : successUrl);
 		}
-		return null;
+				
+		return result;
 	}
 	
 	@RequestMapping(value = { "/auth/ldaplogin" }, method = RequestMethod.POST)
@@ -195,6 +202,7 @@ public class LoginController extends AbstractController {
 	@RequestMapping(value = { "/auth/", "/whoami" }, method = RequestMethod.GET)
 	@ResponseBody
 	public final User whoami() {
+		userSessionService.setUser(userService.getCurrentUser());		
 		return userService.getCurrentUser();
 	}
 
@@ -219,12 +227,30 @@ public class LoginController extends AbstractController {
 	@RequestMapping(value = { "/test/me" }, method = RequestMethod.GET)
 	@ResponseBody
 	public final User me() {
-		return userSessionService.getUser();
+		User me = userSessionService.getUser();
+		if (me == null) {
+			throw new UnauthorizedException();
+		}
+		return me;
 	}
 
 	@RequestMapping(value = { "/test/mysession" }, method = RequestMethod.GET)
 	@ResponseBody
 	public final Session mysession() {
-		return userSessionService.getSession();
+		Session mysession = userSessionService.getSession();
+		if (mysession == null) {
+			throw new UnauthorizedException();
+		}
+		return mysession;
+	}
+
+	@RequestMapping(value = { "/test/myrole" }, method = RequestMethod.GET)
+	@ResponseBody
+	public final UserSessionService.Role myrole() {
+		UserSessionService.Role myrole = userSessionService.getRole();
+		if (myrole == null) {
+			throw new UnauthorizedException();
+		}
+		return myrole;
 	}
 }
diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
index fa2345703d0b483d4c0e5b6d9833c8b6489f86fa..34aff2eff911121fc0c60b21b98807d34ea80c42 100644
--- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
+++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java
@@ -25,19 +25,13 @@ import java.net.URLEncoder;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 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.JSONException;
 import net.sf.json.JSONObject;
 import net.sf.json.util.JSONUtils;
 
@@ -56,7 +50,6 @@ import com.fourspaces.couchdb.ViewResults;
 
 import de.thm.arsnova.connector.model.Course;
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Feedback;
 import de.thm.arsnova.entities.FoodVote;
 import de.thm.arsnova.entities.InterposedQuestion;
 import de.thm.arsnova.entities.InterposedReadingCount;
@@ -114,59 +107,6 @@ public class CouchDBDao implements IDatabaseDao {
 		this.userService = service;
 	}
 
-	/**
-	 * This method cleans up old feedback votes at the scheduled interval.
-	 */
-	@Override
-	public final void cleanFeedbackVotes(final int cleanupFeedbackDelay) {
-		final long timelimitInMillis = 60000 * (long) cleanupFeedbackDelay;
-		final long maxAllowedTimeInMillis = System.currentTimeMillis() - timelimitInMillis;
-
-		Map<String, Set<String>> affectedUsers = new HashMap<String, Set<String>>();
-		Set<String> allAffectedSessions = new HashSet<String>();
-
-		List<Document> results = findFeedbackForDeletion(maxAllowedTimeInMillis);
-		for (Document d : results) {
-			try {
-				// Read the required document data
-				Document feedback = this.getDatabase().getDocument(d.getId());
-				String arsInternalSessionId = feedback.getString("sessionId");
-				String user = feedback.getString("user");
-
-				// Store user and session data for later. We need this to
-				// communicate the changes back to the users.
-				Set<String> affectedArsSessions = affectedUsers.get(user);
-				if (affectedArsSessions == null) {
-					affectedArsSessions = new HashSet<String>();
-				}
-				affectedArsSessions.add(getSessionKeyword(arsInternalSessionId));
-				affectedUsers.put(user, affectedArsSessions);
-				allAffectedSessions.addAll(affectedArsSessions);
-
-				this.database.deleteDocument(feedback);
-				LOGGER.debug("Cleaning up Feedback document " + d.getId());
-			} catch (IOException e) {
-				LOGGER.error("Could not delete Feedback document " + d.getId());
-			} catch (JSONException e) {
-				LOGGER.error(
-						"Could not delete Feedback document {}, error is: {} ",
-						new Object[] {d.getId(), e}
-				);
-			}
-		}
-		if (!results.isEmpty()) {
-			feedbackService.broadcastFeedbackChanges(affectedUsers, allAffectedSessions);
-		}
-	}
-
-	private List<Document> findFeedbackForDeletion(final long maxAllowedTimeInMillis) {
-		View cleanupFeedbackView = new View("understanding/cleanup");
-		cleanupFeedbackView.setStartKey("null");
-		cleanupFeedbackView.setEndKey(String.valueOf(maxAllowedTimeInMillis));
-		ViewResults feedbackForCleanup = this.getDatabase().view(cleanupFeedbackView);
-		return feedbackForCleanup.getResults();
-	}
-
 	@Override
 	public final Session getSession(final String keyword) {
 		Session result = this.getSessionFromKeyword(keyword);
@@ -182,28 +122,24 @@ public class CouchDBDao implements IDatabaseDao {
 
 	@Override
 	public final List<Session> getMySessions(final User user) {
-		try {
-			View view = new View("session/by_creator");
-			view.setStartKey("[" + URLEncoder.encode("\"" + user.getUsername() + "\"", "UTF-8") + "]");
-			view.setEndKey("[" + URLEncoder.encode("\"" + user.getUsername() + "\",{}", "UTF-8") + "]");
+		NovaView view = new NovaView("session/by_creator");
+		view.setStartKeyArray(user.getUsername());
+		view.setEndKeyArray(user.getUsername(), "{}");
 
-			ViewResults sessions = this.getDatabase().view(view);
+		ViewResults sessions = this.getDatabase().view(view);
 
-			List<Session> result = new ArrayList<Session>();
-			for (Document d : sessions.getResults()) {
-				Session session = (Session) JSONObject.toBean(
-						d.getJSONObject().getJSONObject("value"),
-						Session.class
-				);
-				session.setCreator(d.getJSONObject().getJSONArray("key").getString(0));
-				session.setName(d.getJSONObject().getJSONArray("key").getString(1));
-				session.set_id(d.getId());
-				result.add(session);
-			}
-			return result;
-		} catch (UnsupportedEncodingException e) {
-			return null;
+		List<Session> result = new ArrayList<Session>();
+		for (Document d : sessions.getResults()) {
+			Session session = (Session) JSONObject.toBean(
+					d.getJSONObject().getJSONObject("value"),
+					Session.class
+			);
+			session.setCreator(d.getJSONObject().getJSONArray("key").getString(0));
+			session.setName(d.getJSONObject().getJSONArray("key").getString(1));
+			session.set_id(d.getId());
+			result.add(session);
 		}
+		return result;
 	}
 
 	@Override
@@ -214,20 +150,22 @@ public class CouchDBDao implements IDatabaseDao {
 		}
 
 		User user = this.userService.getCurrentUser();
-		View view = null;
+		NovaView view = null;
 
 		try {
+			String viewName;
 			if (session.getCreator().equals(user.getUsername())) {
-				view = new View("skill_question/by_session_sorted_by_subject_and_text");
+				viewName = "skill_question/by_session_sorted_by_subject_and_text";
 			} else {
 				if (user.getType().equals(User.THM)) {
-					view = new View("skill_question/by_session_for_thm_full");
+					viewName = "skill_question/by_session_for_thm_full";
 				} else {
-					view = new View("skill_question/by_session_for_all_full");
+					viewName = "skill_question/by_session_for_all_full";
 				}
 			}
-			view.setStartKey("[" + URLEncoder.encode("\"" + session.get_id() + "\"", "UTF-8") + "]");
-			view.setEndKey("[" + URLEncoder.encode("\"" + session.get_id() + "\",{}", "UTF-8") + "]");
+			view = new NovaView(viewName);
+			view.setStartKeyArray(session.get_id());
+			view.setEndKeyArray(session.get_id(), "{}");
 
 			ViewResults questions = this.getDatabase().view(view);
 			if (questions == null || questions.isEmpty()) {
@@ -258,8 +196,6 @@ public class CouchDBDao implements IDatabaseDao {
 			}
 
 			return result;
-		} catch (UnsupportedEncodingException e) {
-			return null;
 		} catch (IOException e) {
 			return null;
 		}
@@ -267,63 +203,49 @@ public class CouchDBDao implements IDatabaseDao {
 
 	@Override
 	public final int getSkillQuestionCount(final Session session) {
-		try {
-			View view = new View("skill_question/count_by_session");
-			view.setKey(URLEncoder.encode("\"" + session.get_id() + "\"", "UTF-8"));
-			ViewResults results = this.getDatabase().view(view);
-
-			if (results.getJSONArray("rows").optJSONObject(0) == null) {
-				return 0;
-			}
-
-			return results.getJSONArray("rows").optJSONObject(0).optInt("value");
+		NovaView view = new NovaView("skill_question/count_by_session");
+		view.setKey(session.get_id());
+		ViewResults results = this.getDatabase().view(view);
 
-		} catch (UnsupportedEncodingException e) {
+		if (results.getJSONArray("rows").optJSONObject(0) == null) {
 			return 0;
 		}
+
+		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
 	}
 
 	@Override
 	public final Session getSessionFromKeyword(final String keyword) {
-		try {
-			View view = new View("session/by_keyword");
-			view.setKey(URLEncoder.encode("\"" + keyword + "\"", "UTF-8"));
-			ViewResults results = this.getDatabase().view(view);
+		NovaView view = new NovaView("session/by_keyword");
+		view.setKey(keyword);
+		ViewResults results = this.getDatabase().view(view);
 
-			if (results.getJSONArray("rows").optJSONObject(0) == null) {
-				return null;
-			}
-			return (Session) JSONObject.toBean(
-					results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
-					Session.class
-			);
-		} catch (UnsupportedEncodingException e) {
+		if (results.getJSONArray("rows").optJSONObject(0) == null) {
 			return null;
 		}
+		return (Session) JSONObject.toBean(
+				results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
+				Session.class
+		);
 	}
 
 	@Override
 	public final Session getSessionFromId(final String sessionId) {
-		try {
-			View view = new View("session/by_id");
-			view.setKey(URLEncoder.encode("\"" + sessionId + "\"", "UTF-8"));
-			ViewResults results = this.getDatabase().view(view);
+		View view = new View("session/by_id");
+		view.setKey(sessionId);
+		ViewResults results = this.getDatabase().view(view);
 
-			if (results.getJSONArray("rows").optJSONObject(0) == null) {
-				return null;
-			}
-			return (Session) JSONObject.toBean(
-					results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
-					Session.class
-			);
-		} catch (UnsupportedEncodingException e) {
+		if (results.getJSONArray("rows").optJSONObject(0) == null) {
 			return null;
 		}
+		return (Session) JSONObject.toBean(
+				results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
+				Session.class
+		);
 	}
 
 	@Override
 	public final Session saveSession(final Session session) {
-
 		Document sessionDocument = new Document();
 		sessionDocument.put("type", "session");
 		sessionDocument.put("name", session.getName());
@@ -338,160 +260,9 @@ public class CouchDBDao implements IDatabaseDao {
 		} catch (IOException e) {
 			return null;
 		}
-
 		return this.getSession(sessionDocument.getString("keyword"));
 	}
 
-	@Override
-	public final Feedback getFeedback(final String keyword) {
-		String sessionId = this.getSessionId(keyword);
-		if (sessionId == null) {
-			throw new NotFoundException();
-		}
-		View view = new View("understanding/by_session");
-		view.setGroup(true);
-		view.setStartKey(URLEncoder.encode("[\"" + sessionId + "\"]"));
-		view.setEndKey(URLEncoder.encode("[\"" + sessionId + "\",{}]"));
-		ViewResults results = this.getDatabase().view(view);
-
-		LOGGER.debug("Feedback: {}", results.getJSONArray("rows"));
-
-		return this.createFeedbackObject(results);
-	}
-
-	private Feedback createFeedbackObject(final ViewResults results) {
-		int[] values = {0, 0, 0, 0};
-		JSONArray rows = results.getJSONArray("rows");
-
-		try {
-			for (int i = Feedback.MIN_FEEDBACK_TYPE; i <= Feedback.MAX_FEEDBACK_TYPE; i++) {
-				String key = rows.optJSONObject(i).optJSONArray("key").getString(1);
-				JSONObject feedback = rows.optJSONObject(i);
-
-				if (key.equals("Bitte schneller")) {
-					values[Feedback.FEEDBACK_FASTER] = feedback.getInt("value");
-				}
-				if (key.equals("Kann folgen")) {
-					values[Feedback.FEEDBACK_OK] = feedback.getInt("value");
-				}
-				if (key.equals("Zu schnell")) {
-					values[Feedback.FEEDBACK_SLOWER] = feedback.getInt("value");
-				}
-				if (key.equals("Nicht mehr dabei")) {
-					values[Feedback.FEEDBACK_AWAY] = feedback.getInt("value");
-				}
-			}
-		} catch (Exception e) {
-			return new Feedback(
-					values[Feedback.FEEDBACK_FASTER],
-					values[Feedback.FEEDBACK_OK],
-					values[Feedback.FEEDBACK_SLOWER],
-					values[Feedback.FEEDBACK_AWAY]
-			);
-		}
-		return new Feedback(
-				values[Feedback.FEEDBACK_FASTER],
-				values[Feedback.FEEDBACK_OK],
-				values[Feedback.FEEDBACK_SLOWER],
-				values[Feedback.FEEDBACK_AWAY]
-		);
-	}
-
-	@Override
-	public final boolean saveFeedback(
-			final String keyword,
-			final int value,
-			final de.thm.arsnova.entities.User user
-	) {
-		String sessionId = this.getSessionId(keyword);
-		if (sessionId == null) {
-			return false;
-		}
-		if (!(value >= Feedback.MIN_FEEDBACK_TYPE && value <= Feedback.MAX_FEEDBACK_TYPE)) {
-			return false;
-		}
-
-		Document feedback = new Document();
-		List<Document> postedFeedback = findPreviousFeedback(sessionId, user);
-
-		// Feedback can only be posted once. If there already is some feedback,
-		// we need to update it.
-		if (!postedFeedback.isEmpty()) {
-			for (Document f : postedFeedback) {
-				// Use the first found feedback and update value and timestamp
-				try {
-					feedback = this.getDatabase().getDocument(f.getId());
-					feedback.put("value", feedbackValueToString(value));
-					feedback.put("timestamp", System.currentTimeMillis());
-				} catch (IOException e) {
-					return false;
-				}
-				break;
-			}
-		} else {
-			feedback.put("type", "understanding");
-			feedback.put("user", user.getUsername());
-			feedback.put("sessionId", sessionId);
-			feedback.put("timestamp", System.currentTimeMillis());
-			feedback.put("value", feedbackValueToString(value));
-		}
-
-		try {
-			this.getDatabase().saveDocument(feedback);
-		} catch (IOException e) {
-			return false;
-		}
-
-		return true;
-	}
-
-	private List<Document> findPreviousFeedback(final String sessionId, final de.thm.arsnova.entities.User user) {
-		View view = new View("understanding/by_user");
-		try {
-			view.setKey(
-					URLEncoder.encode(
-							"[\"" + sessionId + "\",\"" + user.getUsername() + "\"]",
-							"UTF-8"
-					)
-			);
-		} catch (UnsupportedEncodingException e) {
-			return Collections.<Document> emptyList();
-		}
-		ViewResults results = this.getDatabase().view(view);
-		return results.getResults();
-	}
-
-	private String feedbackValueToString(final int value) {
-		switch (value) {
-		case Feedback.FEEDBACK_FASTER:
-			return "Bitte schneller";
-		case Feedback.FEEDBACK_OK:
-			return "Kann folgen";
-		case Feedback.FEEDBACK_SLOWER:
-			return "Zu schnell";
-		case Feedback.FEEDBACK_AWAY:
-			return "Nicht mehr dabei";
-		default:
-			return null;
-		}
-	}
-
-	private int feedbackValueFromString(final String value) {
-		if (value.equals("Bitte schneller")) {
-			return Feedback.FEEDBACK_FASTER;
-		}
-		if (value.equals("Kann folgen")) {
-			return Feedback.FEEDBACK_OK;
-		}
-		if (value.equals("Zu schnell")) {
-			return Feedback.FEEDBACK_AWAY;
-		}
-		if (value.equals("Nicht mehr dabei")) {
-			return Feedback.FEEDBACK_AWAY;
-		}
-		return Integer.MIN_VALUE;
-	}
-
 	@Override
 	@Transactional(isolation = Isolation.READ_COMMITTED)
 	public final boolean sessionKeyAvailable(final String keyword) {
@@ -501,16 +272,6 @@ public class CouchDBDao implements IDatabaseDao {
 		return !results.containsKey(keyword);
 	}
 
-	private String getSessionId(final String keyword) {
-		View view = new View("session/by_keyword");
-		view.setKey(URLEncoder.encode("\"" + keyword + "\""));
-		ViewResults results = this.getDatabase().view(view);
-		if (results.getJSONArray("rows").optJSONObject(0) == null) {
-			return null;
-		}
-		return results.getJSONArray("rows").optJSONObject(0).optJSONObject("value").getString("_id");
-	}
-
 	private String getSessionKeyword(final String internalSessionId) throws IOException {
 		Document document = this.getDatabase().getDocument(internalSessionId);
 		if (document.has("keyword")) {
@@ -626,8 +387,8 @@ public class CouchDBDao implements IDatabaseDao {
 	@Override
 	public final Question getQuestion(final String id) {
 		try {
-			View view = new View("skill_question/by_id");
-			view.setKey(URLEncoder.encode("\"" + id + "\"", "UTF-8"));
+			NovaView view = new NovaView("skill_question/by_id");
+			view.setKey(id);
 			ViewResults results = this.getDatabase().view(view);
 
 			if (results.getJSONArray("rows").optJSONObject(0) == null) {
@@ -656,8 +417,8 @@ public class CouchDBDao implements IDatabaseDao {
 	@Override
 	public final LoggedIn registerAsOnlineUser(final User user, final Session session) {
 		try {
-			View view = new View("logged_in/all");
-			view.setKey(URLEncoder.encode("\"" + user.getUsername() + "\"", "UTF-8"));
+			NovaView view = new NovaView("logged_in/all");
+			view.setKey(user.getUsername());
 			ViewResults results = this.getDatabase().view(view);
 
 			LoggedIn loggedIn = new LoggedIn();
@@ -696,8 +457,6 @@ public class CouchDBDao implements IDatabaseDao {
 				l.setVisitedSessions(new ArrayList<VisitedSession>(visitedSessions));
 			}
 			return l;
-		} catch (UnsupportedEncodingException e) {
-			return null;
 		} catch (IOException e) {
 			return null;
 		}
@@ -715,61 +474,26 @@ public class CouchDBDao implements IDatabaseDao {
 		}
 	}
 
-	@Override
-	public final Integer getMyFeedback(final String keyword, final User user) {
-		try {
-			String sessionId = this.getSessionId(keyword);
-			if (sessionId == null) {
-				throw new NotFoundException();
-			}
-
-			View view = new View("understanding/by_user");
-			view.setKey(
-					URLEncoder.encode(
-							"[\"" + sessionId + "\", \"" + user.getUsername() + "\"]",
-							"UTF-8"
-					)
-			);
-			ViewResults results = this.getDatabase().view(view);
-			JSONArray rows = results.getJSONArray("rows");
-
-			if (rows.size() == 0) {
-				return null;
-			}
-
-			JSONObject json = rows.optJSONObject(0).optJSONObject("value");
-			return this.feedbackValueFromString(json.getString("value"));
-		} catch (UnsupportedEncodingException e) {
-			return null;
-		}
-	}
-
 	@Override
 	public final List<String> getQuestionIds(final Session session, final User user) {
-		View view;
+		NovaView view;
 		if (user.getType().equals("thm")) {
-			view = new View("skill_question/by_session_only_id_for_thm");
+			view = new NovaView("skill_question/by_session_only_id_for_thm");
 		} else {
-			view = new View("skill_question/by_session_only_id_for_all");
+			view = new NovaView("skill_question/by_session_only_id_for_all");
 		}
 
-		try {
-			view.setKey(URLEncoder.encode("\"" + session.get_id() + "\"", "UTF-8"));
-			ViewResults results = this.getDatabase().view(view);
-			if (results.getResults().size() == 0) {
-				return new ArrayList<String>();
-			}
-
-			List<String> ids = new ArrayList<String>();
-			for (Document d : results.getResults()) {
-				ids.add(d.getId());
-			}
-			return ids;
+		view.setKey(session.get_id());
+		ViewResults results = this.getDatabase().view(view);
+		if (results.getResults().size() == 0) {
+			return new ArrayList<String>();
+		}
 
-		} catch (IOException e) {
-			LOGGER.error("Could not get list of question ids of session {}", session.getKeyword());
+		List<String> ids = new ArrayList<String>();
+		for (Document d : results.getResults()) {
+			ids.add(d.getId());
 		}
-		return new ArrayList<String>();
+		return ids;
 	}
 
 	@Override
@@ -781,6 +505,20 @@ public class CouchDBDao implements IDatabaseDao {
 			LOGGER.error("IOException: Could not delete question {}", question.get_id());
 		}
 	}
+	
+	@Override
+	public final void deleteAllQuestionsWithAnswers(Session session) {
+		NovaView view = new NovaView("skill_question/by_session");
+		view.setStartKeyArray(session.get_id());
+		view.setEndKey(session.get_id(), "{}");
+		ViewResults results = this.getDatabase().view(view);
+		
+		for (Document d : results.getResults()) {
+			Question q = new Question();
+			q.set_id(d.getId());
+			this.deleteQuestionWithAnswers(q);
+		}
+	}
 
 	private void deleteDocument(final String documentId) throws IOException {
 		Document d = this.getDatabase().getDocument(documentId);
@@ -790,8 +528,8 @@ public class CouchDBDao implements IDatabaseDao {
 	@Override
 	public final void deleteAnswers(final Question question) {
 		try {
-			View view = new View("answer/cleanup");
-			view.setKey(URLEncoder.encode("\"" + question.get_id() + "\"", "UTF-8"));
+			NovaView view = new NovaView("answer/cleanup");
+			view.setKey(question.get_id());
 			ViewResults results = this.getDatabase().view(view);
 
 			for (Document d : results.getResults()) {
@@ -804,35 +542,23 @@ public class CouchDBDao implements IDatabaseDao {
 
 	@Override
 	public final List<String> getUnAnsweredQuestionIds(final Session session, final User user) {
-		try {
-			View view = new View("answer/by_user");
-			view.setKey(
-					"[" + URLEncoder.encode(
-							"\"" + user.getUsername() + "\",\"" + session.get_id() + "\"",
-							"UTF-8"
-					)
-					+ "]"
-			);
-			ViewResults anseweredQuestions = this.getDatabase().view(view);
+		NovaView view = new NovaView("answer/by_user");
+		view.setKey(user.getUsername(), session.get_id());
+		ViewResults anseweredQuestions = this.getDatabase().view(view);
 
-			List<String> answered = new ArrayList<String>();
-			for (Document d : anseweredQuestions.getResults()) {
-				answered.add(d.getString("value"));
-			}
+		List<String> answered = new ArrayList<String>();
+		for (Document d : anseweredQuestions.getResults()) {
+			answered.add(d.getString("value"));
+		}
 
-			List<String> questions = this.getQuestionIds(session, user);
-			List<String> unanswered = new ArrayList<String>();
-			for (String questionId : questions) {
-				if (!answered.contains(questionId)) {
-					unanswered.add(questionId);
-				}
+		List<String> questions = this.getQuestionIds(session, user);
+		List<String> unanswered = new ArrayList<String>();
+		for (String questionId : questions) {
+			if (!answered.contains(questionId)) {
+				unanswered.add(questionId);
 			}
-			return unanswered;
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving unansweredquestions", e);
 		}
-
-		return null;
+		return unanswered;
 	}
 
 	@Override
@@ -842,108 +568,73 @@ public class CouchDBDao implements IDatabaseDao {
 			throw new UnauthorizedException();
 		}
 
-		try {
-			View view = new View("answer/by_question_and_user_and_piround");
-			if (2 == piRound) {
-				view.setKey(
-						"[" + URLEncoder.encode(
-								"\"" + questionId + "\",\"" + user.getUsername() + "\",2",
-								"UTF-8"
-						)
-						+ "]"
-				);
-			} else {
-				/* needed for legacy questions whose piRound property has not been set */
-				view.setStartKey(
-						"[" + URLEncoder.encode(
-								"\"" + questionId + "\",\"" + user.getUsername() + "\"",
-								"UTF-8"
-						)
-						+ "]"
-				);
-				view.setEndKey(
-						"[" + URLEncoder.encode(
-								"\"" + questionId + "\",\"" + user.getUsername() + "\",1",
-								"UTF-8"
-						)
-						+ "]"
-				);
-			}
-			ViewResults results = this.getDatabase().view(view);
-			if (results.getResults().isEmpty()) {
-				return null;
-			}
-			return (Answer) JSONObject.toBean(
-					results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
-					Answer.class
-			);
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error(
-					"Error while retrieving answer for user {} and question {}, {}",
-					new Object[] {user,	questionId, e }
-			);
+		NovaView view = new NovaView("answer/by_question_and_user_and_piround");
+		if (2 == piRound) {
+			view.setKey(questionId, user.getUsername(), "2");
+		} else {
+			/* needed for legacy questions whose piRound property has not been set */
+			view.setStartKey(questionId, user.getUsername());
+			view.setEndKey(questionId, user.getUsername(), "1");
 		}
-
-		return null;
+		ViewResults results = this.getDatabase().view(view);
+		if (results.getResults().isEmpty()) {
+			return null;
+		}
+		return (Answer) JSONObject.toBean(
+				results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
+				Answer.class
+		);
 	}
 
 	@Override
 	public final List<Answer> getAnswers(final String questionId, int piRound) {
-		try {
-			View view = new View("skill_question/count_answers_by_question_and_piround");
-			if (2 == piRound) {
-				view.setStartKey("[" + URLEncoder.encode(
-					"\"" + questionId + "\",2",
-					"UTF-8"
-				) + "]");
-				view.setEndKey("[" + URLEncoder.encode(
-					"\"" + questionId + "\",2,{}",
-					"UTF-8"
-				) + "]");
-			} else {
-				/* needed for legacy questions whose piRound property has not been set */
-				view.setStartKey("[" + URLEncoder.encode(
-					"\"" + questionId + "\"",
-					"UTF-8"
-				) + "]");
-				view.setEndKey("[" + URLEncoder.encode(
-					"\"" + questionId + "\",1,{}",
-					"UTF-8"
-				) + "]");
-			}
-			view.setGroup(true);
-			ViewResults results = this.getDatabase().view(view);
-			List<Answer> answers = new ArrayList<Answer>();
-			for (Document d : results.getResults()) {
-				Answer a = new Answer();
-				a.setAnswerCount(d.getInt("value"));
-				a.setQuestionId(d.getJSONObject().getJSONArray("key").getString(0));
-				a.setPiRound(piRound);
-				a.setAnswerText(d.getJSONObject().getJSONArray("key").getString(2));
-				answers.add(a);
-			}
-			return answers;
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving answers", e);
+		NovaView view = new NovaView("skill_question/count_answers_by_question_and_piround");
+		if (2 == piRound) {
+			view.setStartKey(questionId, "2");
+			view.setEndKey(questionId, "2", "{}");
+		} else {
+			/* needed for legacy questions whose piRound property has not been set */
+			view.setStartKeyArray(questionId);
+			view.setEndKeyArray(questionId, "1", "{}");
 		}
-		return null;
+		view.setGroup(true);
+		ViewResults results = this.getDatabase().view(view);
+		int abstentionCount = this.getAbstentionAnswerCount(questionId);
+		List<Answer> answers = new ArrayList<Answer>();
+		for (Document d : results.getResults()) {
+			Answer a = new Answer();
+			a.setAnswerCount(d.getInt("value"));
+			a.setAbstentionCount(abstentionCount);
+			a.setQuestionId(d.getJSONObject().getJSONArray("key").getString(0));
+			a.setPiRound(piRound);
+			String answerText = d.getJSONObject().getJSONArray("key").getString(2);
+			a.setAnswerText(answerText == "null" ? null : answerText);
+			answers.add(a);
+		}
+		return answers;
+	}
+
+	private int getAbstentionAnswerCount(final String questionId) {
+		NovaView view = new NovaView("skill_question/count_abstention_answers_by_question");
+		view.setKey(questionId);
+		view.setGroup(true);
+		ViewResults results = this.getDatabase().view(view);
+		if (results.getResults().size() == 0) {
+			return 0;
+		}
+		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
 	}
 
 	@Override
 	public final int getAnswerCount(final String questionId) {
-		try {
-			View view = new View("skill_question/count_answers_by_question");
-			view.setKey(URLEncoder.encode("\"" + questionId + "\"", "UTF-8"));
-			view.setGroup(true);
-			ViewResults results = this.getDatabase().view(view);
-			if (results.getResults().size() == 0) {
-				return 0;
-			}
-			return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving answer count", e);
+		NovaView view = new NovaView("skill_question/count_answers_by_question");
+		view.setKey(questionId);
+		view.setGroup(true);
+		ViewResults results = this.getDatabase().view(view);
+		if (results.getResults().size() == 0) {
+			return 0;
 		}
-		return 0;
+		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
 	}
 
 	@Override
@@ -965,20 +656,14 @@ public class CouchDBDao implements IDatabaseDao {
 	@Override
 	public final int countActiveUsers(Session session, long since) {
 		if (session == null) throw new NotFoundException();
-		try {
-			View view = new View("logged_in/count");
-			view.setStartKey(
-					URLEncoder.encode("[\"" + session.get_id() + "\", " + String.valueOf(since) + "]", "UTF-8")
-			);
-			view.setEndKey(URLEncoder.encode("[\"" + session.get_id() + "\", {}]", "UTF-8"));
-			ViewResults results = this.getDatabase().view(view);
-			if (isEmptyResults(results)) {
-				return 0;
-			}
-			return results.getJSONArray("rows").optJSONObject(0).getInt("value");
-		} catch (UnsupportedEncodingException e) {
+		NovaView view = new NovaView("logged_in/count");
+		view.setStartKey(session.get_id(), String.valueOf(since));
+		view.setEndKey(session.get_id(), "{}");
+		ViewResults results = this.getDatabase().view(view);
+		if (isEmptyResults(results)) {
 			return 0;
 		}
+		return results.getJSONArray("rows").optJSONObject(0).getInt("value");
 	}
 
 	private boolean isEmptyResults(ViewResults results) {
@@ -987,24 +672,19 @@ public class CouchDBDao implements IDatabaseDao {
 
 	@Override
 	public List<Answer> getFreetextAnswers(String questionId) {
-		try {
-			View view = new View("skill_question/freetext_answers_full");
-			view.setKey(URLEncoder.encode("\"" + questionId + "\"", "UTF-8"));
-			ViewResults results = this.getDatabase().view(view);
-			if (results.getResults().isEmpty()) {
-				throw new NotFoundException();
-			}
-			List<Answer> answers = new ArrayList<Answer>();
-			for (Document d : results.getResults()) {
-				Answer a = (Answer) JSONObject.toBean(d.getJSONObject().getJSONObject("value"), Answer.class);
-				a.setQuestionId(questionId);
-				answers.add(a);
-			}
+		List<Answer> answers = new ArrayList<Answer>();
+		NovaView view = new NovaView("skill_question/freetext_answers_full");
+		view.setKey(questionId);
+		ViewResults results = this.getDatabase().view(view);
+		if (results.getResults().isEmpty()) {
 			return answers;
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving freetext answers", e);
 		}
-		return null;
+		for (Document d : results.getResults()) {
+			Answer a = (Answer) JSONObject.toBean(d.getJSONObject().getJSONObject("value"), Answer.class);
+			a.setQuestionId(questionId);
+			answers.add(a);
+		}
+		return answers;
 	}
 
 	@Override
@@ -1019,29 +699,22 @@ public class CouchDBDao implements IDatabaseDao {
 			throw new UnauthorizedException();
 		}
 
-		try {
-			View view = new View("answer/by_user_and_session_full");
-			view.setKey(
-				"[" + URLEncoder.encode("\"" + user.getUsername() + "\",\"" + s.get_id() + "\"", "UTF-8") + "]"
-			);
-			ViewResults results = this.getDatabase().view(view);
-			List<Answer> answers = new ArrayList<Answer>();
-			if (results == null || results.getResults() == null || results.getResults().isEmpty()) {
-				return answers;
-			}
-			for (Document d : results.getResults()) {
-				Answer a = (Answer) JSONObject.toBean(d.getJSONObject().getJSONObject("value"), Answer.class);
-				a.set_id(d.getId());
-				a.set_rev(d.getRev());
-				a.setUser(user.getUsername());
-				a.setSessionId(s.get_id());
-				answers.add(a);
-			}
+		NovaView view = new NovaView("answer/by_user_and_session_full");
+		view.setKey(user.getUsername(), s.get_id());
+		ViewResults results = this.getDatabase().view(view);
+		List<Answer> answers = new ArrayList<Answer>();
+		if (results == null || results.getResults() == null || results.getResults().isEmpty()) {
 			return answers;
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving user answers", e);
 		}
-		return null;
+		for (Document d : results.getResults()) {
+			Answer a = (Answer) JSONObject.toBean(d.getJSONObject().getJSONObject("value"), Answer.class);
+			a.set_id(d.getId());
+			a.set_rev(d.getRev());
+			a.setUser(user.getUsername());
+			a.setSessionId(s.get_id());
+			answers.add(a);
+		}
+		return answers;
 	}
 
 	@Override
@@ -1051,18 +724,13 @@ public class CouchDBDao implements IDatabaseDao {
 			throw new NotFoundException();
 		}
 
-		try {
-			View view = new View("skill_question/count_answers_by_session");
-			view.setKey(URLEncoder.encode("\"" + s.get_id() + "\"", "UTF-8"));
-			ViewResults results = this.getDatabase().view(view);
-			if (results.getResults().size() == 0) {
-				return 0;
-			}
-			return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving total answer count", e);
+		NovaView view = new NovaView("skill_question/count_answers_by_session");
+		view.setKey(s.get_id());
+		ViewResults results = this.getDatabase().view(view);
+		if (results.getResults().size() == 0) {
+			return 0;
 		}
-		return 0;
+		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
 	}
 
 	@Override
@@ -1072,42 +740,32 @@ public class CouchDBDao implements IDatabaseDao {
 			throw new NotFoundException();
 		}
 
-		try {
-			View view = new View("interposed_question/count_by_session");
-			view.setKey(URLEncoder.encode("\"" + s.get_id() + "\"", "UTF-8"));
-			view.setGroup(true);
-			ViewResults results = this.getDatabase().view(view);
-			if (results.size() == 0 || results.getResults().size() == 0) {
-				return 0;
-			}
-			return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving interposed question count", e);
+		NovaView view = new NovaView("interposed_question/count_by_session");
+		view.setKey(s.get_id());
+		view.setGroup(true);
+		ViewResults results = this.getDatabase().view(view);
+		if (results.size() == 0 || results.getResults().size() == 0) {
+			return 0;
 		}
-		return 0;
+		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
 	}
 
 	@Override
 	public InterposedReadingCount getInterposedReadingCount(Session session) {
-		try {
-			View view = new View("interposed_question/count_by_session_reading");
-			view.setStartKey(URLEncoder.encode("[\"" + session.get_id() + "\"]", "UTF-8"));
-			view.setEndKey(URLEncoder.encode("[\"" + session.get_id() + "\", {}]", "UTF-8"));
-			view.setGroup(true);
-			ViewResults results = this.getDatabase().view(view);
-			if (results.size() == 0 || results.getResults().size() == 0) {
-				return new InterposedReadingCount();
-			}
-			int read = results.getJSONArray("rows").optJSONObject(0).optInt("value");
-			int unread = 0;
-			if (results.getJSONArray("rows").optJSONObject(1) != null) {
-				unread = results.getJSONArray("rows").optJSONObject(1).optInt("value");
-			}
-			return new InterposedReadingCount(read, unread);
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving interposed question count", e);
+		NovaView view = new NovaView("interposed_question/count_by_session_reading");
+		view.setStartKeyArray(session.get_id());
+		view.setEndKeyArray(session.get_id(), "{}");
+		view.setGroup(true);
+		ViewResults results = this.getDatabase().view(view);
+		if (results.size() == 0 || results.getResults().size() == 0) {
+			return new InterposedReadingCount();
 		}
-		return new InterposedReadingCount();
+		int read = results.getJSONArray("rows").optJSONObject(0).optInt("value");
+		int unread = 0;
+		if (results.getJSONArray("rows").optJSONObject(1) != null) {
+			unread = results.getJSONArray("rows").optJSONObject(1).optInt("value");
+		}
+		return new InterposedReadingCount(read, unread);
 	}
 
 	@Override
@@ -1117,29 +775,24 @@ public class CouchDBDao implements IDatabaseDao {
 			throw new NotFoundException();
 		}
 
-		try {
-			View view = new View("interposed_question/by_session");
-			view.setKey(URLEncoder.encode("\"" + s.get_id() + "\"", "UTF-8"));
-			ViewResults questions = this.getDatabase().view(view);
-			if (questions == null || questions.isEmpty()) {
-				return null;
-			}
-			List<InterposedQuestion> result = new ArrayList<InterposedQuestion>();
-			LOGGER.debug("{}", questions.getResults());
-			for (Document document : questions.getResults()) {
-				InterposedQuestion question = (InterposedQuestion) JSONObject.toBean(
-						document.getJSONObject().getJSONObject("value"),
-						InterposedQuestion.class
-				);
-				question.setSessionId(sessionKey);
-				question.set_id(document.getId());
-				result.add(question);
-			}
-			return result;
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving interposed questions", e);
+		NovaView view = new NovaView("interposed_question/by_session");
+		view.setKey(s.get_id());
+		ViewResults questions = this.getDatabase().view(view);
+		if (questions == null || questions.isEmpty()) {
+			return null;
 		}
-		return null;
+		List<InterposedQuestion> result = new ArrayList<InterposedQuestion>();
+		LOGGER.debug("{}", questions.getResults());
+		for (Document document : questions.getResults()) {
+			InterposedQuestion question = (InterposedQuestion) JSONObject.toBean(
+					document.getJSONObject().getJSONObject("value"),
+					InterposedQuestion.class
+			);
+			question.setSessionId(sessionKey);
+			question.set_id(document.getId());
+			result.add(question);
+		}
+		return result;
 	}
 
 	public Question getInterposedQuestion(String sessionKey, String documentId) {
@@ -1167,8 +820,8 @@ public class CouchDBDao implements IDatabaseDao {
 
 		String date = new SimpleDateFormat("dd-mm-yyyyy").format(new Date());
 		try {
-			View view = new View("food_vote/get_user_vote");
-			view.setKey("[" + URLEncoder.encode("\"" + date + "\",\"" + u.getUsername() + "\"", "UTF-8") + "]");
+			NovaView view = new NovaView("food_vote/get_user_vote");
+			view.setKey(date, u.getUsername());
 			ViewResults results = this.getDatabase().view(view);
 
 			if (results.getResults().isEmpty()) {
@@ -1183,8 +836,6 @@ public class CouchDBDao implements IDatabaseDao {
 				vote.put("name", menu);
 				this.database.saveDocument(vote);
 			}
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving user food vote", e);
 		} catch (IOException e) {
 			LOGGER.error("Error while saving user food vote", e);
 		}
@@ -1194,23 +845,17 @@ public class CouchDBDao implements IDatabaseDao {
 	public List<FoodVote> getFoodVote() {
 		List<FoodVote> foodVotes = new ArrayList<FoodVote>();
 		String date = new SimpleDateFormat("dd-mm-yyyyy").format(new Date());
-		try {
-			View view = new View("food_vote/count_by_day");
-			view.setStartKey("[" + URLEncoder.encode("\"" + date + "\"", "UTF-8") + "]");
-			view.setEndKey("[" + URLEncoder.encode("\"" + date + "\",{}", "UTF-8") + "]");
-			view.setGroup(true);
-			ViewResults results = this.getDatabase().view(view);
-			for (Document d : results.getResults()) {
-				FoodVote vote = new FoodVote();
-				vote.setCount(d.getJSONObject().optInt("value"));
-				vote.setDay(date);
-				vote.setName(d.getJSONObject().getJSONArray("key").getString(1));
-				foodVotes.add(vote);
-			}
-
-			return foodVotes;
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving food vote count", e);
+		NovaView view = new NovaView("food_vote/count_by_day");
+		view.setStartKeyArray(date);
+		view.setEndKeyArray(date, "{}");
+		view.setGroup(true);
+		ViewResults results = this.getDatabase().view(view);
+		for (Document d : results.getResults()) {
+			FoodVote vote = new FoodVote();
+			vote.setCount(d.getJSONObject().optInt("value"));
+			vote.setDay(date);
+			vote.setName(d.getJSONObject().getJSONArray("key").getString(1));
+			foodVotes.add(vote);
 		}
 		return foodVotes;
 	}
@@ -1218,20 +863,15 @@ public class CouchDBDao implements IDatabaseDao {
 	@Override
 	public int getFoodVoteCount() {
 		String date = new SimpleDateFormat("dd-mm-yyyyy").format(new Date());
-		try {
-			View view = new View("food_vote/count_by_day");
-			view.setStartKey("[" + URLEncoder.encode("\"" + date + "\"", "UTF-8") + "]");
-			view.setEndKey("[" + URLEncoder.encode("\"" + date + "\",{}", "UTF-8") + "]");
-			view.setGroup(false);
-			ViewResults results = this.getDatabase().view(view);
-			if (results.size() == 0 || results.getResults().size() == 0) {
-				return 0;
-			}
-			return results.getJSONArray("rows").optJSONObject(0).optInt("value");
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving food vote count", e);
+		NovaView view = new NovaView("food_vote/count_by_day");
+		view.setStartKeyArray(date);
+		view.setEndKeyArray(date, "{}");
+		view.setGroup(false);
+		ViewResults results = this.getDatabase().view(view);
+		if (results.size() == 0 || results.getResults().size() == 0) {
+			return 0;
 		}
-		return 0;
+		return results.getJSONArray("rows").optJSONObject(0).optInt("value");
 	}
 
 	@Override
@@ -1316,34 +956,30 @@ public class CouchDBDao implements IDatabaseDao {
 
 	@Override
 	public List<Session> getMyVisitedSessions(User user) {
-		try {
-			View view = new View("logged_in/visited_sessions_by_user");
-			view.setKey(URLEncoder.encode("\"" + user.getUsername() + "\"", "UTF-8"));
-			ViewResults sessions = this.getDatabase().view(view);
-			List<Session> allSessions = new ArrayList<Session>();
-			for (Document d : sessions.getResults()) {
-				// Not all users have visited sessions
-				if (d.getJSONObject().optJSONArray("value") != null) {
-					@SuppressWarnings("unchecked")
-					Collection<Session> visitedSessions =  JSONArray.toCollection(
-						d.getJSONObject().getJSONArray("value"),
-						Session.class
-					);
-					allSessions.addAll(visitedSessions);
-				}
+		NovaView view = new NovaView("logged_in/visited_sessions_by_user");
+		view.setKey(user.getUsername());
+		ViewResults sessions = this.getDatabase().view(view);
+		List<Session> allSessions = new ArrayList<Session>();
+		for (Document d : sessions.getResults()) {
+			// Not all users have visited sessions
+			if (d.getJSONObject().optJSONArray("value") != null) {
+				@SuppressWarnings("unchecked")
+				Collection<Session> visitedSessions =  JSONArray.toCollection(
+					d.getJSONObject().getJSONArray("value"),
+					Session.class
+				);
+				allSessions.addAll(visitedSessions);
 			}
-			// Do these sessions still exist?
-			List<Session> result = new ArrayList<Session>();
-			for (Session s : allSessions) {
-				Session session = this.getSessionFromKeyword(s.getKeyword());
-				if (session != null) {
-					result.add(session);
-				}
+		}
+		// Do these sessions still exist?
+		List<Session> result = new ArrayList<Session>();
+		for (Session s : allSessions) {
+			Session session = this.getSessionFromKeyword(s.getKeyword());
+			if (session != null) {
+				result.add(session);
 			}
-			return result;
-		} catch (UnsupportedEncodingException e) {
-			return null;
 		}
+		return result;
 	}
 
 	@Override
@@ -1424,24 +1060,17 @@ public class CouchDBDao implements IDatabaseDao {
 
 	@Override
 	public final List<String> getActiveUsers(int timeDifference) {
-		try {
-			long inactiveBeforeTimestamp = new Date().getTime() - timeDifference * 1000;
-
-			View view = new View("logged_in/by_and_only_timestamp_and_username");
-			view.setStartKey("[" + URLEncoder.encode(String.valueOf(inactiveBeforeTimestamp), "UTF-8") + "]");
-			ViewResults results = this.getDatabase().view(view);
-			LOGGER.debug("getActiveUsers result count: {}", String.valueOf(results.size()));
+		long inactiveBeforeTimestamp = new Date().getTime() - timeDifference * 1000;
 
-			List<String> result = new ArrayList<String>();
-			for (Document d : results.getResults()) {
-				result.add(d.getJSONObject().getJSONArray("key").getString(1));
-			}
+		NovaView view = new NovaView("logged_in/by_and_only_timestamp_and_username");
+		view.setStartKeyArray(String.valueOf(inactiveBeforeTimestamp));
+		ViewResults results = this.getDatabase().view(view);
 
-			return result;
-		} catch (UnsupportedEncodingException e) {
-			LOGGER.error("Error while retrieving active users", e);
+		List<String> result = new ArrayList<String>();
+		for (Document d : results.getResults()) {
+			result.add(d.getJSONObject().getJSONArray("key").getString(1));
 		}
-		return null;
+		return result;
 	}
 
 	private class ExtendedView extends View {
diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
index 4d9e600e5b0d9b3ce72fb586cf07b109bc9efebb..d6027c30c7a90916fbaf61aa17495890389405c4 100644
--- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
+++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java
@@ -23,7 +23,6 @@ import java.util.List;
 
 import de.thm.arsnova.connector.model.Course;
 import de.thm.arsnova.entities.Answer;
-import de.thm.arsnova.entities.Feedback;
 import de.thm.arsnova.entities.FoodVote;
 import de.thm.arsnova.entities.InterposedQuestion;
 import de.thm.arsnova.entities.InterposedReadingCount;
@@ -33,8 +32,6 @@ import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.entities.User;
 
 public interface IDatabaseDao {
-	void cleanFeedbackVotes(int cleanupFeedbackDelay);
-
 	Session getSessionFromKeyword(String keyword);
 
 	Session getSession(String keyword);
@@ -43,10 +40,6 @@ public interface IDatabaseDao {
 
 	Session saveSession(Session session);
 
-	Feedback getFeedback(String keyword);
-
-	boolean saveFeedback(String keyword, int value, User user);
-
 	boolean sessionKeyAvailable(String keyword);
 
 	Question saveQuestion(Session session, Question question);
@@ -63,12 +56,12 @@ public interface IDatabaseDao {
 
 	void updateSessionOwnerActivity(Session session);
 
-	Integer getMyFeedback(String keyword, User user);
-
 	List<String> getQuestionIds(Session session, User user);
 
 	void deleteQuestionWithAnswers(Question question);
 
+	void deleteAllQuestionsWithAnswers(Session session);
+
 	List<String> getUnAnsweredQuestionIds(Session session, User user);
 
 	Answer getMyAnswer(String questionId, int piRound);
diff --git a/src/main/java/de/thm/arsnova/dao/NovaView.java b/src/main/java/de/thm/arsnova/dao/NovaView.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9325b598ea5208be3412643ed0c31999ef2dc04
--- /dev/null
+++ b/src/main/java/de/thm/arsnova/dao/NovaView.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 THM webMedia
+ *
+ * This file is part of ARSnova.
+ *
+ * ARSnova is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.dao;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import com.fourspaces.couchdb.View;
+
+public class NovaView extends View {
+
+	public NovaView(String fullname) {
+		super(fullname);
+	}
+
+	@Override
+	public void setStartKey(String key) {
+		this.startKey = quote(key);
+	}
+
+	public void setStartKeyArray(String key) {
+		if (isNumber(key)) {
+			this.startKey = encode("[" + key + "]");
+		} else {
+			this.startKey = encode("[\"" + key + "\"]");
+		}
+	}
+
+	public void setStartKeyArray(String... keys) {
+		this.setStartKey(keys);
+	}
+
+	@Override
+	public void setEndKey(String key) {
+		this.endKey = quote(key);
+	}
+
+	public void setEndKeyArray(String key) {
+		if (isNumber(key)) {
+			this.endKey = encode("[" + key + "]");
+		} else {
+			this.endKey = encode("[\"" + key + "\"]");
+		}
+	}
+
+	public void setEndKeyArray(String... keys) {
+		this.setEndKey(keys);
+	}
+
+	public void setStartKey(String... keys) {
+		this.startKey = toJsonArray(keys);
+	}
+
+	public void setEndKey(String... keys) {
+		this.endKey = toJsonArray(keys);
+	}
+
+	@Override
+	public void setKey(String key) {
+		this.key = quote(key);
+	}
+
+	public void setKey(String... keys) {
+		this.key = toJsonArray(keys);
+	}
+
+	private String toJsonArray(String[] strings) {
+		StringBuilder sb = new StringBuilder();
+		for (String str : strings) {
+			if (isNumber(str)) {
+				sb.append(str + ",");
+			} else if (str.equals("{}")) {
+				sb.append(str + ",");
+			} else {
+				sb.append("\"" + str + "\"" + ",");
+			}
+		}
+		sb.replace(sb.length() - 1, sb.length(), ""); // remove final comma
+		sb.insert(0, "[");
+		sb.append("]");
+		return encode(sb.toString());
+	}
+
+	private String quote(String string) {
+		return encode("\"" + string + "\"");
+	}
+	
+	private boolean isNumber(String string) {
+		return string.matches("^[0-9]+$");
+	}
+
+	private String encode(String string) {
+		try {
+			return URLEncoder.encode(string, "UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			// Since we're using 'UTF-8', this should Exception should never occur.
+		}
+		return "";
+	}
+}
diff --git a/src/main/java/de/thm/arsnova/entities/Answer.java b/src/main/java/de/thm/arsnova/entities/Answer.java
index 5aded99a5762f924b2ae42c370c0bc91520ac1ba..278ac92d50b61363859b23a4133e836993b331a9 100644
--- a/src/main/java/de/thm/arsnova/entities/Answer.java
+++ b/src/main/java/de/thm/arsnova/entities/Answer.java
@@ -13,7 +13,8 @@ public class Answer {
 	private String user;
 	private long timestamp;
 	private int answerCount = 1;
-	private boolean abstention; // Currently available only for freetext answers!
+	private boolean abstention; // Currently available only for freetext and mc answers!
+	private int abstentionCount; // Currently available only for freetext and mc answers!
 
 	public Answer() {
 		this.type = "skill_question_answer";
@@ -115,6 +116,14 @@ public class Answer {
 		this.abstention = abstention;
 	}
 
+	public int getAbstentionCount() {
+		return abstentionCount;
+	}
+
+	public void setAbstentionCount(int abstentionCount) {
+		this.abstentionCount = abstentionCount;
+	}
+
 	@Override
 	public final String toString() {
 		return "Answer type:'" + type + "'"
diff --git a/src/main/java/de/thm/arsnova/entities/User.java b/src/main/java/de/thm/arsnova/entities/User.java
index 13ae4a9cba5b4f8f2535694ce4132974c0b2414c..2f9c290bf2af3a41d6eac5b16d329d413e0ee4ff 100644
--- a/src/main/java/de/thm/arsnova/entities/User.java
+++ b/src/main/java/de/thm/arsnova/entities/User.java
@@ -9,6 +9,8 @@ import org.scribe.up.profile.twitter.TwitterProfile;
 import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 
+import de.thm.arsnova.services.UserSessionService;
+
 public class User implements Serializable {
 	public static final String GOOGLE = "google";
 	public static final String TWITTER = "twitter";
@@ -20,6 +22,7 @@ public class User implements Serializable {
 	private static final long serialVersionUID = 1L;
 	private String username;
 	private String type;
+	private UserSessionService.Role role;
 
 	public User(Google2Profile profile) {
 		setUsername(profile.getEmail());
@@ -67,6 +70,18 @@ public class User implements Serializable {
 		this.type = type;
 	}
 
+	public UserSessionService.Role getRole() {
+		return role;
+	}
+
+	public void setRole(UserSessionService.Role role) {
+		this.role = role;
+	}
+
+	public boolean hasRole(UserSessionService.Role role) {
+		return this.role == role;
+	}
+
 	@Override
 	public String toString() {
 		return "User [username=" + username + ", type=" + type + "]";
diff --git a/src/main/java/de/thm/arsnova/services/IQuestionService.java b/src/main/java/de/thm/arsnova/services/IQuestionService.java
index 389e1442a4726e0e9dc696db7426f76377ae0908..4f04a0c73506ff5b42baaf5c87cf24e8321047a8 100644
--- a/src/main/java/de/thm/arsnova/services/IQuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/IQuestionService.java
@@ -41,6 +41,8 @@ public interface IQuestionService {
 
 	void deleteQuestion(String questionId);
 
+	void deleteAllQuestions(String sessionKeyword);
+
 	List<String> getUnAnsweredQuestionIds(String sessionKey);
 
 	Answer getMyAnswer(String questionId);
diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/QuestionService.java
index d41a9173f9bec3083b2d0b443be63b896e012941..1f9d70e87685d57dccc93eab2c8afa80612a0c20 100644
--- a/src/main/java/de/thm/arsnova/services/QuestionService.java
+++ b/src/main/java/de/thm/arsnova/services/QuestionService.java
@@ -148,6 +148,17 @@ public class QuestionService implements IQuestionService {
 		databaseDao.deleteQuestionWithAnswers(question);
 	}
 
+	@Override
+	@Authenticated
+	public void deleteAllQuestions(String sessionKeyword) {
+		User user = userService.getCurrentUser();
+		Session session = databaseDao.getSession(sessionKeyword);
+		if (user == null || session == null || !session.isCreator(user)) {
+			throw new UnauthorizedException();
+		}
+		databaseDao.deleteAllQuestionsWithAnswers(session);
+	}
+
 	@Override
 	@Authenticated
 	public void deleteInterposedQuestion(String questionId) {
@@ -229,7 +240,11 @@ public class QuestionService implements IQuestionService {
 	@Override
 	@Authenticated
 	public List<Answer> getFreetextAnswers(String questionId) {
-		return databaseDao.getFreetextAnswers(questionId);
+		List<Answer> answers = databaseDao.getFreetextAnswers(questionId);
+		if (answers == null) {
+			throw new NotFoundException();
+		}
+		return answers;
 	}
 
 	@Override
diff --git a/src/main/java/de/thm/arsnova/services/SessionService.java b/src/main/java/de/thm/arsnova/services/SessionService.java
index 34b2b3d20954ee8082eacdd1249650b467c0c47e..2843b36e72bfef48660f5b8fa2d861c536bc3dd1 100644
--- a/src/main/java/de/thm/arsnova/services/SessionService.java
+++ b/src/main/java/de/thm/arsnova/services/SessionService.java
@@ -64,7 +64,6 @@ public class SessionService implements ISessionService {
 	}
 
 	@Override
-	@Authenticated
 	public final Session joinSession(final String keyword, final UUID socketId) {
 		/* Socket.IO solution */
 
@@ -96,7 +95,18 @@ public class SessionService implements ISessionService {
 	public final Session joinSession(final String keyword) {
 		/* HTTP polling solution (legacy) */
 
-		Session session = databaseDao.getSession(keyword);
+		User user = userService.getCurrentUser();
+		Session session = databaseDao.getSessionFromKeyword(keyword);
+		if (session == null) {
+			throw new NotFoundException();
+		}
+		if (!session.isActive()) {
+			if (user.hasRole(UserSessionService.Role.STUDENT)) {
+				throw new ForbiddenException();
+			} else if (user.hasRole(UserSessionService.Role.SPEAKER) && !session.isCreator(user)) {
+				throw new ForbiddenException();
+			}
+		}
 
 		userService.addCurrentUserToSessionMap(keyword);
 		socketIoServer.reportActiveUserCountForSession(keyword);
diff --git a/src/main/java/de/thm/arsnova/services/UserService.java b/src/main/java/de/thm/arsnova/services/UserService.java
index 23494da815e2bff274bdc3c52fb22b8bffbebbf0..99521ec7530b50d628bd9b9555633967da72b3f4 100644
--- a/src/main/java/de/thm/arsnova/services/UserService.java
+++ b/src/main/java/de/thm/arsnova/services/UserService.java
@@ -142,8 +142,12 @@ public class UserService implements IUserService {
 		}
 		String session = user2sessionLegacy.get(user);
 		if (session == null) {
-			return false;
+			session = user2session.get(user);
+			if (session == null) {
+				return false;
+			}
 		}
+
 		return keyword.equals(session);
 	}
 
diff --git a/src/main/java/de/thm/arsnova/services/UserSessionService.java b/src/main/java/de/thm/arsnova/services/UserSessionService.java
index 84f2885033b88772db4c74d34c4f831b6530982d..df3110cd50221cba01a1dc79b7ddb1951271091a 100644
--- a/src/main/java/de/thm/arsnova/services/UserSessionService.java
+++ b/src/main/java/de/thm/arsnova/services/UserSessionService.java
@@ -8,6 +8,11 @@ import de.thm.arsnova.events.ARSnovaEvent;
 import de.thm.arsnova.socket.ARSnovaSocketIOServer;
 
 public interface UserSessionService {
+	
+	enum Role {
+		STUDENT,
+		SPEAKER
+	}
 
 	void setUser(User user);
 	User getUser();
@@ -18,5 +23,11 @@ public interface UserSessionService {
 	void setSocketId(UUID socketId);
 	UUID getSocketId();
 
+	void setRole(Role role);
+	Role getRole();
+	
+	boolean inSession();
+	boolean isAuthenticated();
+	
 	void sendEventViaWebSocket(ARSnovaSocketIOServer server, ARSnovaEvent event);
 }
\ No newline at end of file
diff --git a/src/main/java/de/thm/arsnova/services/UserSessionServiceImpl.java b/src/main/java/de/thm/arsnova/services/UserSessionServiceImpl.java
index e10a7b6c662d102775b72b729c23009cc72f7fd1..1a2c22874303c9cc95f9a6716bf89dd7443baa6e 100644
--- a/src/main/java/de/thm/arsnova/services/UserSessionServiceImpl.java
+++ b/src/main/java/de/thm/arsnova/services/UserSessionServiceImpl.java
@@ -25,10 +25,12 @@ public class UserSessionServiceImpl implements UserSessionService, Serializable
 	private User user;
 	private Session session;
 	private UUID socketId;
+	private Role role;
 
 	@Override
 	public void setUser(User u) {
 		this.user = u;
+		this.user.setRole(this.role);
 	}
 
 	@Override
@@ -59,6 +61,22 @@ public class UserSessionServiceImpl implements UserSessionService, Serializable
 	private boolean hasConnectedWebSocket() {
 		return getSocketId() != null;
 	}
+	
+	@Override
+	public boolean inSession() {
+		return (
+			this.isAuthenticated()
+			&& this.getSession() != null
+		);
+	}
+	
+	@Override
+	public boolean isAuthenticated() {
+		return (
+			this.getUser() != null
+			&& this.getRole() != null	
+		);
+	}
 
 	@Override
 	public void sendEventViaWebSocket(ARSnovaSocketIOServer server, ARSnovaEvent event) {
@@ -85,4 +103,17 @@ public class UserSessionServiceImpl implements UserSessionService, Serializable
 			server.sendToClient(getSocketId(), event);
 		}
 	}
+
+	@Override
+	public void setRole(Role r) {
+		role = r;
+		if (user != null) {
+			user.setRole(role);
+		}
+	}
+	
+	@Override
+	public Role getRole() {
+		return role;
+	}
 }
diff --git a/src/site/apt/installation.apt b/src/site/apt/installation.apt
index 824ec56bcaab052d5f97bdfbd961d1c1ce4e14e5..e30cf7011119154090972cc365713a56e487b5e9 100644
--- a/src/site/apt/installation.apt
+++ b/src/site/apt/installation.apt
@@ -73,4 +73,39 @@ Make environment usable for productive usage
  To protect requests and responses you should use HTTPS and configure your Apache Webserver installation to redirect all traffic according to this
  {{{http://wiki.apache.org/httpd/RedirectSSL}example}}.
 
- Finally you should (re)start all services. ARSnova2 is now listening on HTTP port 80 and 443.
\ No newline at end of file
+ Finally you should (re)start all services. ARSnova2 is now listening on HTTP port 80 and 443.
+ 
+Securing your websocket connection
+
+ To provide SSL websocket encryption, you have to provide the servers ssl key and certificate in a Java keystore. The following steps will guide you threw this process.
+ 
+ Use your webserver certificate, private key and certificate chain to create a PKCS12 keystore
+
++---------------------------+
+openssl pkcs12 -export -in <servercert>.crt -inkey <serverkey>.key \
+               -out keystore.p12 -name 1 \
+               -certfile <your_cert_chain_file>
++---------------------------+
+
+ You will be asked for a password for your PKCS12 keystore. This password must be used for importing this keystore into your java keystore.
+ The import can be done by using this command:
+
++---------------------------+
+keytool -importkeystore \
+        -deststorepass <your_java_keystore_password> -destkeypass <your_java_keystore_password> -destkeystore arsnova.jks \
+        -srckeystore keystore.p12 -srcstoretype PKCS12 -srcstorepass <your_pkcs12_keystore_password> \
+        -alias 1
++---------------------------+
+ 
+ Be sure to provide the correct certificate and key file names and to use the correct passwords for your keystore.
+ 
+ The last step is to find your ARSnova configuration file, setup the location of your Java keystore and its password.
+ 
++---------------------------+
+ 
+security.ssl=true
+security.keystore=<your keystore location>
+security.storepass=<your keystore password>
+ 
++---------------------------+
+ 
\ No newline at end of file
diff --git a/src/site/resources/showcase.png b/src/site/resources/showcase.png
new file mode 100644
index 0000000000000000000000000000000000000000..6832c11cb0666acf7b262769adf8ca51ea428ffe
Binary files /dev/null and b/src/site/resources/showcase.png differ
diff --git a/src/test/java/de/thm/arsnova/controller/LoginControllerTest.java b/src/test/java/de/thm/arsnova/controller/LoginControllerTest.java
index f10539f3e7fb1fd8c0a72ede265db8c7e4da7a62..65b8ffa4af117a2a211af79108bd93fbc928193d 100644
--- a/src/test/java/de/thm/arsnova/controller/LoginControllerTest.java
+++ b/src/test/java/de/thm/arsnova/controller/LoginControllerTest.java
@@ -126,7 +126,7 @@ public class LoginControllerTest {
 
 		handlerAdapter.handle(request, response, loginController);
 		assertNotNull(response);
-		assertEquals("{\"username\":\"ptsr00\",\"type\":\"ldap\"}", response.getContentAsString());
+		assertEquals("{\"username\":\"ptsr00\",\"type\":\"ldap\",\"role\":null}", response.getContentAsString());
 	}
 
 	@Test
diff --git a/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java b/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java
index f8fe71eb0d39304eaf7baf4e9abfc2386f15319b..cdb927ae3b831584dc1f6f78db5decd11327273a 100644
--- a/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java
+++ b/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java
@@ -20,10 +20,12 @@ import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
 
 import de.thm.arsnova.dao.StubDatabaseDao;
+import de.thm.arsnova.entities.Session;
 import de.thm.arsnova.exceptions.ForbiddenException;
 import de.thm.arsnova.exceptions.NotFoundException;
 import de.thm.arsnova.exceptions.UnauthorizedException;
 import de.thm.arsnova.services.StubUserService;
+import de.thm.arsnova.services.UserSessionService;
 
 @RunWith(SpringJUnit4ClassRunner.class)
 @ContextConfiguration(locations = {
@@ -76,12 +78,17 @@ public class SessionControllerTest {
 
 	@Test(expected = ForbiddenException.class)
 	public void testShouldNotGetForbiddenSession() throws Exception {
+		Session session = new Session();
+		session.setKeyword("08154711");
+		session.setCreator("some other user");
+		session.setActive(false);
+		databaseDao.saveSession(session);
 		userService.setUserAuthenticated(true);
+		userService.setRole(UserSessionService.Role.STUDENT);
 
 		request.setMethod("GET");
-		request.setRequestURI("/session/99999999");
-		final ModelAndView mav = handlerAdapter.handle(request, response,
-				sessionController);
+		request.setRequestURI("/session/08154711");
+		final ModelAndView mav = handlerAdapter.handle(request, response, sessionController);
 
 		assertNull(mav);
 		assertTrue(response.getStatus() == 403);
diff --git a/src/test/java/de/thm/arsnova/dao/NovaViewTest.java b/src/test/java/de/thm/arsnova/dao/NovaViewTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ef0f87694f6e8e24807ac5dcd757f608a727bd6
--- /dev/null
+++ b/src/test/java/de/thm/arsnova/dao/NovaViewTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2012 THM webMedia
+ *
+ * This file is part of ARSnova.
+ *
+ * ARSnova is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * ARSnova is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package de.thm.arsnova.dao;
+
+import static org.junit.Assert.*;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+import org.junit.Test;
+
+public class NovaViewTest {
+
+	@Test
+	public void setKeyShouldAcceptSingleArgument() {
+		NovaView v = new NovaView(null);
+		v.setKey("foo");
+		assertEncodedEquals("key", "\"foo\"", v.getQueryString());
+	}
+
+	@Test
+	public void setKeyShouldAcceptMultipleArgument() {
+		NovaView v = new NovaView(null);
+		v.setKey("foo", "bar", "baz");
+		assertEncodedEquals("key", "[\"foo\",\"bar\",\"baz\"]", v.getQueryString());
+	}
+
+	@Test
+	public void setStartKeyShouldAcceptSingleArgument() {
+		NovaView v = new NovaView(null);
+		v.setStartKey("foo");
+		assertEncodedEquals("startkey", "\"foo\"", v.getQueryString());
+	}
+
+	@Test
+	public void setStartKeyShouldAcceptSingleArgumentArray() {
+		NovaView v = new NovaView(null);
+		v.setStartKeyArray("foo");
+		assertEncodedEquals("startkey", "[\"foo\"]", v.getQueryString());
+	}
+
+	@Test
+	public void setEndKeyShouldAcceptSingleArgumentArray() {
+		NovaView v = new NovaView(null);
+		v.setEndKeyArray("foo");
+		assertEncodedEquals("endkey", "[\"foo\"]", v.getQueryString());
+	}
+
+	@Test
+	public void setEndKeyShouldAcceptSingleArgument() {
+		NovaView v = new NovaView(null);
+		v.setEndKey("foo");
+		assertEncodedEquals("endkey", "\"foo\"", v.getQueryString());
+	}
+
+	@Test
+	public void setStartKeyShouldAcceptMultipleArgument() {
+		NovaView v = new NovaView(null);
+		v.setStartKey("foo", "bar", "baz");
+		assertEncodedEquals("startkey", "[\"foo\",\"bar\",\"baz\"]", v.getQueryString());
+	}
+
+	@Test
+	public void setEndKeyShouldAcceptMultipleArgument() {
+		NovaView v = new NovaView(null);
+		v.setEndKey("foo", "bar", "baz");
+		assertEncodedEquals("endkey", "[\"foo\",\"bar\",\"baz\"]", v.getQueryString());
+	}
+
+	@Test
+	public void keysShouldSupportEmptyObject() {
+		NovaView v = new NovaView(null);
+		v.setKey("foo", "bar", "{}");
+		assertEncodedEquals("key", "[\"foo\",\"bar\",{}]", v.getQueryString());
+	}
+
+	@Test
+	public void arrayKeysShouldNotEnquoteNumbers() {
+		NovaView v = new NovaView(null);
+		v.setKey("foo", "bar", "2");
+		assertEncodedEquals("key", "[\"foo\",\"bar\",2]", v.getQueryString());
+	}
+
+	@Test
+	public void singleArrayKeysShouldNotEnquoteNumbers() {
+		NovaView v1 = new NovaView(null);
+		NovaView v2 = new NovaView(null);
+		v1.setStartKeyArray("2");
+		v2.setEndKeyArray("2");
+		assertEncodedEquals("startkey", "[2]", v1.getQueryString());
+		assertEncodedEquals("endkey", "[2]", v2.getQueryString());
+	}
+
+	private void assertEncodedEquals(String key, String expected, String actual) {
+		try {
+			assertEquals(key + "=" + URLEncoder.encode(expected, "UTF-8"), actual);
+		} catch (UnsupportedEncodingException e) {
+			fail(e.getLocalizedMessage());
+		}
+	}
+}
diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
index ffc536497e58a35dc181f301609838c55ee2eb46..5f83db6dbe0650196b6c90c5b202437bc019c53b 100644
--- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
+++ b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java
@@ -104,11 +104,6 @@ public class StubDatabaseDao implements IDatabaseDao {
 		stubQuestions.put("12345678", questions);
 	}
 
-	@Override
-	public void cleanFeedbackVotes(int cleanupFeedbackDelay) {
-		stubSessions.clear();
-	}
-
 	@Override
 	public Session getSession(String keyword) {
 		// Magic keyword for forbidden session
@@ -150,37 +145,6 @@ public class StubDatabaseDao implements IDatabaseDao {
 		}
 		return result;
 	}
-	
-	@Override
-	public Feedback getFeedback(String keyword) {
-		// Magic keyword for forbidden session
-		if (keyword.equals("99999999"))
-			throw new ForbiddenException();
-
-		Feedback feedback = stubFeedbacks.get(keyword);
-		if (feedback == null)
-			throw new NotFoundException();
-
-		return feedback;
-	}
-
-	@Override
-	public boolean saveFeedback(String keyword, int value, User user) {
-		if (stubFeedbacks.get(keyword) == null) {
-			stubFeedbacks.put(keyword, new Feedback(0, 0, 0, 0));
-		}
-
-		Feedback sessionFeedback = stubFeedbacks.get(keyword);
-
-		List<Integer> values = sessionFeedback.getValues();
-		values.set(value, values.get(value) + 1);
-
-		sessionFeedback = new Feedback(values.get(0), values.get(1), values.get(2), values.get(3));
-
-		stubFeedbacks.put(keyword, sessionFeedback);
-
-		return true;
-	}
 
 	@Override
 	public boolean sessionKeyAvailable(String keyword) {
@@ -240,12 +204,6 @@ public class StubDatabaseDao implements IDatabaseDao {
 
 	}
 
-	@Override
-	public Integer getMyFeedback(String keyword, User user) {
-		// TODO Auto-generated method stub
-		return null;
-	}
-
 	@Override
 	public Answer getMyAnswer(String questionId, int piRound) {
 		// TODO Auto-generated method stub
@@ -439,4 +397,10 @@ public class StubDatabaseDao implements IDatabaseDao {
 	public void deleteSession(Session session) {
 		// TODO Auto-generated method stub
 	}
+
+	@Override
+	public void deleteAllQuestionsWithAnswers(Session session) {
+		// TODO Auto-generated method stub
+		
+	}
 }
diff --git a/src/test/java/de/thm/arsnova/services/SessionServiceTest.java b/src/test/java/de/thm/arsnova/services/SessionServiceTest.java
index 5700c0eaef926567b2d4cabc5a2df4d032f0c7d6..c6ede4f8dfd05792f6996ab9a81aed41f0cb3a35 100644
--- a/src/test/java/de/thm/arsnova/services/SessionServiceTest.java
+++ b/src/test/java/de/thm/arsnova/services/SessionServiceTest.java
@@ -103,8 +103,6 @@ public class SessionServiceTest {
 	@Test
 	public void testShouldSaveSession() {
 		userService.setUserAuthenticated(true);
-		// Prevent "NotFoundExceptions" inside StubDatabase while saving the session
-		databaseDao.saveFeedback("11111111", 1, userService.getCurrentUser());
 
 		Session session = new Session();
 		session.setActive(true);
diff --git a/src/test/java/de/thm/arsnova/services/StubUserService.java b/src/test/java/de/thm/arsnova/services/StubUserService.java
index 94f67fa33f13ed8a6269a8592bda98bbe49b11f2..c7667d2e8739f277df6473eb740a7306a7bd0515 100644
--- a/src/test/java/de/thm/arsnova/services/StubUserService.java
+++ b/src/test/java/de/thm/arsnova/services/StubUserService.java
@@ -46,4 +46,8 @@ public class StubUserService extends UserService {
 	public User getCurrentUser() {
 		return stubUser;
 	}
+	
+	public void setRole(UserSessionService.Role role) {
+		stubUser.setRole(role);
+	}
 }