diff --git a/src/main/java/de/thm/arsnova/controller/UserController.java b/src/main/java/de/thm/arsnova/controller/UserController.java index 79d36774b91da28dae006d37f76afb0d8c78de52..e2dd6e1a9df6c6c30e3c14de7630d0cf4642a45f 100644 --- a/src/main/java/de/thm/arsnova/controller/UserController.java +++ b/src/main/java/de/thm/arsnova/controller/UserController.java @@ -17,7 +17,10 @@ */ package de.thm.arsnova.controller; +import de.thm.arsnova.dao.IDatabaseDao; import de.thm.arsnova.entities.DbUser; +import de.thm.arsnova.entities.LoggedIn; +import de.thm.arsnova.entities.User; import de.thm.arsnova.services.IUserService; import de.thm.arsnova.services.UserSessionService; import org.springframework.beans.factory.annotation.Autowired; @@ -46,6 +49,9 @@ public class UserController extends AbstractController { @Autowired private UserSessionService userSessionService; + @Autowired + private IDatabaseDao iDatabaseDao; + @RequestMapping(value = "/register", method = RequestMethod.POST) public void register(@RequestParam final String username, @RequestParam final String password, @@ -76,12 +82,16 @@ public class UserController extends AbstractController { } @RequestMapping(value = "/{username}/", method = RequestMethod.DELETE) - public void activate( + public void delete( @PathVariable final String username, final HttpServletRequest request, final HttpServletResponse response) { - if (null == userService.deleteDbUser(username)) { + LoggedIn loggedIn = iDatabaseDao.getLoggedInByUser(new User(username)); + if (null == loggedIn) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } else { + userService.deleteUserContent(loggedIn); + userService.anonymizeUser(loggedIn); } } diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java index 4028845db1495af16848d84e15d85c19c338ce04..9f59d2ae2b5ca48201d94bc0a9e24181059e92bd 100644 --- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java +++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java @@ -826,6 +826,35 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware } } + @Override + public LoggedIn getLoggedInByUser(User user) { + final NovaView view = new NovaView("logged_in/all"); + view.setKey(user.getUsername()); + view.setIncludeDocs(true); + LoggedIn l = null; + final ViewResults result = getDatabase().view(view); + for (Document doc : result.getResults()) { + final MorpherRegistry morpherRegistry = JSONUtils.getMorpherRegistry(); + final Morpher dynaMorpher = new BeanMorpher(VisitedSession.class, morpherRegistry); + morpherRegistry.registerMorpher(dynaMorpher); + + if (result.getJSONArray("rows").optJSONObject(0) == null) { + return null; + } + l = (LoggedIn) JSONObject.toBean( + doc.getJSONObject().getJSONObject("doc"), + LoggedIn.class + ); + @SuppressWarnings("unchecked") + final Collection<VisitedSession> vs = JSONArray.toCollection( + doc.getJSONObject().getJSONObject("doc").getJSONArray("visitedSessions"), + VisitedSession.class + ); + l.setVisitedSessions(new ArrayList<>(vs)); + } + return l; + } + @Override @CachePut(value = "sessions") public Session updateSessionOwnerActivity(final Session session) { @@ -909,6 +938,35 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware getDatabase().deleteDocument(d); } + @CacheEvict("answers") + @Override + public List<Answer> getUserAnswersForSession(String username, String sessionId) { + final NovaView view = new NovaView("answer/by_user"); + view.setKey(username, sessionId); + view.setIncludeDocs(true); + + List<Answer> answers = new ArrayList<>(); + + List<Document> results = this.getDatabase().view(view).getResults(); + + final List<List<Document>> partitions = Lists.partition(results, BULK_PARTITION_SIZE); + for (List<Document> partition: partitions) { + for (Document doc : partition) { + if (!"".equals(doc.optString("error"))) { + // Skip documents we could not load. Maybe they were deleted. + continue; + } + Answer a = (Answer) JSONObject.toBean( + doc.getJSONObject().getJSONObject("doc"), + Answer.class + ); + answers.add(a); + } + } + + return answers; + } + @CacheEvict("answers") @Override public int deleteAnswers(final Question question) { @@ -986,6 +1044,8 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware } } + + @Override public List<Answer> getAnswers(final Question question, final int piRound) { final String questionId = question.get_id(); @@ -2234,16 +2294,22 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware questionIds.add(q.get_id()); allQuestions.add(d); } - final NovaView bulkView = new NovaView("answer/cleanup"); - bulkView.setKeys(questionIds); - bulkView.setIncludeDocs(true); - final List<Document> result = getDatabase().view(bulkView).getResults(); final List<Document> allAnswers = new ArrayList<>(); - for (Document a : result) { - final Document d = new Document(a.getJSONObject("doc")); - d.put("_deleted", true); - allAnswers.add(d); + int resultCounter = 0; + + for (String qId : questionIds) { + final NovaView bulkView = new NovaView("answer/cleanup"); + bulkView.setKey(qId); + bulkView.setIncludeDocs(true); + final List<Document> result = getDatabase().view(bulkView).getResults(); + + for (Document a : result) { + final Document d = new Document(a.getJSONObject("doc")); + d.put("_deleted", true); + allAnswers.add(d); + } + resultCounter += result.size(); } try { @@ -2251,7 +2317,7 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware deleteList.addAll(allQuestions); getDatabase().bulkSaveDocuments(deleteList.toArray(new Document[deleteList.size()])); - return new int[] {deleteList.size(), result.size()}; + return new int[] {deleteList.size(), resultCounter}; } catch (IOException e) { logger.error("Could not bulk delete questions and answers.", e); } @@ -2402,6 +2468,41 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware return 0; } + @Override + public List<LoggedIn> getInactiveLoggedIn(long lastActivityBefore) { + final List<LoggedIn> loggedInDocs = new ArrayList<>(); + NovaView view = new NovaView("logged_in/by_last_activity"); + view.setEndKey(lastActivityBefore); + view.setIncludeDocs(true); + final MorpherRegistry morpherRegistry = JSONUtils.getMorpherRegistry(); + final Morpher dynaMorpher = new BeanMorpher(VisitedSession.class, morpherRegistry); + morpherRegistry.registerMorpher(dynaMorpher); + List<Document> results = this.getDatabase().view(view).getResults(); + + final List<List<Document>> partitions = Lists.partition(results, BULK_PARTITION_SIZE); + for (List<Document> partition: partitions) { + for (Document doc : partition) { + if (!"".equals(doc.optString("error"))) { + // Skip documents we could not load. Maybe they were deleted. + continue; + } + LoggedIn l = (LoggedIn) JSONObject.toBean( + doc.getJSONObject().getJSONObject("doc"), + LoggedIn.class + ); + @SuppressWarnings("unchecked") + final Collection<VisitedSession> vs = JSONArray.toCollection( + doc.getJSONObject().getJSONObject("doc").getJSONArray("visitedSessions"), + VisitedSession.class + ); + l.setVisitedSessions(new ArrayList<>(vs)); + loggedInDocs.add(l); + } + } + + return loggedInDocs; + } + @Override public SessionInfo importSession(User user, ImportExportSession importSession) { final Session session = this.saveSession(user, importSession.generateSessionEntity(user)); @@ -2832,4 +2933,68 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware return null; } + @Override + public void bulkUpdateAnswers(List<Answer> answers) { + List<Document> docs = new ArrayList<>(); + for (Answer a : answers) { + if (a.get_id() != null) { + final JSONObject json = JSONObject.fromObject(a); + docs.add(new Document(json)); + } + } + try { + getDatabase().bulkSaveDocuments(docs.toArray(new Document[docs.size()])); + } catch (IOException e) { + logger.error("Could not update Answers document {}.", docs, e); + } + } + + @Override + public void bulkUpdateInterposedQuestion(List<InterposedQuestion> interposedQuestions) { + List<Document> docs = new ArrayList<>(); + for (InterposedQuestion a : interposedQuestions) { + if (a.get_id() != null) { + final JSONObject json = JSONObject.fromObject(a); + docs.add(new Document(json)); + } + } + try { + getDatabase().bulkSaveDocuments(docs.toArray(new Document[docs.size()])); + } catch (IOException e) { + logger.error("Could not update interposed question document {}.", docs, e); + } + } + + @Override + public void updateLoggedIn(LoggedIn l) { + if (l.get_id() == null) { + return; + } + try { + final JSONObject json = JSONObject.fromObject(l); + final Document doc = new Document(json); + getDatabase().saveDocument(doc); + } catch (IOException e) { + logger.error("Could not update LoggedIn document {}.", l, e); + } + } + + @Override + public void bulkDeleteInterposedQuestionsForSessionAndUser(String sessionId, String username) { + final NovaView view = new NovaView("interposed_question/by_session_and_creator"); + view.setKey(sessionId, username); + final List<Document> result = getDatabase().view(view).getResults(); + final List<Document> allInterposedQuestions = new ArrayList<>(); + for (Document a : result) { + final Document d = new Document(a.getJSONObject("doc")); + d.put("_deleted", true); + allInterposedQuestions.add(d); + } + try { + getDatabase().bulkSaveDocuments(allInterposedQuestions.toArray(new Document[allInterposedQuestions.size()])); + } catch (IOException e) { + logger.error("Could not bulk delete interposed questions.", e); + } + } + } diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java index 052957180379141785cd1ada8dbef49a9cb9dd0e..6ad2f30281dc422386b7b523a38ed6f1d59d3675 100644 --- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java +++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java @@ -103,6 +103,8 @@ public interface IDatabaseDao { LoggedIn registerAsOnlineUser(User u, Session s); + LoggedIn getLoggedInByUser(User user); + Session updateSessionOwnerActivity(Session session); List<String> getQuestionIds(Session session, User user); @@ -113,6 +115,8 @@ public interface IDatabaseDao { List<String> getUnAnsweredQuestionIds(Session session, User user); + List<Answer> getUserAnswersForSession(String username, String sessionId); + Answer getMyAnswer(User me, String questionId, int piRound); List<Answer> getAnswers(Question question, int piRound); @@ -234,6 +238,8 @@ public interface IDatabaseDao { int deleteInactiveUsers(long lastActivityBefore); + List<LoggedIn> getInactiveLoggedIn(long lastActivityBefore); + CourseScore getLearningProgress(Session session); List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit); @@ -291,4 +297,12 @@ public interface IDatabaseDao { MotdList getMotdListForUser(final String username); MotdList createOrUpdateMotdList(MotdList motdlist); + + void bulkUpdateAnswers(List<Answer> answers); + + void bulkUpdateInterposedQuestion(List<InterposedQuestion> interposedQuestions); + + void updateLoggedIn(LoggedIn l); + + void bulkDeleteInterposedQuestionsForSessionAndUser(String sessionId, String username); } diff --git a/src/main/java/de/thm/arsnova/entities/LoggedIn.java b/src/main/java/de/thm/arsnova/entities/LoggedIn.java index 1ab7caa708467f0518a1941f5babebfd7967960c..153c13d4482a76ac5c90a5850b61851efde8f256 100644 --- a/src/main/java/de/thm/arsnova/entities/LoggedIn.java +++ b/src/main/java/de/thm/arsnova/entities/LoggedIn.java @@ -33,6 +33,7 @@ public class LoggedIn { private long timestamp; private List<VisitedSession> visitedSessions = new ArrayList<>(); private List<String> _conflicts; + private boolean anonymized; public LoggedIn() { this.type = "logged_in"; @@ -126,6 +127,14 @@ public class LoggedIn { return !(_conflicts == null || _conflicts.isEmpty()); } + public boolean isAnonymized() { + return anonymized; + } + + public void setAnonymized(boolean anonymized) { + this.anonymized = anonymized; + } + @Override public String toString() { return "LoggedIn [_id=" + _id + ", _rev=" + _rev + ", 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 da31bee08ccb0ecabfc126e63d7638f51c85ab28..18632930d3c8aa7f14551226f963a98f1679c897 100644 --- a/src/main/java/de/thm/arsnova/entities/User.java +++ b/src/main/java/de/thm/arsnova/entities/User.java @@ -79,6 +79,11 @@ public class User implements Serializable { setType(LDAP); } + public User(String username) { + setUsername(username); + setType(User.ANONYMOUS); + } + public String getUsername() { return username; } diff --git a/src/main/java/de/thm/arsnova/services/IQuestionService.java b/src/main/java/de/thm/arsnova/services/IQuestionService.java index 2b04883598eb3ac9d4aa5cb1db1a84a2fc21cea8..ff95d8b42ebb391b2875f126e6f287bc6eade461 100644 --- a/src/main/java/de/thm/arsnova/services/IQuestionService.java +++ b/src/main/java/de/thm/arsnova/services/IQuestionService.java @@ -22,6 +22,7 @@ import de.thm.arsnova.entities.InterposedQuestion; import de.thm.arsnova.entities.InterposedReadingCount; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.User; +import de.thm.arsnova.entities.VisitedSession; import java.util.List; import java.util.Map; @@ -72,6 +73,8 @@ public interface IQuestionService { List<Answer> getFreetextAnswers(String questionId, int offset, int limit); + List<Answer> getFreetextAnswersInternal(String questionId); + List<Answer> getMyAnswers(String sessionKey); int getTotalAnswerCount(String sessionKey); @@ -168,4 +171,6 @@ public interface IQuestionService { List<Question> replaceImageData(List<Question> questions); + void anonymizeParticipant(VisitedSession session, String oldUsername, String anonymizedUsername); + } diff --git a/src/main/java/de/thm/arsnova/services/IUserService.java b/src/main/java/de/thm/arsnova/services/IUserService.java index 9b90a24fbc221e20b5961c76e3fbdc5e8a7cf988..930f3eb5fc6cc4f2a670c4a038372014cf241056 100644 --- a/src/main/java/de/thm/arsnova/services/IUserService.java +++ b/src/main/java/de/thm/arsnova/services/IUserService.java @@ -18,6 +18,7 @@ package de.thm.arsnova.services; import de.thm.arsnova.entities.DbUser; +import de.thm.arsnova.entities.LoggedIn; import de.thm.arsnova.entities.User; import java.util.Map; @@ -69,4 +70,10 @@ public interface IUserService { void initiatePasswordReset(String username); boolean resetPassword(DbUser dbUser, String key, String password); + + void deleteUserContent(LoggedIn user); + + void anonymizeUser(LoggedIn username); + + LoggedIn getLoggedInFromUser(User user); } diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/QuestionService.java index 8d7415b6684af9d543ff81d26c92f80b3ce17782..eb00fc9885d51b09c9219cd272e5cecf51b9a323 100644 --- a/src/main/java/de/thm/arsnova/services/QuestionService.java +++ b/src/main/java/de/thm/arsnova/services/QuestionService.java @@ -25,6 +25,7 @@ import de.thm.arsnova.entities.InterposedReadingCount; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.Session; import de.thm.arsnova.entities.User; +import de.thm.arsnova.entities.VisitedSession; import de.thm.arsnova.events.*; import de.thm.arsnova.exceptions.BadRequestException; import de.thm.arsnova.exceptions.ForbiddenException; @@ -46,6 +47,7 @@ import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import java.util.stream.Collectors; /** * Performs all question, interposed question, and answer related operations. @@ -463,7 +465,6 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis return databaseDao.getAllAnswers(question); } } - @Override @PreAuthorize("isAuthenticated()") public int getAnswerCount(final String questionId) { @@ -527,6 +528,17 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis return answers; } + @Override + @PreAuthorize("isAuthenticated()") + public List<Answer> getFreetextAnswersInternal(final String questionId) { + final List<Answer> answers = databaseDao.getFreetextAnswers(questionId, 0, 0); + if (answers == null) { + throw new NotFoundException(); + } + + return answers; + } + @Override @PreAuthorize("isAuthenticated()") public List<Answer> getMyAnswers(final String sessionKey) { @@ -1023,4 +1035,35 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis return imageData; } + + @Override + public void anonymizeParticipant(VisitedSession vs, String oldUsername, String anonymizedUsername) { + Session helperSession = new Session(); + helperSession.set_id(vs.get_id()); + helperSession.setKeyword(vs.getKeyword()); + User helperUser = new User(oldUsername); + + List<Answer> answers = new ArrayList<>(); + List<Answer> questionAnswers = databaseDao.getUserAnswersForSession(oldUsername, vs.get_id()); + for (Answer a : questionAnswers) { + if (a.getUser().equals(oldUsername)) { + a.setUser(anonymizedUsername); + answers.add(a); + } + } + + List<InterposedQuestion> comments = + databaseDao.getInterposedQuestions(helperSession, helperUser, 0, 0); + for (InterposedQuestion c : comments) { + c.setCreator(anonymizedUsername); + c.setSessionId(vs.get_id()); + } + + if (!comments.isEmpty()) { + databaseDao.bulkUpdateInterposedQuestion(comments); + } + if (!answers.isEmpty()) { + databaseDao.bulkUpdateAnswers(answers); + } + } } diff --git a/src/main/java/de/thm/arsnova/services/UserService.java b/src/main/java/de/thm/arsnova/services/UserService.java index 276b0357b1f1bd05c7bafc760294c9c40daec3ac..3b3b5f07240c3a784d6379cd9ee6313ed5ee5bc0 100644 --- a/src/main/java/de/thm/arsnova/services/UserService.java +++ b/src/main/java/de/thm/arsnova/services/UserService.java @@ -21,7 +21,11 @@ import com.codahale.metrics.annotation.Gauge; import de.thm.arsnova.dao.IDatabaseDao; import de.thm.arsnova.entities.DbUser; import de.thm.arsnova.entities.User; +import de.thm.arsnova.entities.LoggedIn; +import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.VisitedSession; import de.thm.arsnova.exceptions.BadRequestException; +import de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.NotFoundException; import de.thm.arsnova.exceptions.UnauthorizedException; import org.apache.commons.lang.RandomStringUtils; @@ -47,6 +51,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.keygen.BytesKeyGenerator; import org.springframework.security.crypto.keygen.KeyGenerators; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; @@ -77,6 +82,8 @@ import java.util.regex.Pattern; @MonitorGauges public class UserService implements IUserService { + private IQuestionService questionService; + private static final int LOGIN_TRY_RESET_DELAY_MS = 30 * 1000; private static final int LOGIN_BAN_RESET_DELAY_MS = 2 * 60 * 1000; @@ -88,6 +95,8 @@ public class UserService implements IUserService { private static final long ACTIVATION_KEY_CHECK_INTERVAL_MS = 30 * 60 * 1000L; private static final long ACTIVATION_KEY_DURABILITY_MS = 6 * 60 * 60 * 1000L; + private static final long USER_INACTIVITY_CHECK_INTERVAL_MS = 30 * 60 * 1000L; + private static final Logger logger = LoggerFactory.getLogger(UserService.class); private static final ConcurrentHashMap<UUID, User> socketid2user = new ConcurrentHashMap<>(); @@ -140,6 +149,9 @@ public class UserService implements IUserService { @Value("${security.admin-accounts}") private String[] adminAccounts; + @Value("${user.cleanup-days:0}") + private int userCleanupThresholdDays; + private Pattern mailPattern; private BytesKeyGenerator keygen; private BCryptPasswordEncoder encoder; @@ -151,6 +163,11 @@ public class UserService implements IUserService { loginBans = Collections.synchronizedSet(new HashSet<String>()); } + @Autowired + public void setQuestionService(final IQuestionService questionService) { + this.questionService = questionService; + } + @Scheduled(fixedDelay = LOGIN_TRY_RESET_DELAY_MS) public void resetLoginTries() { if (!loginTries.isEmpty()) { @@ -167,14 +184,34 @@ public class UserService implements IUserService { } } + /* + * This deletes users that did register but not activate their account + */ @Scheduled(fixedDelay = ACTIVATION_KEY_CHECK_INTERVAL_MS) public void deleteInactiveUsers() { - logger.info("Delete inactive users."); + logger.info("Delete not activated users."); long unixTime = System.currentTimeMillis(); long lastActivityBefore = unixTime - ACTIVATION_KEY_DURABILITY_MS; databaseDao.deleteInactiveUsers(lastActivityBefore); } + /* + * This triggers anonymization of users that have been inactive for userCleanupThresholdDays amount of days + */ + @Scheduled(fixedDelay = USER_INACTIVITY_CHECK_INTERVAL_MS) + public void anonymizeInactiveUsers() { + if (userCleanupThresholdDays > 0) { + logger.info("Anonymize inactive users"); + long unixTime = System.currentTimeMillis(); + long lastActivityBefore = unixTime - userCleanupThresholdDays * 24 * 60 * 60 * 1000L; + List<LoggedIn> loggedIns = databaseDao.getInactiveLoggedIn(lastActivityBefore); + for (LoggedIn l : loggedIns) { + deleteUserContent(l); + anonymizeUser(l); + } + } + } + @Override public User getCurrentUser() { final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); @@ -533,6 +570,49 @@ public class UserService implements IUserService { return true; } + @Override + public void deleteUserContent(LoggedIn l) { + User user = getCurrentUser(); + if (!user.isAdmin() && !user.getUsername().equals(l.getUser())) { + throw new ForbiddenException(); + } + + List<Session> userSessions = databaseDao.getSessionsForUsername(l.getUser(), 0, 0); + for (Session s : userSessions) { + databaseDao.deleteSession(s); + } + } + + @Override + public void anonymizeUser(LoggedIn l) { + User user = getCurrentUser(); + if (!user.isAdmin() && !user.getUsername().equals(l.getUser())) { + throw new ForbiddenException(); + } + + String username = l.getUser(); + PasswordEncoder pe = new BCryptPasswordEncoder(); + String anonymizedUsername = pe.encode(username + l.getTimestamp()); + l.setUser(anonymizedUsername); + l.setAnonymized(true); + for (VisitedSession vs : l.getVisitedSessions()) { + questionService.anonymizeParticipant(vs, username, anonymizedUsername); + } + // this trims the document because it won't be deleted and stays forever + l.setVisitedSessions(new ArrayList<>()); + DbUser dbUser = this.getDbUser(username); + if (dbUser != null) { + dbUser.setUsername(anonymizedUsername); + databaseDao.createOrUpdateUser(dbUser); + } + databaseDao.updateLoggedIn(l); + } + + @Override + public LoggedIn getLoggedInFromUser(User user) { + return databaseDao.getLoggedInByUser(user); + } + private void sendEmail(DbUser dbUser, String subject, String body) { MimeMessage msg = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(msg, "UTF-8"); diff --git a/src/main/resources/arsnova.properties.example b/src/main/resources/arsnova.properties.example index 34fa9b0a1d80f6a0a91fa1bbd5e3d77c239494e8..93d8c87c988e8e1a121aeb24a1a19ae0e9c697a5 100644 --- a/src/main/resources/arsnova.properties.example +++ b/src/main/resources/arsnova.properties.example @@ -246,6 +246,9 @@ session.demo-id= # Delete guest sessions automatically after X days of owner inactivity. #session.guest-session.cleanup-days=180 +# Anonymize user content for users with X days of inactivity +#user.cleanup-days=180 + # Label underneath ARSnova logo ui.slogan=Audience Response System diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java index 0eaf46922750056b7f8881b3e04469b2ee5d564e..6fa3358eaddaf71d4a07999f6da0312205f766bd 100644 --- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java +++ b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java @@ -206,6 +206,12 @@ public class StubDatabaseDao implements IDatabaseDao { return new LoggedIn(); } + @Override + public LoggedIn getLoggedInByUser(User user) { + // TODO Auto-generated method stub + return null; + } + @Override public Session updateSessionOwnerActivity(Session session) { // TODO Auto-generated method stub @@ -754,4 +760,36 @@ public class StubDatabaseDao implements IDatabaseDao { // TODO Auto-generated method stub return null; } + + @Override + public void bulkUpdateAnswers(List<Answer> answers) { + // TODO Auto-generated method stub + } + + @Override + public void updateLoggedIn(LoggedIn l) { + // TODO Auto-generated method stub + } + + @Override + public void bulkDeleteInterposedQuestionsForSessionAndUser(String sessionId, String username) { + // TODO Auto-generated method stub + } + + @Override + public List<LoggedIn> getInactiveLoggedIn(long lastActivityBefore) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void bulkUpdateInterposedQuestion(List<InterposedQuestion> interposedQuestions) { + // TODO Auto-generated method stub + } + + @Override + public List<Answer> getUserAnswersForSession(String username, String sessionId) { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/test/resources/arsnova.properties.example b/src/test/resources/arsnova.properties.example index 34fa9b0a1d80f6a0a91fa1bbd5e3d77c239494e8..93d8c87c988e8e1a121aeb24a1a19ae0e9c697a5 100644 --- a/src/test/resources/arsnova.properties.example +++ b/src/test/resources/arsnova.properties.example @@ -246,6 +246,9 @@ session.demo-id= # Delete guest sessions automatically after X days of owner inactivity. #session.guest-session.cleanup-days=180 +# Anonymize user content for users with X days of inactivity +#user.cleanup-days=180 + # Label underneath ARSnova logo ui.slogan=Audience Response System