Commit ef299621 authored by Daniel Gerhardt's avatar Daniel Gerhardt

Separate session persistance code and migrate it to Ektorp

LoggedIn documents are also handled by the SessionRepository for now.
parent 57ec03af
......@@ -26,14 +26,17 @@ import de.thm.arsnova.connector.client.ConnectorClientImpl;
import de.thm.arsnova.entities.DbUser;
import de.thm.arsnova.entities.LogEntry;
import de.thm.arsnova.entities.Motd;
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.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.CouchDbLogEntryRepository;
import de.thm.arsnova.persistance.couchdb.CouchDbMotdRepository;
import de.thm.arsnova.persistance.couchdb.CouchDbSessionRepository;
import de.thm.arsnova.persistance.couchdb.CouchDbUserRepository;
import de.thm.arsnova.persistance.couchdb.InitializingCouchDbConnector;
import de.thm.arsnova.socket.ARSnovaSocket;
......@@ -284,6 +287,11 @@ public class AppConfig extends WebMvcConfigurerAdapter {
return new CouchDbMotdRepository(Motd.class, couchDbConnector(), false);
}
@Bean
public SessionRepository sessionRepository() throws Exception {
return new CouchDbSessionRepository(Session.class, couchDbConnector(), false);
}
@Bean
public UserRepository userRepository() throws Exception {
return new CouchDbUserRepository(DbUser.class, couchDbConnector(), false);
......
......@@ -17,32 +17,15 @@
*/
package de.thm.arsnova.dao;
import de.thm.arsnova.connector.model.Course;
import de.thm.arsnova.domain.CourseScore;
import de.thm.arsnova.entities.*;
import de.thm.arsnova.entities.transport.ImportExportSession;
import java.util.List;
import java.util.Map;
/**
* All methods the database must support.
*/
public interface IDatabaseDao {
Session getSessionFromKeyword(String keyword);
List<Session> getMySessions(User user, final int start, final int limit);
List<Session> getSessionsForUsername(String username, final int start, final int limit);
List<Session> getPublicPoolSessions();
List<Session> getMyPublicPoolSessions(User user);
Session saveSession(User user, Session session);
boolean sessionKeyAvailable(String keyword);
Question saveQuestion(Session session, Question question);
InterposedQuestion saveQuestion(Session session, InterposedQuestion question, User user);
......@@ -55,10 +38,6 @@ public interface IDatabaseDao {
int getSkillQuestionCount(Session session);
LoggedIn registerAsOnlineUser(User u, Session s);
Session updateSessionOwnerActivity(Session session);
List<String> getQuestionIds(Session session, User user);
int deleteQuestionWithAnswers(Question question);
......@@ -101,10 +80,6 @@ public interface IDatabaseDao {
void markInterposedQuestionAsRead(InterposedQuestion question);
List<Session> getMyVisitedSessions(User user, final int start, final int limit);
List<Session> getVisitedSessionsForUsername(String username, final int start, final int limit);
Question updateQuestion(Question question);
int deleteAnswers(Question question);
......@@ -113,27 +88,10 @@ public interface IDatabaseDao {
Answer updateAnswer(Answer answer);
Session getSessionFromId(String sessionId);
void deleteAnswer(String answerId);
void deleteInterposedQuestion(InterposedQuestion question);
List<Session> getCourseSessions(List<Course> courses);
Session updateSession(Session session);
Session changeSessionCreator(Session session, String newCreator);
/**
* Deletes a session and related data.
*
* @param session the session for deletion
*/
int[] deleteSession(Session session);
int[] deleteInactiveGuestSessions(long lastActivityBefore);
int deleteInactiveGuestVisitedSessionLists(long lastActivityBefore);
List<Question> getLectureQuestionsForUsers(Session session);
......@@ -182,22 +140,10 @@ public interface IDatabaseDao {
CourseScore getLearningProgress(Session session);
List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit);
List<SessionInfo> getPublicPoolSessionsInfo();
List<SessionInfo> getMyPublicPoolSessionsInfo(final User user);
List<SessionInfo> getMyVisitedSessionsInfo(User currentUser, final int start, final int limit);
int deleteAllPreparationAnswers(Session session);
int deleteAllLectureAnswers(Session session);
SessionInfo importSession(User user, ImportExportSession importSession);
ImportExportSession exportSession(String sessionkey, Boolean withAnswer, Boolean withFeedbackQuestions);
Statistics getStatistics();
List<String> getSubjects(Session session, String questionVariant);
......
......@@ -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;
......@@ -41,19 +43,23 @@ public class LearningProgressOptions implements Serializable {
public LearningProgressOptions() { }
@ApiModelProperty(required = true, value = "the type")
@JsonView({View.Persistence.class, View.Public.class})
public String getType() {
return type;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setType(String learningProgressType) {
this.type = learningProgressType;
}
@ApiModelProperty(required = true, value = "either lecture or preparation")
@JsonView({View.Persistence.class, View.Public.class})
public String getQuestionVariant() {
return questionVariant;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setQuestionVariant(String questionVariant) {
this.questionVariant = questionVariant;
}
......
......@@ -17,25 +17,24 @@
*/
package de.thm.arsnova.entities;
import com.fasterxml.jackson.annotation.JsonView;
import de.thm.arsnova.entities.serialization.View;
import java.util.ArrayList;
import java.util.List;
/**
* Once a user joins a session, this class is used to identify a returning user.
*/
public class LoggedIn {
private String _id;
private String _rev;
private String type;
public class LoggedIn implements Entity {
private String id;
private String rev;
private String user;
private String sessionId;
private long timestamp;
private List<VisitedSession> visitedSessions = new ArrayList<>();
private List<String> _conflicts;
public LoggedIn() {
this.type = "logged_in";
this.updateTimestamp();
}
......@@ -47,88 +46,80 @@ public class LoggedIn {
private boolean isAlreadyVisited(Session s) {
for (VisitedSession vs : this.visitedSessions) {
if (vs.get_id().equals(s.get_id())) {
if (vs.getId().equals(s.getId())) {
return true;
}
}
return false;
}
public void updateTimestamp() {
this.timestamp = System.currentTimeMillis();
}
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
@JsonView(View.Persistence.class)
public String getId() {
return id;
}
public String get_rev() {
return _rev;
@JsonView(View.Persistence.class)
public void setId(final String id) {
this.id = id;
}
public void set_rev(String _rev) {
this._rev = _rev;
@JsonView(View.Persistence.class)
public String getRevision() {
return rev;
}
public String getType() {
return type;
@JsonView(View.Persistence.class)
public void setRevision(final String rev) {
this.rev = rev;
}
public void setType(String type) {
this.type = type;
public void updateTimestamp() {
this.timestamp = System.currentTimeMillis();
}
@JsonView(View.Persistence.class)
public String getUser() {
return user;
}
@JsonView(View.Persistence.class)
public void setUser(String user) {
this.user = user;
}
@JsonView(View.Persistence.class)
public String getSessionId() {
return sessionId;
}
@JsonView(View.Persistence.class)
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
@JsonView(View.Persistence.class)
public long getTimestamp() {
return timestamp;
}
@JsonView(View.Persistence.class)
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
@JsonView(View.Persistence.class)
public List<VisitedSession> getVisitedSessions() {
return visitedSessions;
}
@JsonView(View.Persistence.class)
public void setVisitedSessions(List<VisitedSession> visitedSessions) {
this.visitedSessions = visitedSessions;
}
public List<String> get_conflicts() {
return _conflicts;
}
public void set_conflicts(List<String> _conflicts) {
this._conflicts = _conflicts;
}
public boolean hasConflicts() {
return !(_conflicts == null || _conflicts.isEmpty());
}
@Override
public String toString() {
return "LoggedIn [_id=" + _id + ", _rev=" + _rev + ", type=" + type
return "LoggedIn [id=" + id + ", rev=" + rev + ", type=" + getType()
+ ", user=" + user + ", sessionId=" + sessionId
+ ", timestamp=" + timestamp + ", visitedSessions="
+ visitedSessions + "]";
......
......@@ -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;
......@@ -72,143 +74,177 @@ public class SessionFeature implements Serializable {
public SessionFeature() { }
@JsonView({View.Persistence.class, View.Public.class})
public boolean isLecture() {
return lecture;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setLecture(boolean lecture) {
this.lecture = lecture;
}
@ApiModelProperty(required = true, value = "jitt")
@JsonView({View.Persistence.class, View.Public.class})
public boolean isJitt() {
return jitt;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setJitt(boolean jitt) {
this.jitt = jitt;
}
@ApiModelProperty(required = true, value = "feedback")
@JsonView({View.Persistence.class, View.Public.class})
public boolean isFeedback() {
return feedback;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setFeedback(boolean feedback) {
this.feedback = feedback;
}
@ApiModelProperty(required = true, value = "interposed")
@JsonView({View.Persistence.class, View.Public.class})
public boolean isInterposed() {
return interposed;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setInterposed(boolean interposed) {
this.interposed = interposed;
}
@ApiModelProperty(required = true, value = "peer instruction")
@JsonView({View.Persistence.class, View.Public.class})
public boolean isPi() {
return pi;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setPi(boolean pi) {
this.pi = pi;
}
@ApiModelProperty(required = true, value = "learning progress")
@JsonView({View.Persistence.class, View.Public.class})
public boolean isLearningProgress() {
return learningProgress;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setLearningProgress(boolean learningProgress) {
this.learningProgress = learningProgress;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isCustom() {
return custom;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setCustom(boolean custom) {
this.custom = custom;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isClicker() {
return clicker;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setClicker(boolean clicker) {
this.clicker = clicker;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isPeerGrading() {
return peerGrading;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setPeerGrading(boolean peerGrading) {
this.peerGrading = peerGrading;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isFlashcardFeature() {
return flashcardFeature;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setFlashcardFeature(boolean flashcardFeature) {
this.flashcardFeature = flashcardFeature;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isFlashcard() {
return flashcard;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setFlashcard(boolean flashcard) {
this.flashcard = flashcard;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isTotal() {
return total;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setTotal(boolean total) {
this.total = total;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isLiveFeedback() {
return liveFeedback;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setLiveFeedback(boolean liveFeedback) {
this.liveFeedback = liveFeedback;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isInterposedFeedback() {
return interposedFeedback;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setInterposedFeedback(boolean interposedFeedback) {
this.interposedFeedback = interposedFeedback;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isLiveClicker() {
return liveClicker;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setLiveClicker(boolean liveClicker) {
this.liveClicker = liveClicker;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isTwitterWall() {
return twitterWall;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setTwitterWall(boolean twitterWall) {
this.twitterWall = twitterWall;
}
@JsonView({View.Persistence.class, View.Public.class})
public boolean isSlides() {
return slides;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setSlides(boolean slides) {
this.slides = slides;
}
......
......@@ -17,11 +17,14 @@
*/
package de.thm.arsnova.entities;
import com.fasterxml.jackson.annotation.JsonView;
import de.thm.arsnova.entities.serialization.View;
/**
* A session a user has visited previously.
*/
public class VisitedSession {
private String _id;
private String id;
private String name;
private String keyword;
......@@ -29,38 +32,44 @@ public class VisitedSession {
}
public VisitedSession(Session s) {
this._id = s.get_id();
this.id = s.getId();
this.name = s.getName();
this.keyword = s.getKeyword();
}
public String get_id() {
return _id;
@JsonView({View.Persistence.class, View.Public.class})
public String getId() {
return id;
}
public void set_id(String _id) {
this._id = _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 String getName() {
return name;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setName(String name) {
this.name = name;
}
@JsonView({View.Persistence.class, View.Public.class})
public String getKeyword() {
return keyword;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setKeyword(String keyword) {
this.keyword = keyword;
}
@Override
public String toString() {
return "VisitedSession [_id=" + _id + ", name=" + name + ", keyword="
return "VisitedSession [id=" + id + ", name=" + name + ", keyword="
+ keyword + "]";
}
}
......@@ -24,6 +24,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.Session;
import java.util.HashMap;
import java.util.Map;
......@@ -35,6 +36,7 @@ public class CouchDbTypeFieldConverter implements Converter<Class<? extends Enti
typeMapping.put(LogEntry.class, "log");
typeMapping.put(DbUser.class, "userdetails");
typeMapping.put(Motd.class, "motd");
typeMapping.put(Session.class, "session");
}
@Override
......
......@@ -153,7 +153,6 @@ public class ImportExportSession {
s.setPpSubject(session.getPublicPool().getPpSubject());
s.setPpUniversity(session.getPublicPool().getPpUniversity());
// other fields
s.setType("session");
s.setCreator(user.getUsername());
s.setCreationTime(new Date().getTime());
return s;
......
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.persistance;
import de.thm.arsnova.connector.model.Course;
import de.thm.arsnova.entities.LoggedIn;
import de.thm.arsnova.entities.Session;
import de.thm.arsnova.entities.SessionInfo;
import de.thm.arsnova.entities.User;
import de.thm.arsnova.entities.transport.ImportExportSession;
import java.util.List;
public interface SessionRepository {
Session getSessionFromId(String sessionId);
Session getSessionFromKeyword(String keyword);
Session saveSession(User user, Session session);
Session updateSession(Session session);
/**
* Deletes a session and related data.
*
* @param session the session for deletion
*/
int[] deleteSession(Session session);
Session changeSessionCreator(Session session, String newCreator);
int[] deleteInactiveGuestSessions(long lastActivityBefore);
List<Session> getMySessions(User user, final int start, final int limit);
List<Session> getSessionsForUsername(String username, final int start, final int limit);
List<Session> getPublicPoolSessions();
List<Session> getMyPublicPoolSessions(User user);
boolean sessionKeyAvailable(String keyword);
Session updateSessionOwnerActivity(Session session);
List<Session> getVisitedSessionsForUsername(String username, final int start, final int limit);
List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit);
List<SessionInfo> getPublicPoolSessionsInfo();
List<SessionInfo> getMyPublicPoolSessionsInfo(final User user);
List<SessionInfo> getMyVisitedSessionsInfo(User currentUser, final int start, final int limit);
List<Session> getCourseSessions(List<Course> courses);
SessionInfo importSession(User user, ImportExportSession importSession);
ImportExportSession exportSession(String sessionkey, Boolean withAnswer, Boolean withFeedbackQuestions);
LoggedIn registerAsOnlineUser(final User user, final Session session);
}
......@@ -23,6 +23,7 @@ import de.thm.arsnova.entities.Question;
import de.thm.arsnova.entities.Session;
import de.thm.arsnova.entities.User;
import de.thm.arsnova.exceptions.UnauthorizedException;
import de.thm.arsnova.persistance.SessionRepository;
import org.pac4j.oauth.profile.facebook.FacebookProfile;
import org.pac4j.oauth.profile.google2.Google2Profile;
import org.pac4j.oauth.profile.twitter.TwitterProfile;
......@@ -47,6 +48,9 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
@Autowired
private IDatabaseDao dao;
@Autowired
private SessionRepository sessionRepository;
@Override
public boolean hasPermission(
final Authentication authentication,
......@@ -104,9 +108,9 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
final Object permission
) {
if (permission instanceof String && ("owner".equals(permission) || "write".equals(permission))) {
return dao.getSessionFromKeyword(targetId.toString()).getCreator().equals(username);
return sessionRepository.getSessionFromKeyword(targetId.toString()).getCreator().equals(username);
} else if (permission instanceof String && "read".equals(permission)) {
return dao.getSessionFromKeyword(targetId.toString()).isActive();
return sessionRepository.getSessionFromKeyword(targetId.toString()).isActive();
}
return false;
}
......@@ -119,7 +123,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
if (permission instanceof String && "owner".equals(permission)) {
final Question question = dao.getQuestion(targetId.toString());
if (question != null) {
final Session session = dao.getSessionFromId(question.getSessionId());
final Session session = sessionRepository.getSessionFromId(question.getSessionId());
return session != null && session.getCreator().equals(username);
}
......@@ -140,7 +144,7 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator {
return true;
}
// Allow deletion if requested by session owner
final Session session = dao.getSessionFromKeyword(question.getSessionId());