diff --git a/CHANGELOG.md b/CHANGELOG.md index 640e3a0c2823e43f7b5f56f20788c12fea7d5c72..0e5cf98e7837a1e9d60a9500940a1396470a77cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 2.1 +Major features: +* Public Pool (experimental): It is now possible to share sessions with other + users in a pool of public sessions. Other users can create their own copies of + shared sessions. This feature can be enabled in the arsnova.properties + configuration. + +Minor features and changes: +* Adjustments to correctly handle requests for imports from the frontend. +* Some communication between the frontend and backend has been optimized for + improved performance. +* Additional configuration parameters for tracking, session export and import, a + demo session and a blog URL have been introduced. + +**This version is brought to you by:** +Project management: Klaus Quibeldey-Cirkel +Lead programming: Andreas Gärtner, Daniel Gerhardt, Christoph Thelen +Contributions: Felix Schmidt, Artjom Siebert, Daniel Vogel +Sponsoring: [AG QLS](https://www.thm.de/zqe/qmnetzwerk/agqls), +[HMWK](https://wissenschaft.hessen.de/wissenschaft/it-neue-medien/kompetenznetz-e-learning-hessen) + + ## 2.0.2 This release updates dependencies. The updated library for Socket.IO support fixes memory leaks and disables SSL 3.0 support (POODLE vulnerability). diff --git a/pom.xml b/pom.xml index bf19387af6259ce59a052e43b43f3dd91054d81d..5f8035d461265a5f049b1f4d48867b278f63c371 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>de.thm.arsnova</groupId> <artifactId>arsnova-backend</artifactId> - <version>2.1.0-SNAPSHOT</version> + <version>2.2.0-SNAPSHOT</version> <packaging>war</packaging> <properties> @@ -86,7 +86,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> - <version>2.7</version> + <version>2.8</version> <configuration> <dependencyLocationsEnabled>false</dependencyLocationsEnabled> <dependencyDetailsEnabled>false</dependencyDetailsEnabled> @@ -95,7 +95,7 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> - <version>2.9.1</version> + <version>2.10.1</version> <configuration></configuration> </plugin> </plugins> @@ -314,7 +314,7 @@ <dependency> <groupId>de.thm.arsnova.connector</groupId> <artifactId>connector-client</artifactId> - <version>0.72.0</version> + <version>0.73.0</version> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> @@ -350,7 +350,7 @@ <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> - <version>9.2.6.v20141205</version> + <version>9.2.7.v20150116</version> <configuration> <scanIntervalSeconds>1</scanIntervalSeconds> <webApp> @@ -409,7 +409,7 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> - <version>1.6</version> + <version>1.7</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> diff --git a/src/main/java/de/thm/arsnova/config/ExtraConfig.java b/src/main/java/de/thm/arsnova/config/ExtraConfig.java index 922b7f3f27b71efb4dd8beabc09625af9f5ec9ea..0e6ba84af71b0f683501b2501b4f14608f9abc3c 100644 --- a/src/main/java/de/thm/arsnova/config/ExtraConfig.java +++ b/src/main/java/de/thm/arsnova/config/ExtraConfig.java @@ -19,6 +19,9 @@ package de.thm.arsnova.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; @@ -33,6 +36,7 @@ import de.thm.arsnova.connector.client.ConnectorClientImpl; import de.thm.arsnova.socket.ARSnovaSocketIOServer; @Configuration +@EnableCaching public class ExtraConfig { @Autowired @@ -98,4 +102,9 @@ public class ExtraConfig { socketServer.setStorepass(socketStorepass); return socketServer; } + + @Bean + public CacheManager cacheManager() { + return new ConcurrentMapCacheManager(); + } } diff --git a/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java b/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java index 32ee01d84c132c605cebe118dee3f44b4215e30c..6b5a2bc098b5ba29f2ee66e593a4e7e14f7de2cd 100644 --- a/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java +++ b/src/main/java/de/thm/arsnova/controller/AudienceQuestionController.java @@ -54,8 +54,8 @@ public class AudienceQuestionController extends AbstractController { @RequestMapping(value = "/readcount", method = RequestMethod.GET) @DeprecatedApi - public final InterposedReadingCount getUnreadInterposedCount(@RequestParam("sessionkey") final String sessionkey) { - return questionService.getInterposedReadingCount(sessionkey); + public final InterposedReadingCount getUnreadInterposedCount(@RequestParam("sessionkey") final String sessionkey, String user) { + return questionService.getInterposedReadingCount(sessionkey, user); } @RequestMapping(value = "/", method = RequestMethod.GET) diff --git a/src/main/java/de/thm/arsnova/controller/ConfigurationController.java b/src/main/java/de/thm/arsnova/controller/ConfigurationController.java index 0077da747a94e884f03d7ce4ffc9bec46f3fb329..1d6ed3fd007ea53713546c54c69b3307b8d88eed 100644 --- a/src/main/java/de/thm/arsnova/controller/ConfigurationController.java +++ b/src/main/java/de/thm/arsnova/controller/ConfigurationController.java @@ -102,16 +102,16 @@ public class ConfigurationController extends AbstractController { @Value("${question.answer-option-limit:8}") private String answerOptionLimit; - + @Value("${upload.filesize_b:}") private String maxUploadFilesize; @Value("${question.parse-answer-option-formatting:false}") private String parseAnswerOptionFormatting; - + @Value("${pp.subjects}") private String ppSubjects; - + @Value("${pp.licenses}") private String ppLicenses; @@ -129,16 +129,19 @@ public class ConfigurationController extends AbstractController { @Value("${tracking.site-id}") private String trackingSiteId; - - @Value("${optional.demoSessionKey:}") + + @Value("${session.demo-id:}") private String demoSessionKey; + @Value("${optional.arsnova-slogan:}") + private String arsnovaSlogan; + @Value("${pp.session-levels.de}") private String ppLevelsDe; @Value("${pp.session-levels.en}") private String ppLevelsEn; - + @RequestMapping(method = RequestMethod.GET) @ResponseBody public final HashMap<String, Object> getConfiguration(HttpServletRequest request) { @@ -168,7 +171,7 @@ public class ConfigurationController extends AbstractController { if (!"".equals(blogUrl)) { config.put("blogUrl", blogUrl); } - if (!"".equals(presenterDocumentationUrl)) { + if (!"".equals(presenterDocumentationUrl)) { config.put("presenterDocumentationUrl", presenterDocumentationUrl); } if (!"".equals(overlayUrl)) { @@ -186,6 +189,9 @@ public class ConfigurationController extends AbstractController { if (!"".equals(demoSessionKey)) { config.put("demoSessionKey", demoSessionKey); } + if (!"".equals(arsnovaSlogan)) { + config.put("arsnovaSlogan", arsnovaSlogan); + } if (!"".equals(maxUploadFilesize)) { config.put("maxUploadFilesize", maxUploadFilesize); } @@ -203,13 +209,16 @@ public class ConfigurationController extends AbstractController { features.put("gridSquare", "true".equals(gridSquareEnabled)); features.put("sessionImportExport", "true".equals(sessionImportExportEnabled)); features.put("publicPool", "true".equals(publicPoolEnabled)); - - // add public pool configuration - config.put("publicPool", publicPool); - - publicPool.put("subjects", ppSubjects); - publicPool.put("licenses", ppLicenses); - publicPool.put("logoMaxFilesize", ppLogoMaxFilesize); + + // add public pool configuration on demand + if (features.get("publicPool")) { + config.put("publicPool", publicPool); + publicPool.put("subjects", ppSubjects); + publicPool.put("licenses", ppLicenses); + publicPool.put("logoMaxFilesize", ppLogoMaxFilesize); + publicPool.put("levelsDe", ppLevelsDe); + publicPool.put("levelsEn", ppLevelsEn); + } if (!"".equals(trackingTrackerUrl)) { HashMap<String, String> tracking = new HashMap<String, String>(); @@ -219,8 +228,6 @@ public class ConfigurationController extends AbstractController { tracking.put("trackerUrl", trackingTrackerUrl); tracking.put("siteId", trackingSiteId); } - publicPool.put("levelsDe", ppLevelsDe); - publicPool.put("levelsEn", ppLevelsEn); config.put("grid", gridImageMaxFileSize); diff --git a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java index 3e4f7b3d5a4d23b48b83f81e4634cbc0b0c703ea..e0d3f79b56db1cbc639e2bf196075b6962961931 100644 --- a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java +++ b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java @@ -38,7 +38,6 @@ import org.springframework.web.bind.annotation.RestController; import de.thm.arsnova.entities.Answer; import de.thm.arsnova.entities.Question; import de.thm.arsnova.exceptions.BadRequestException; -import de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.NoContentException; import de.thm.arsnova.exceptions.NotFoundException; import de.thm.arsnova.services.IQuestionService; @@ -105,11 +104,11 @@ public class LecturerQuestionController extends AbstractController { ) { boolean p = true; List<Question> questions; - + if (publish != null) { p = publish; } - + if (lectureQuestionsOnly) { questions = questionService.getLectureQuestions(sessionkey); questionService.publishQuestions(sessionkey, publish, questions); @@ -310,10 +309,10 @@ public class LecturerQuestionController extends AbstractController { @RequestMapping(value = "/{questionId}/answer/", method = RequestMethod.POST) public final Answer saveAnswer( @PathVariable final String questionId, - @RequestBody final Answer answer, + @RequestBody final de.thm.arsnova.entities.transport.Answer answer, final HttpServletResponse response ) { - return questionService.saveAnswer(answer); + return questionService.saveAnswer(questionId, answer); } @RequestMapping(value = "/{questionId}/answer/{answerId}", method = RequestMethod.PUT) @@ -384,10 +383,10 @@ public class LecturerQuestionController extends AbstractController { questionService.getAnswerCount(questionId), questionService.getAbstentionAnswerCount(questionId) ); - + return list; } - + @RequestMapping(value = "/{questionId}/freetextanswer/", method = RequestMethod.GET) public final List<Answer> getFreetextAnswers(@PathVariable final String questionId) { return questionService.getFreetextAnswers(questionId); diff --git a/src/main/java/de/thm/arsnova/controller/SecurityExceptionControllerAdvice.java b/src/main/java/de/thm/arsnova/controller/SecurityExceptionControllerAdvice.java index 2b4608d972fcd6d097794190064597bddaaec9a2..6628671bddcedfac7420c3b14aa967fde809441a 100644 --- a/src/main/java/de/thm/arsnova/controller/SecurityExceptionControllerAdvice.java +++ b/src/main/java/de/thm/arsnova/controller/SecurityExceptionControllerAdvice.java @@ -37,6 +37,7 @@ import de.thm.arsnova.exceptions.NotFoundException; import de.thm.arsnova.exceptions.NotImplementedException; import de.thm.arsnova.exceptions.PreconditionFailedException; import de.thm.arsnova.exceptions.UnauthorizedException; +import de.thm.arsnova.exceptions.RequestEntityTooLargeException; @ControllerAdvice public class SecurityExceptionControllerAdvice { @@ -98,4 +99,9 @@ public class SecurityExceptionControllerAdvice { @ExceptionHandler(NotImplementedException.class) public void handleNotImplementedException(final Exception e, final HttpServletRequest request) { } + + @ResponseStatus(HttpStatus.REQUEST_ENTITY_TOO_LARGE) + @ExceptionHandler(RequestEntityTooLargeException.class) + public void handleRequestEntityTooLargeException(final Exception e, final HttpServletRequest request) { + } } diff --git a/src/main/java/de/thm/arsnova/controller/SessionController.java b/src/main/java/de/thm/arsnova/controller/SessionController.java index 890e9f337f0e5dbbb928f1f114f75001901218af..f76d377d31c73c90b972d34eea637833870718c2 100644 --- a/src/main/java/de/thm/arsnova/controller/SessionController.java +++ b/src/main/java/de/thm/arsnova/controller/SessionController.java @@ -30,7 +30,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.core.token.Sha512DigestUtils; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -42,6 +41,7 @@ import org.springframework.web.bind.annotation.RestController; import de.thm.arsnova.connector.model.Course; import de.thm.arsnova.entities.Session; import de.thm.arsnova.entities.SessionInfo; +import de.thm.arsnova.entities.transport.ImportExportSession; import de.thm.arsnova.exceptions.UnauthorizedException; import de.thm.arsnova.services.ISessionService; import de.thm.arsnova.services.IUserService; @@ -65,13 +65,7 @@ public class SessionController extends AbstractController { @RequestMapping(value = "/{sessionkey}", method = RequestMethod.GET) public final Session joinSession(@PathVariable final String sessionkey) { - final Session session = sessionService.getSession(sessionkey); - if (!session.isCreator(userService.getCurrentUser())) { - session.setCreator("NOT VISIBLE TO YOU"); - } else { - session.setCreator(Sha512DigestUtils.shaHex(session.getCreator())); - } - return session; + return Session.anonymizedCopy(sessionService.getSession(sessionkey)); } @RequestMapping(value = "/{sessionkey}", method = RequestMethod.DELETE) @@ -203,10 +197,10 @@ public class SessionController extends AbstractController { } @RequestMapping(value = "/publicpool", method = RequestMethod.GET) - public final List<Session> getPublicPoolSessions( + public final List<SessionInfo> getPublicPoolSessions( final HttpServletResponse response ) { - List<Session> sessions = sessionService.getPublicPoolSessions(); + List<SessionInfo> sessions = sessionService.getPublicPoolSessionsInfo(); if (sessions == null || sessions.isEmpty()) { response.setStatus(HttpServletResponse.SC_NO_CONTENT); @@ -216,6 +210,14 @@ public class SessionController extends AbstractController { return sessions; } + @RequestMapping(value = "/import", method = RequestMethod.POST) + public final SessionInfo importSession( + @RequestBody final ImportExportSession session, + final HttpServletResponse response + ) { + return sessionService.importSession(session); + } + @RequestMapping(value = "/{sessionkey}/lock", method = RequestMethod.POST) public final Session lockSession( @PathVariable final String sessionkey, diff --git a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java index 3f2a8b3b703150f8bfd406575e2ff3bb950ea4b0..598d80da2adf47d92e23d8181b842bc1624d261a 100644 --- a/src/main/java/de/thm/arsnova/dao/CouchDBDao.java +++ b/src/main/java/de/thm/arsnova/dao/CouchDBDao.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import net.sf.ezmorph.Morpher; @@ -37,6 +38,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; @@ -59,6 +63,8 @@ import de.thm.arsnova.entities.Session; import de.thm.arsnova.entities.SessionInfo; import de.thm.arsnova.entities.User; import de.thm.arsnova.entities.VisitedSession; +import de.thm.arsnova.entities.transport.ImportExportSession; +import de.thm.arsnova.entities.transport.ImportExportSession.ImportExportQuestion; import de.thm.arsnova.exceptions.NotFoundException; import de.thm.arsnova.services.ISessionService; @@ -139,12 +145,18 @@ public class CouchDBDao implements IDatabaseDao { d.getJSONObject().getJSONObject("value"), Session.class ); - //session.set_id(d.getId()); + session.set_id(d.getId()); result.add(session); } return result; } + @Override + public final List<SessionInfo> getPublicPoolSessionsInfo() { + final List<Session> sessions = this.getPublicPoolSessions(); + return getInfosForSessions(sessions); + } + @Override public final List<Session> getMyPublicPoolSessions(final User user) { final NovaView view = new NovaView("session/public_pool_by_creator"); @@ -346,6 +358,7 @@ public class CouchDBDao implements IDatabaseDao { } @Override + @Cacheable("sessions") public final Session getSessionFromKeyword(final String keyword) { final NovaView view = new NovaView("session/by_keyword"); view.setKey(keyword); @@ -361,6 +374,7 @@ public class CouchDBDao implements IDatabaseDao { } @Override + @Cacheable("sessions") public final Session getSessionFromId(final String sessionId) { final NovaView view = new NovaView("session/by_id"); view.setKey(sessionId); @@ -403,6 +417,7 @@ public class CouchDBDao implements IDatabaseDao { } catch (final IOException e) { return null; } + // session caching is done by loading the created session return getSession(sessionDocument.getString("keyword")); } @@ -444,6 +459,7 @@ public class CouchDBDao implements IDatabaseDao { return database; } + @CachePut(value = "questions", key="#question") @Override public final Question saveQuestion(final Session session, final Question question) { final Document q = toQuestionDocument(session, question); @@ -501,6 +517,7 @@ public class CouchDBDao implements IDatabaseDao { return q; } + @CachePut(value = "questions") @Override public final Question updateQuestion(final Question question) { try { @@ -576,6 +593,7 @@ public class CouchDBDao implements IDatabaseDao { return null; } + @Cacheable("questions") @Override public final Question getQuestion(final String id) { try { @@ -660,19 +678,21 @@ public class CouchDBDao implements IDatabaseDao { } @Override - public final void updateSessionOwnerActivity(final Session session) { + @CachePut(value = "sessions") + public final Session updateSessionOwnerActivity(final Session session) { try { /* Do not clutter CouchDB. Only update once every 3 hours. */ if (session.getLastOwnerActivity() > System.currentTimeMillis() - 3 * 3600000) { - return; + return session; } session.setLastOwnerActivity(System.currentTimeMillis()); final JSONObject json = JSONObject.fromObject(session); getDatabase().saveDocument(new Document(json)); + return session; } catch (final IOException e) { LOGGER.error("Failed to update lastOwnerActivity for Session {}", session); - return; + return session; } } @@ -683,6 +703,7 @@ public class CouchDBDao implements IDatabaseDao { return collectQuestionIds(view); } + @CacheEvict(value = "questions") @Override public final void deleteQuestionWithAnswers(final Question question) { try { @@ -704,11 +725,14 @@ public class CouchDBDao implements IDatabaseDao { view.setEndKey(session.get_id(), "{}"); final ViewResults results = getDatabase().view(view); + List<Question> questions = new ArrayList<Question>(); for (final Document d : results.getResults()) { final Question q = new Question(); q.set_id(d.getId()); - deleteQuestionWithAnswers(q); + q.set_rev(d.getRev()); + questions.add(q); } + deleteAllAnswersWithQuestions(questions); } private void deleteDocument(final String documentId) throws IOException { @@ -721,11 +745,16 @@ public class CouchDBDao implements IDatabaseDao { try { final NovaView view = new NovaView("answer/cleanup"); view.setKey(question.get_id()); + view.setIncludeDocs(true); final ViewResults results = getDatabase().view(view); - for (final Document d : results.getResults()) { - deleteDocument(d.getId()); + List<Document> answersToDelete = new ArrayList<Document>(); + for (final Document a : results.getResults()) { + final Document d = new Document(a.getJSONObject("doc")); + d.put("_deleted", true); + answersToDelete.add(d); } + database.bulkSaveDocuments(answersToDelete.toArray(new Document[answersToDelete.size()])); } catch (final IOException e) { LOGGER.error("IOException: Could not delete answers for question {}", question.get_id()); } @@ -1254,20 +1283,7 @@ public class CouchDBDao implements IDatabaseDao { } @Override - public Session lockSession(final Session session, final Boolean lock) { - try { - final Document s = database.getDocument(session.get_id()); - s.put("active", lock); - database.saveDocument(s); - session.set_rev(s.getRev()); - return session; - } catch (final IOException e) { - LOGGER.error("Could not lock session {}", session); - } - return null; - } - - @Override + @CachePut(value = "sessions") public Session updateSession(final Session session) { try { final Document s = database.getDocument(session.get_id()); @@ -1286,6 +1302,7 @@ public class CouchDBDao implements IDatabaseDao { } @Override + @CacheEvict(value = "sessions") public void deleteSession(final Session session) { try { deleteDocument(session.get_id()); @@ -1519,6 +1536,7 @@ public class CouchDBDao implements IDatabaseDao { publishQuestions(session, publish, questions); } + @CacheEvict(value = "questions", allEntries = true) @Override public void publishQuestions(final Session session, final boolean publish, List<Question> questions) { for (final Question q : questions) { @@ -1541,25 +1559,77 @@ public class CouchDBDao implements IDatabaseDao { @Override public void deleteAllQuestionsAnswers(final Session session) { final List<Question> questions = getQuestions(new NovaView("skill_question/by_session"), session); - for (final Question q : questions) { - deleteAnswers(q); - } + deleteAllAnswersForQuestions(questions); } @Override public void deleteAllPreparationAnswers(final Session session) { final List<Question> questions = getQuestions(new NovaView("skill_question/preparation_question_by_session"), session); - for (final Question q : questions) { - deleteAnswers(q); - } + deleteAllAnswersForQuestions(questions); } @Override public void deleteAllLectureAnswers(final Session session) { final List<Question> questions = getQuestions(new NovaView("skill_question/lecture_question_by_session"), session); - for (final Question q : questions) { - deleteAnswers(q); + deleteAllAnswersForQuestions(questions); + } + + private boolean deleteAllAnswersForQuestions(List<Question> questions) { + List<String> questionIds = new ArrayList<String>(); + for (Question q : questions) { + questionIds.add(q.get_id()); + } + 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<Document>(); + for (Document a : result) { + final Document d = new Document(a.getJSONObject("doc")); + d.put("_deleted", true); + allAnswers.add(d); } + try { + getDatabase().bulkSaveDocuments(allAnswers.toArray(new Document[allAnswers.size()])); + } catch (IOException e) { + LOGGER.error("Could not bulk delete answers: {}", e.getMessage()); + return false; + } + return true; + } + + private boolean deleteAllAnswersWithQuestions(List<Question> questions) { + List<String> questionIds = new ArrayList<String>(); + final List<Document> allQuestions = new ArrayList<Document>(); + for (Question q : questions) { + final Document d = new Document(); + d.put("_id", q.get_id()); + d.put("_rev", q.get_rev()); + d.put("_deleted", true); + 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<Document>(); + for (Document a : result) { + final Document d = new Document(a.getJSONObject("doc")); + d.put("_deleted", true); + allAnswers.add(d); + } + + try { + List<Document> deleteList = new ArrayList<Document>(allAnswers); + deleteList.addAll(allQuestions); + getDatabase().bulkSaveDocuments(deleteList.toArray(new Document[deleteList.size()])); + } catch (IOException e) { + LOGGER.error("Could not bulk delete questions and answers: {}", e.getMessage()); + return false; + } + return true; } @Override @@ -1658,4 +1728,91 @@ public class CouchDBDao implements IDatabaseDao { return false; } + + @Override + public SessionInfo importSession(User user, ImportExportSession importSession) { + final Session session = this.saveSession(user, importSession.generateSessionEntity(user)); + List<Document> questions = new ArrayList<Document>(); + // We need to remember which answers belong to which question. + // The answers need a questionId, so we first store the questions to get the IDs. + // Then we update the answer objects and store them as well. + Map<Document, ImportExportQuestion> mapping = new HashMap<Document, ImportExportQuestion>(); + // Later, generate all answer documents + List<Document> answers = new ArrayList<Document>(); + // We can then push answers together with interposed questions in one large bulk request + List<Document> interposedQuestions = new ArrayList<Document>(); + try { + // add session id to all questions and generate documents + for (ImportExportQuestion question : importSession.getQuestions()) { + Document doc = toQuestionDocument(session, question); + question.setSessionId(session.get_id()); + questions.add(doc); + mapping.put(doc, question); + } + database.bulkSaveDocuments(questions.toArray(new Document[questions.size()])); + + // bulk import answers together with interposed questions + for (Entry<Document, ImportExportQuestion> entry : mapping.entrySet()) { + final Document doc = entry.getKey(); + final ImportExportQuestion question = entry.getValue(); + question.set_id(doc.getId()); + question.set_rev(doc.getRev()); + for (de.thm.arsnova.entities.transport.Answer answer : question.getAnswers()) { + final Answer a = answer.generateAnswerEntity(user, question); + final Document answerDoc = new Document(); + answerDoc.put("type", "skill_question_answer"); + answerDoc.put("sessionId", a.getSessionId()); + answerDoc.put("questionId", a.getQuestionId()); + answerDoc.put("answerSubject", a.getAnswerSubject()); + answerDoc.put("questionVariant", a.getQuestionVariant()); + answerDoc.put("questionValue", a.getQuestionValue()); + answerDoc.put("answerText", a.getAnswerText()); + answerDoc.put("timestamp", a.getTimestamp()); + answerDoc.put("piRound", a.getPiRound()); + answerDoc.put("abstention", a.isAbstention()); + answers.add(answerDoc); + } + } + for (de.thm.arsnova.entities.transport.InterposedQuestion i : importSession.getFeedbackQuestions()) { + final Document q = new Document(); + q.put("type", "interposed_question"); + q.put("sessionId", session.get_id()); + q.put("subject", i.getSubject()); + q.put("text", i.getText()); + q.put("timestamp", i.getTimestamp()); + q.put("read", i.isRead()); + // we do not store the creator's name + q.put("creator", ""); + interposedQuestions.add(q); + } + List<Document> documents = new ArrayList<Document>(answers); + documents.addAll(interposedQuestions); + database.bulkSaveDocuments(documents.toArray(new Document[documents.size()])); + } catch (IOException e) { + LOGGER.error("Could not import this session: {}", e.getMessage()); + // Something went wrong, delete this session since we do not want a partial import + this.deleteSession(session); + return null; + } + // Calculate some statistics... + int unreadInterposed = 0; + for (de.thm.arsnova.entities.transport.InterposedQuestion i : importSession.getFeedbackQuestions()) { + if (!i.isRead()) { + unreadInterposed++; + } + } + int numUnanswered = 0; + for (ImportExportQuestion question : importSession.getQuestions()) { + if (question.getAnswers().size() == 0) { + numUnanswered++; + } + } + final SessionInfo info = new SessionInfo(session); + info.setNumQuestions(questions.size()); + info.setNumUnanswered(numUnanswered); + info.setNumAnswers(answers.size()); + info.setNumInterposed(interposedQuestions.size()); + info.setNumUnredInterposed(unreadInterposed); + return info; + } } diff --git a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java index 3807436a32ae3f5fa004215da177a355e810f346..d688abbebe96b06212afe87a79157ccd996444ce 100644 --- a/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java +++ b/src/main/java/de/thm/arsnova/dao/IDatabaseDao.java @@ -18,6 +18,7 @@ package de.thm.arsnova.dao; import java.util.List; + import de.thm.arsnova.connector.model.Course; import de.thm.arsnova.domain.CourseScore; import de.thm.arsnova.entities.Answer; @@ -29,6 +30,7 @@ import de.thm.arsnova.entities.Question; 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; public interface IDatabaseDao { Session getSessionFromKeyword(String keyword); @@ -57,7 +59,7 @@ public interface IDatabaseDao { LoggedIn registerAsOnlineUser(User u, Session s); - void updateSessionOwnerActivity(Session session); + Session updateSessionOwnerActivity(Session session); List<String> getQuestionIds(Session session, User user); @@ -123,8 +125,6 @@ public interface IDatabaseDao { List<Session> getCourseSessions(List<Course> courses); - Session lockSession(Session session, Boolean lock); - Session updateSession(Session session); void deleteSession(Session session); @@ -175,6 +175,8 @@ public interface IDatabaseDao { List<SessionInfo> getMySessionsInfo(User user); + List<SessionInfo> getPublicPoolSessionsInfo(); + List<SessionInfo> getMyPublicPoolSessionsInfo(final User user); List<SessionInfo> getMyVisitedSessionsInfo(User currentUser); @@ -182,4 +184,6 @@ public interface IDatabaseDao { void deleteAllPreparationAnswers(Session session); void deleteAllLectureAnswers(Session session); + + SessionInfo importSession(User user, ImportExportSession importSession); } diff --git a/src/main/java/de/thm/arsnova/dao/NovaView.java b/src/main/java/de/thm/arsnova/dao/NovaView.java index 9c46b1b4f0a0292bdbe2131692054ac9b404997e..1248b17196bcf46374102d0b35ea1269ad580ba1 100644 --- a/src/main/java/de/thm/arsnova/dao/NovaView.java +++ b/src/main/java/de/thm/arsnova/dao/NovaView.java @@ -36,6 +36,16 @@ public class NovaView extends View { protected StaleMode stale = StaleMode.NONE; + protected boolean includeDocs = false; + + public boolean isIncludeDocs() { + return includeDocs; + } + + public void setIncludeDocs(boolean includeDocs) { + this.includeDocs = includeDocs; + } + public NovaView(final String fullname) { super(fullname); } @@ -122,6 +132,12 @@ public class NovaView extends View { query.append("stale=update_after"); } } + if (includeDocs != false) { + if (query.length() > 0) { + query.append("&"); + } + query.append("include_docs=true"); + } if (query.length() == 0) { return null; diff --git a/src/main/java/de/thm/arsnova/entities/Question.java b/src/main/java/de/thm/arsnova/entities/Question.java index f03cdadbcb5b2e926a4383dcc027dd996745dbc1..7ab74351f9d849909c99d9776ba2f41a265070c3 100644 --- a/src/main/java/de/thm/arsnova/entities/Question.java +++ b/src/main/java/de/thm/arsnova/entities/Question.java @@ -249,7 +249,7 @@ public class Question { public void setImage(final String image) { this.image = image; } - + public String getFcImage() { return fcImage; } @@ -373,39 +373,39 @@ public class Question { public String getGridLineColor() { return gridLineColor; } - + public void setGridLineColor(String gridLineColor) { this.gridLineColor = gridLineColor; } - + public int getNumberOfDots() { return numberOfDots; } - + public void setNumberOfDots(int numberOfDots) { this.numberOfDots = numberOfDots; } - + public String getGridType() { return gridType; } - + public void setGridType(String gridType) { this.gridType = gridType; } - + public void setScaleFactor(String scaleFactor) { this.scaleFactor = scaleFactor; } - + public String getScaleFactor() { return this.scaleFactor; } - + public void setGridScaleFactor(String scaleFactor) { this.gridScaleFactor = scaleFactor; } - + public String getGridScaleFactor() { return this.gridScaleFactor; } @@ -414,4 +414,51 @@ public class Question { public final String toString() { return "Question type '" + type + "': " + subject + ";\n" + text + possibleAnswers; } + + public int calculateValue(Answer answer) { + if (answer.isAbstention()) { + return 0; + } else if (this.questionType.equals("mc")) { + return calculateMultipleChoiceValue(answer); + } else if (this.questionType.equals("grid")) { + return calculateGridValue(answer); + } else { + return calculateRegularValue(answer); + } + } + + private int calculateRegularValue(Answer answer) { + String answerText = answer.getAnswerText(); + for (PossibleAnswer p : this.possibleAnswers) { + if (answerText.equals(p.getText())) { + return p.getValue(); + } + } + return 0; + } + + private int calculateGridValue(Answer answer) { + int value = 0; + String[] answers = answer.getAnswerText().split(","); + for (int i = 0; i < answers.length; i++) { + for (PossibleAnswer p : this.possibleAnswers) { + if (answers[i].equals(p.getText())) { + value += p.getValue(); + } + } + } + return value; + } + + private int calculateMultipleChoiceValue(Answer answer) { + int value = 0; + String[] answers = answer.getAnswerText().split(","); + for (int i = 0; i < this.possibleAnswers.size() && i < answers.length; i++) { + if (answers[i].equals("1")) { + PossibleAnswer p = this.possibleAnswers.get(i); + value += p.getValue(); + } + } + return value; + } } diff --git a/src/main/java/de/thm/arsnova/entities/Session.java b/src/main/java/de/thm/arsnova/entities/Session.java index 85b809776a2664db6eca175bb0411ea0009ce6aa..78e7dbd386229bac08d0d68fb7d38f95109b1e19 100644 --- a/src/main/java/de/thm/arsnova/entities/Session.java +++ b/src/main/java/de/thm/arsnova/entities/Session.java @@ -37,7 +37,7 @@ public class Session implements Serializable { private String courseId; private List<String> _conflicts; private long creationTime; - + private String ppAuthorName; private String ppAuthorMail; private String ppUniversity; @@ -52,6 +52,27 @@ public class Session implements Serializable { private String _id; private String _rev; + /** + * Returns a copy of the given session without any information that identifies a person. + * @param original The session to create a anonymized copy of + * @return + */ + public static Session anonymizedCopy(final Session original) { + final Session copy = new Session(); + copy.type = original.type; + copy.name = original.name; + copy.shortName = original.shortName; + copy.keyword = original.keyword; + copy.creator = ""; // anonymous + copy.active = original.active; + copy.lastOwnerActivity = original.lastOwnerActivity; + copy.courseType = original.courseType; + copy.courseId = original.courseId; + copy._id = original._id; + copy._rev = original._rev; + return copy; + } + public String getType() { return type; } @@ -156,7 +177,7 @@ public class Session implements Serializable { public boolean isCourseSession() { return getCourseId() != null && !getCourseId().isEmpty(); } - + public long getCreationTime() { return creationTime; } @@ -164,7 +185,7 @@ public class Session implements Serializable { public void setCreationTime(long creationTime) { this.creationTime = creationTime; } - + public String getPpAuthorName() { return ppAuthorName; } @@ -172,7 +193,7 @@ public class Session implements Serializable { public void setPpAuthorName(final String ppAuthorName) { this.ppAuthorName = ppAuthorName; } - + public String getPpAuthorMail() { return ppAuthorMail; } @@ -180,7 +201,7 @@ public class Session implements Serializable { public void setPpAuthorMail(final String ppAuthorMail) { this.ppAuthorMail = ppAuthorMail; } - + public String getPpUniversity() { return ppUniversity; } @@ -188,7 +209,7 @@ public class Session implements Serializable { public void setPpUniversity(final String ppUniversity) { this.ppUniversity = ppUniversity; } - + public String getPpLogo() { return ppLogo; } @@ -196,7 +217,7 @@ public class Session implements Serializable { public void setPpLogo(final String ppLogo) { this.ppLogo = ppLogo; } - + public String getPpSubject() { return ppSubject; } @@ -204,7 +225,7 @@ public class Session implements Serializable { public void setPpSubject(final String ppSubject) { this.ppSubject = ppSubject; } - + public String getPpLicense() { return ppLicense; } @@ -212,42 +233,42 @@ public class Session implements Serializable { public void setPpLicense(final String ppLicense) { this.ppLicense = ppLicense; } - + public String getPpDescription() { return ppDescription; } - + public void setPpDescription(final String ppDescription) { this.ppDescription = ppDescription; } - + public String getPpFaculty() { return ppFaculty; } - + public void setPpFaculty(final String ppFaculty) { this.ppFaculty = ppFaculty; } - + public String getPpLevel() { return ppLevel; } - + public void setPpLevel(final String ppLevel) { this.ppLevel = ppLevel; } - + public String getSessionType() { return sessionType; } - + public void setSessionType(final String sessionType) { this.sessionType = sessionType; } @Override public String toString() { - return "Session [keyword=" + keyword+ ", type=" + type + ", creator=" + creator + "]"; + return "Session [keyword=" + keyword + ", type=" + type + ", creator=" + creator + "]"; } @Override diff --git a/src/main/java/de/thm/arsnova/entities/SessionInfo.java b/src/main/java/de/thm/arsnova/entities/SessionInfo.java index 671211fabb068832bb82f3a961beb59b5a411a09..71e925eff3116cfa15af990da49b7039828ddf3b 100644 --- a/src/main/java/de/thm/arsnova/entities/SessionInfo.java +++ b/src/main/java/de/thm/arsnova/entities/SessionInfo.java @@ -28,6 +28,9 @@ public class SessionInfo { private boolean active; private String courseType; private long creationTime; + private String sessionType; + private String ppLevel; + private String ppSubject; private int numQuestions; private int numAnswers; @@ -42,8 +45,13 @@ public class SessionInfo { this.active = session.isActive(); this.courseType = session.getCourseType(); this.creationTime = session.getCreationTime(); + this.sessionType = session.getSessionType(); + this.ppLevel = session.getPpLevel(); + this.ppSubject = session.getPpSubject(); } + public SessionInfo() {} + public static List<SessionInfo> fromSessionList(List<Session> sessions) { List<SessionInfo> infos = new ArrayList<SessionInfo>(); for (Session s : sessions) { @@ -92,6 +100,30 @@ public class SessionInfo { this.courseType = courseType; } + public String getSessionType() { + return sessionType; + } + + public void setSessionType(String sessionType) { + this.sessionType = sessionType; + } + + public String getPpLevel() { + return ppLevel; + } + + public void setPpLevel(String ppLevel) { + this.ppLevel = ppLevel; + } + + public String getPpSubject() { + return ppSubject; + } + + public void setPpSubject(String ppSubject) { + this.ppSubject = ppSubject; + } + public int getNumQuestions() { return numQuestions; } @@ -123,7 +155,7 @@ public class SessionInfo { public void setNumUnanswered(int numUnanswered) { this.numUnanswered = numUnanswered; } - + public long getCreationTime() { return creationTime; } diff --git a/src/main/java/de/thm/arsnova/entities/VisitedSession.java b/src/main/java/de/thm/arsnova/entities/VisitedSession.java index a358e078767f6f00de19c0cea97cb0520e5d4635..e5af42dfabae4b56d96b5443148c7d7087f0b912 100644 --- a/src/main/java/de/thm/arsnova/entities/VisitedSession.java +++ b/src/main/java/de/thm/arsnova/entities/VisitedSession.java @@ -61,4 +61,4 @@ public class VisitedSession { return "VisitedSession [_id=" + _id + ", name=" + name + ", keyword=" + keyword + "]"; } -} \ No newline at end of file +} diff --git a/src/main/java/de/thm/arsnova/entities/transport/Answer.java b/src/main/java/de/thm/arsnova/entities/transport/Answer.java new file mode 100644 index 0000000000000000000000000000000000000000..b12dde79236ae21993546d4d7d9fc41e7302bf79 --- /dev/null +++ b/src/main/java/de/thm/arsnova/entities/transport/Answer.java @@ -0,0 +1,83 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 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.entities.transport; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import de.thm.arsnova.entities.Question; +import de.thm.arsnova.entities.User; + +@JsonInclude(JsonInclude.Include.NON_DEFAULT) +public class Answer { + + private String answerSubject; + + private String answerText; + + private boolean abstention; + + public String getAnswerText() { + return answerText; + } + + public void setAnswerText(String answerText) { + this.answerText = answerText; + } + + public String getAnswerSubject() { + return answerSubject; + } + + public void setAnswerSubject(String answerSubject) { + this.answerSubject = answerSubject; + } + + public boolean isAbstention() { + return abstention; + } + + public void setAbstention(boolean abstention) { + this.abstention = abstention; + } + + public de.thm.arsnova.entities.Answer generateAnswerEntity(final User user, final Question question) { + // rewrite all fields so that no manipulated data gets written + // only answerText, answerSubject, and abstention are allowed + de.thm.arsnova.entities.Answer theAnswer = new de.thm.arsnova.entities.Answer(); + theAnswer.setAnswerSubject(this.getAnswerSubject()); + theAnswer.setAnswerText(this.getAnswerText()); + theAnswer.setSessionId(question.getSessionId()); + theAnswer.setUser(user.getUsername()); + theAnswer.setQuestionId(question.get_id()); + theAnswer.setTimestamp(new Date().getTime()); + theAnswer.setQuestionVariant(question.getQuestionVariant()); + theAnswer.setAbstention(this.isAbstention()); + // calculate learning progress value after all properties are set + theAnswer.setQuestionValue(question.calculateValue(theAnswer)); + + if ("freetext".equals(question.getQuestionType())) { + theAnswer.setPiRound(0); + } else { + theAnswer.setPiRound(question.getPiRound()); + } + + return theAnswer; + } +} diff --git a/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java b/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java new file mode 100644 index 0000000000000000000000000000000000000000..8021e93ac46b2f1c538e849414a40c91bf7aab2a --- /dev/null +++ b/src/main/java/de/thm/arsnova/entities/transport/ImportExportSession.java @@ -0,0 +1,117 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 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.entities.transport; + +import java.util.Date; +import java.util.List; + +import de.thm.arsnova.entities.Question; +import de.thm.arsnova.entities.Session; +import de.thm.arsnova.entities.User; + +public class ImportExportSession { + + private ImportExportSesssion session; + + private List<ImportExportQuestion> questions; + + private List<InterposedQuestion> feedbackQuestions; + + public ImportExportSesssion getSession() { + return session; + } + + public void setSession(ImportExportSesssion session) { + this.session = session; + } + + public List<ImportExportQuestion> getQuestions() { + return questions; + } + + public void setQuestions(List<ImportExportQuestion> questions) { + this.questions = questions; + } + + public List<InterposedQuestion> getFeedbackQuestions() { + return feedbackQuestions; + } + + public void setFeedbackQuestions(List<InterposedQuestion> feedbackQuestions) { + this.feedbackQuestions = feedbackQuestions; + } + + public Session generateSessionEntity(User user) { + final Session s = new Session(); + // import fields + s.setActive(session.isActive()); + s.setName(session.getName()); + s.setShortName(session.getShortName()); + // other fields + s.setType("session"); + s.setCreator(user.getUsername()); + s.setCreationTime(new Date().getTime()); + return s; + } + + public static class ImportExportQuestion extends Question { + + private List<Answer> answers; + + public List<Answer> getAnswers() { + return answers; + } + + public void setAnswers(List<Answer> answers) { + this.answers = answers; + } + } + + public static class ImportExportSesssion { + + private String name; + + private String shortName; + + private boolean active; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getShortName() { + return shortName; + } + + public void setShortName(String shortName) { + this.shortName = shortName; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + } +} diff --git a/src/main/java/de/thm/arsnova/entities/transport/package-info.java b/src/main/java/de/thm/arsnova/entities/transport/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..accb5a5a2d5ddff85d97fd393087bcc996dc35b9 --- /dev/null +++ b/src/main/java/de/thm/arsnova/entities/transport/package-info.java @@ -0,0 +1 @@ +package de.thm.arsnova.entities.transport; diff --git a/src/main/java/de/thm/arsnova/exceptions/RequestEntityTooLargeException.java b/src/main/java/de/thm/arsnova/exceptions/RequestEntityTooLargeException.java new file mode 100644 index 0000000000000000000000000000000000000000..a8a7f30008255d9e392122a41504f14b88a51614 --- /dev/null +++ b/src/main/java/de/thm/arsnova/exceptions/RequestEntityTooLargeException.java @@ -0,0 +1,5 @@ +package de.thm.arsnova.exceptions; + +public class RequestEntityTooLargeException extends RuntimeException { + private static final long serialVersionUID = 1L; +} diff --git a/src/main/java/de/thm/arsnova/services/IQuestionService.java b/src/main/java/de/thm/arsnova/services/IQuestionService.java index a79438cc06fb9f1e63fc01acec509a2aa914a559..457b9fc7b2d9e9af938e5ee3011bb4b583181e2e 100644 --- a/src/main/java/de/thm/arsnova/services/IQuestionService.java +++ b/src/main/java/de/thm/arsnova/services/IQuestionService.java @@ -17,8 +17,8 @@ */ package de.thm.arsnova.services; -import java.util.List; import java.util.AbstractMap.SimpleEntry; +import java.util.List; import de.thm.arsnova.entities.Answer; import de.thm.arsnova.entities.InterposedQuestion; @@ -59,7 +59,7 @@ public interface IQuestionService { int getInterposedCount(String sessionKey); - InterposedReadingCount getInterposedReadingCount(String sessionKey); + InterposedReadingCount getInterposedReadingCount(String sessionKey, String username); List<InterposedQuestion> getInterposedQuestions(String sessionKey); @@ -71,7 +71,7 @@ public interface IQuestionService { void deleteAnswers(String questionId); - Answer saveAnswer(Answer answer); + Answer saveAnswer(String questionId, de.thm.arsnova.entities.transport.Answer answer); Answer updateAnswer(Answer answer); @@ -90,7 +90,7 @@ public interface IQuestionService { int getFlashcardCount(String sessionkey); int getPreparationQuestionCount(String sessionkey); - + SimpleEntry<String, List<Integer>> getAnswerAndAbstentionCountByQuestion(String questionid); int countLectureQuestionAnswers(String sessionkey); @@ -118,7 +118,7 @@ public interface IQuestionService { void deleteAllInterposedQuestions(String sessionKeyword); void publishAll(String sessionkey, boolean publish); - + void publishQuestions(String sessionkey, boolean publish, List<Question> questions); void deleteAllQuestionsAnswers(String sessionkey); diff --git a/src/main/java/de/thm/arsnova/services/ISessionService.java b/src/main/java/de/thm/arsnova/services/ISessionService.java index 8d223c64c192af75cb1f8b1a8d31275d56a34ddc..aa1250b9b6ad9f6c15fcd5c2c4c414b133b1106d 100644 --- a/src/main/java/de/thm/arsnova/services/ISessionService.java +++ b/src/main/java/de/thm/arsnova/services/ISessionService.java @@ -24,6 +24,7 @@ import java.util.UUID; import de.thm.arsnova.connector.model.Course; import de.thm.arsnova.entities.Session; import de.thm.arsnova.entities.SessionInfo; +import de.thm.arsnova.entities.transport.ImportExportSession; public interface ISessionService { Session getSession(String keyword); @@ -58,7 +59,11 @@ public interface ISessionService { List<SessionInfo> getMySessionsInfo(); + List<SessionInfo> getPublicPoolSessionsInfo(); + List<SessionInfo> getMyPublicPoolSessionsInfo(); List<SessionInfo> getMyVisitedSessionsInfo(); + + SessionInfo importSession(ImportExportSession session); } diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/QuestionService.java index eaafd5e42f12b4bd137e692c418e1c755afa5a45..93f310dde9e351e4c94c55034701108929c0f79a 100644 --- a/src/main/java/de/thm/arsnova/services/QuestionService.java +++ b/src/main/java/de/thm/arsnova/services/QuestionService.java @@ -18,13 +18,14 @@ package de.thm.arsnova.services; import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.AbstractMap.SimpleEntry; +import de.thm.arsnova.exceptions.ForbiddenException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -111,7 +112,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis } // base64 adds offset to filesize, formula taken from: http://en.wikipedia.org/wiki/Base64#MIME - final int fileSize = (int) ((question.getImage().length()-814)/1.37); + final int fileSize = (int) ((question.getImage().length() - 814) / 1.37); if (fileSize > uploadFileSizeByte) { LOGGER.error("Could not save file. File is too large with " + fileSize + " Byte."); throw new BadRequestException(); @@ -270,10 +271,10 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis if (question == null) { return 0; } - + return databaseDao.getAnswerCount(question, question.getPiRound()); } - + @Override @PreAuthorize("isAuthenticated()") public int getAbstentionAnswerCount(final String questionId) { @@ -281,7 +282,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis if (question == null) { return 0; } - + return databaseDao.getAbstentionAnswerCount(questionId); } @@ -339,16 +340,20 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis @Override @PreAuthorize("isAuthenticated()") - public InterposedReadingCount getInterposedReadingCount(final String sessionKey) { + public InterposedReadingCount getInterposedReadingCount(final String sessionKey, String username) { final Session session = databaseDao.getSessionFromKeyword(sessionKey); - final User user = getCurrentUser(); if (session == null) { throw new NotFoundException(); } - if (session.isCreator(user)) { + if (username == null) { return databaseDao.getInterposedReadingCount(session); } else { - return databaseDao.getInterposedReadingCount(session, user); + User currentUser = userService.getCurrentUser(); + if (!currentUser.getUsername().equals(username)) { + throw new ForbiddenException(); + } + + return databaseDao.getInterposedReadingCount(session, currentUser); } } @@ -410,33 +415,29 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis } else if (question.getPiRound() < 1 || question.getPiRound() > 2) { question.setPiRound(oldQuestion.getPiRound() > 0 ? oldQuestion.getPiRound() : 1); } - + final Question result = databaseDao.updateQuestion(question); - if(!oldQuestion.isActive() && question.isActive()) { + if (!oldQuestion.isActive() && question.isActive()) { final NewQuestionEvent event = new NewQuestionEvent(this, result, session); this.publisher.publishEvent(event); } - + return result; } @Override @PreAuthorize("isAuthenticated()") - public Answer saveAnswer(final Answer answer) { + public Answer saveAnswer(final String questionId, final de.thm.arsnova.entities.transport.Answer answer) { final User user = getCurrentUser(); - final Question question = getQuestion(answer.getQuestionId()); + final Question question = getQuestion(questionId); if (question == null) { throw new NotFoundException(); } - if ("freetext".equals(question.getQuestionType())) { - answer.setPiRound(0); - } else { - answer.setPiRound(question.getPiRound()); - } + Answer theAnswer = answer.generateAnswerEntity(user, question); - final Answer result = databaseDao.saveAnswer(answer, user); + final Answer result = databaseDao.saveAnswer(theAnswer, user); final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword()); this.publisher.publishEvent(new NewAnswerEvent(this, result, user, question, session)); @@ -520,15 +521,15 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis public int getPreparationQuestionCount(final String sessionkey) { return databaseDao.getPreparationQuestionCount(getSession(sessionkey)); } - + @Override @PreAuthorize("isAuthenticated()") - public SimpleEntry<String,List<Integer>> getAnswerAndAbstentionCountByQuestion(final String questionid) { + public SimpleEntry<String, List<Integer>> getAnswerAndAbstentionCountByQuestion(final String questionid) { final List<Integer> countList = Arrays.asList( getAnswerCount(questionid), getAbstentionAnswerCount(questionid) ); - + return new AbstractMap.SimpleEntry<String, List<Integer>>(questionid, countList); } @@ -619,7 +620,7 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis } databaseDao.publishAllQuestions(session, publish); } - + @Override @PreAuthorize("isAuthenticated()") public void publishQuestions(final String sessionkey, final boolean publish, List<Question> questions) { diff --git a/src/main/java/de/thm/arsnova/services/SessionService.java b/src/main/java/de/thm/arsnova/services/SessionService.java index 1f29a67cb4987dbf705cea45ae107aa0892e0f7e..533433e0134c5813315a563b5b085809120ce0cf 100644 --- a/src/main/java/de/thm/arsnova/services/SessionService.java +++ b/src/main/java/de/thm/arsnova/services/SessionService.java @@ -40,9 +40,11 @@ import de.thm.arsnova.entities.Question; 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 de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.NotFoundException; import de.thm.arsnova.exceptions.BadRequestException; +import de.thm.arsnova.exceptions.RequestEntityTooLargeException; import de.thm.arsnova.socket.ARSnovaSocketIOServer; @Service @@ -112,7 +114,7 @@ public class SessionService implements ISessionService { public final Session joinSession(final String keyword, final UUID socketId) { /* Socket.IO solution */ - Session session = databaseDao.getSessionFromKeyword(keyword); + Session session = null != keyword ? databaseDao.getSessionFromKeyword(keyword) : null; if (null == session) { userService.removeUserFromSessionBySocketId(socketId); @@ -169,8 +171,8 @@ public class SessionService implements ISessionService { @Override @PreAuthorize("isAuthenticated()") - public final List<Session> getPublicPoolSessions() { - return databaseDao.getPublicPoolSessions(); + public final List<SessionInfo> getPublicPoolSessionsInfo() { + return databaseDao.getPublicPoolSessionsInfo(); } @Override @@ -217,10 +219,10 @@ public class SessionService implements ISessionService { session.setPpLogo(base64ImageString); } // base64 adds offset to filesize, formula taken from: http://en.wikipedia.org/wiki/Base64#MIME - final int fileSize = (int) ((session.getPpLogo().length()-814)/1.37); + final int fileSize = (int) ((session.getPpLogo().length() - 814) / 1.37); if (fileSize > uploadFileSizeByte) { LOGGER.error("Could not save file. File is too large with " + fileSize + " Byte."); - throw new BadRequestException(); + throw new RequestEntityTooLargeException(); } } @@ -266,8 +268,9 @@ public class SessionService implements ISessionService { if (!session.isCreator(user)) { throw new ForbiddenException(); } + session.setActive(lock); socketIoServer.reportSessionStatus(sessionkey, lock); - return databaseDao.lockSession(session, lock); + return databaseDao.updateSession(session); } @Override @@ -302,4 +305,15 @@ public class SessionService implements ISessionService { LearningProgress learningProgress = learningProgressFactory.createFromType(progressType); return learningProgress.getMyProgress(session, user); } + + @Override + @PreAuthorize("isAuthenticated()") + public SessionInfo importSession(ImportExportSession importSession) { + final User user = userService.getCurrentUser(); + final SessionInfo info = databaseDao.importSession(user, importSession); + if (info == null) { + throw new RuntimeException("Error while importing the session."); + } + return info; + } } diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java index c925e80ddd34407ed244b67aaea67d0d5fda169f..2cb748b7c183b3ae23f68633ca7991005dd09d64 100644 --- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java +++ b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java @@ -157,9 +157,6 @@ public class ARSnovaSocketIOServer implements ApplicationListener<NovaEvent>, No if (session.getKeyword() == oldSessionKey) { return; } - if (null != oldSessionKey) { - reportActiveUserCountForSession(oldSessionKey); - } if (null != sessionService.joinSession(session.getKeyword(), client.getSessionId())) { /* active user count has to be sent to the client since the broadcast is @@ -167,6 +164,9 @@ public class ARSnovaSocketIOServer implements ApplicationListener<NovaEvent>, No reportActiveUserCountForSession(session.getKeyword()); reportSessionDataToClient(session.getKeyword(), u, client); } + if (null != oldSessionKey) { + reportActiveUserCountForSession(oldSessionKey); + } } }); diff --git a/src/main/resources/arsnova.properties.example b/src/main/resources/arsnova.properties.example index 40f9bc76f48102c978e765a28add9ed1617c948b..628b9fbcf36eacb0377a6c73b909f9e75fc99fbb 100644 --- a/src/main/resources/arsnova.properties.example +++ b/src/main/resources/arsnova.properties.example @@ -179,6 +179,7 @@ features.question-format.grid-square.enabled=false # Without enabled session-import-export feature no sessions can be added to the # public pool +# features.session-import-export.enabled=false features.public-pool.enabled=false @@ -201,8 +202,11 @@ question.answer-option-limit=8 # effect if neither MathJax nor Markdown are enabled. question.parse-answer-option-formatting=false -# optional: demo session keyword to show above session login button -optional.demoSessionKey= +# optional: demo session id to show above session login button +session.demo-id= + +# label underneath ARSnova logo +optional.arsnova-slogan=Audience Response System # Links which are displayed in the frontend applications # @@ -214,7 +218,8 @@ links.organization.url= links.imprint.url= links.privacy-policy.url= -# configuration for the public pool +# Configuration for the public pool +# pp.subjects = Afrikanistik,Agrarbiologie,Agrarmarketing und Agrarmanagement,Agrarökologie,Agrartechnik,Agrarwissenschaften,Ägyptologie,Akkordeon,Alphabetisierung,Altbauinstandsetzung,Alte Geschichte,Altenpflege und Management,Ältere deutsche Literatur und Sprache,Altorientalistik,Ambient Assisted Living,Amerikanistik,Analytische Chemie,Angewandte Ethik,Angewandte Freizeitwissenschaft,Angewandte Informatik,Angewandte Literatur- und Kulturwissenschaften,Angewandte Mathematik,Angewandte Naturwissenschaften,Angewandte Pharmazie,Angewandte Politikwissenschaft,Angewandte Psychologie,Angewandte Sozial- und Bildungswissenschaften,Angewandte Sprachwissenschaften,Angewandte Systemwissenschaft,Anglistik / Englisch,Anthropologie,Applied Polymer Science,Applied Research,Applied System Dynamics,Arabistik,Arbeits- und Organisationspsychologie,Arbeitslehre / Arbeitswissenschaft,Arbeitslehre / Technik,Arbeitsmarktmanagement,Archäologische Restaurierung,Archäometrie,Architectural Lighting Design,Architektur,Archivwesen,Asienwissenschaften,Assyrologie,Astronomie,Astrophysik,Äthiopistik,Audioproduktion,Audiovisuelle Medien,Augenoptik,Ausstellungsdesign,Austronesien, Sprache/Kulturen,Auswärtige Angelegenheiten,Automatisierungstechnik,Automobilwirtschaft,Automotive System Engineering,Bahnbetrieb und Infrastruktur,Balkanphilologie,Baltic Management Studies,Baltische Philologie,Bank,Banking and Finance,Barrierefreie Systeme,Baubetrieb,Bauingenieurwesen,Baumanagement,Bauphysik,Baustoffingenieurwesen,Bautechnik,Beratung im Gesundheits-, Sozial- und Bildungswesen,Beratung und Sozialrecht,Beratungswissenschaft,Berufspädagogik,Betriebswirtschaft und Kultur-, Freizeit- und Sportmanagement,Betriebswirtschaft und Logistik,Betriebswirtschaft und Recht,Betriebswirtschaftslehre / BWL,Bewegung und Sport im Alter,Bibliothekswesen,Bildhauerei/Plastik,Bildung im Alter,Bildung und Erziehung im Kindesalter,Bildung und Medien,Bildungsmanagement,Bildungsplanung,Bilingualer Unterricht,Bio- and Pharmaceutical Analysis,Biochemie,Biografisches und Kreatives Schreiben,Bioinformatik,Biologie,Biologiedidaktik,Biomathematik,Biomedizinische Technik,Bionik,Biophysik,Biosystemtechnik,Biotechnologie,Biotechnologie und Medizintechnik,Bioverfahrenstechnik,Biowissenschaften,Blasinstrumente,Bodenwissenschaften,Botanik,Brand Design,Brand Management,Brauwesen und Getränketechnologie,British and American Studies,Buchkunst,Buchwissenschaften,Bühnenbild,Bühnentanz,Bundeswehrverwaltung,Business and Communication,Business and Organisation,Business Consulting,Business Ethics und CSR-Management,Business Integration,Business Travel Management,Cerebrovascular Medicine,Chemie,Chemie- und Bioingenieurwesen,Chemiedidaktik,Chemieingenieurwesen/Verfahrenstechnik,China Management,Chinesisch,Choreographie,Christliche Sozialethik und Gesellschaftspolitik,Claviorganum,Clinical Research Management,Coaching und Systementwicklung,Communication Engineering,Comparative and European Law,Compliance and Corporate Governance,Computational Mathematics,Computational Physics,Computational Science,Computer Aided Engineering,Computerlinguistik,Computervisualistik,Computing in the Humanities,Consulting und Controlling,Consumer Science,Controlling,Controlling and Risk Management,Customer Relationship Management,Dänisch,Data and Knowledge Engineering,Deaf Studies (Sprache und Kultur der Gehörlosengemeinschaft),Demographie,Denkmalpflege,Dentaltechnologie,Design,Deutsch - Französische Studien,Deutsch als Fremdsprache,Deutsch-Französisches Recht,Deutsch-Italienische Studien,Deutsches Recht,Diabetes und Management,Diakoniewissenschaft,Didaktik der Mathematik,Didaktik der Mittelschule,Dienstleistungsmanagement,Digitale Forensik,Digitale Medienproduktion,Dirigieren, Chor- und Orchesterleitung,Doing culture. Bildung und Reflexion kultureller Prozesse,Dolmetschen,Dramaturgie,Druck- und Medientechnik,E-Bass,E-Business und Social Media,Economics and Finance,Economics, Finance and Philosophy,Editions- und Dokumentwissenschaften,Education - Regelschule,Educational Media,Eigentums- und Wettbewerbsrecht,Elektro- und Informationstechnik,Elektroakustische Musik,Elektromobilität,Elementare Musikpädagogik,Elementarmathematik,Embedded Systems Engineering,Emergency (Education/Management) Practitioner,Energie und Rohstoffe,Energie- und Umweltmanagement,Energie- und Umweltschutztechnik,Energieprozesstechnik,Energietechnik,Energiewirtschaft,Engineering Physics,Entrepreneurship,Entsorgungsingenieurwesen,Epidemiology,Ergotherapie,Ergotherapie, Logopädie, Physiotherapie,Erlebnispädagogik,Erneuerbare Energien,Erwachsenenbildung,Ethik,Ethnologie,Europäische Betriebswirtschaft,Europäische Ethnologie,Europäische Geschichte,Europäische Literatur,Europäische Rechtslinguistik,Europäische Verwaltung und Politik,Europäisches Management und Controlling,Europalehramt,European Business Management,European Design,European Regulation of Network Industries,European Studies,Eurythmie,Evaluation,Evangelikale Theologie,Eventmarketing,Fachdidaktik,Fachübersetzen,Facility Management,Fahrzeug Interieur Design,Fahrzeuginformatik,Fahrzeugtechnik / Fahrzeugbau,Familienpsychologie,Farbtechnik und Raumgestaltung,Fennistik / Finnougristik,Fernsehjournalismus,Fertigungstechnik,Figurentheater,Film und Fernsehen,Filmmusik,Filmwissenschaft,Financial Information Systems,Finanz- und Rechnungswesen,Finanzmanagement,Fischereiwirtschaft,Flöte,Forstwirtschaft,Fotografie/Fotografik,FrankoMedia,Französisch,Französische Kulturwissenschaften,Friedens- und Konfliktforschung,Friesische Philologie,Frühe Hilfen,Funkidentifikation/Nahbereichsfunktechnik (RFID),Furniture and Interior Design,Gamedesign,Gartenbauwissenschaft,Gebärdensprache,Gebäude- und Energietechnik,Geisteswissenschaftliche Grundlagen,Gemeinschaftskunde,Gemeinwesenarbeit,Gender Studies,General Management,Geoarchäologie,Geographie,Geoinformatik,Geoinformationsmanagement,Geoingenieurwissenschaften,Geologie,Geoökologie,Geophysik,Geotechnik,Geotechnik/Bergbau,Geowissenschaften,Germanistik,Gerontologie,Gesang / Musiktheater,Geschichte,Geschichte der Naturwissenschaften und der Technik,Geschichte des Mittelalters,Gesellschaftswissenschaften,Gestaltungstechnik,Gesundheitsmanagement,Gesundheitspsychologie,Gesundheitswissenschaften,Gewerbelehrer,Gitarre,Global Logistics,Global Management,Global Studies,Globaler Wandel,Goldschmiedekunst,Griechische Philologie,Grundschuldidaktik,Handel,Hauptschuldidaktik,Hauswirtschaft und Werken,Hauswirtschaftswissenschaften,Hebammenkunde,Hebraistik,Heilpädagogik,Heimat- und Sachunterricht,Historische Hilfswissenschaften,Historische Instrumente,Historische Kunst- und Bilddiskurse,Historische Sprach-, Text- und Kulturwissenschaften,Hochbau,Holocaust Communication and Tolerance,Holzbau und Ausbau,Holzgestaltung,Holztechnik,Holzwirtschaft,Hörakustik,Hörfunk,Hotel- und Gastronomiemanagement,Human Resource Management - Personalpolitik,Humanbiologie,Humangeographie,Humanitäre Hilfe,Humanities,Hüttenwesen/Metallkunde,Hydrogeology and Environmental Geosciences,Hydrologie,Iberoromanische Sprachen,Immobilienwirtschaft,Indogermanistik,Indoiranistik,Indologie,Industrie-Elektronik,Industrielles Management und Engineering,Industriemarketing und Technischer Vertrieb,Informatik,Informatik und Kommunikationswissenschaften,Informatik-Ingenieurwesen,Information und Kommunikation,Informations- und Medientechnik,Informationsmanagement,Informationsmanagement im Gesundheitswesen,Informationsrecht,Informationstechnik,Informationsverarbeitung,Informationswissenschaften,Infrastruktur und Umwelt,Ingenieurbau,Ingenieurwesen,Ingenieurwissenschaften, allgemeine,Innenarchitektur,Instrumente und Gesang,Integrative Lerntherapie,Integrierte Gerontologie,Interaction Design,Intercultural Humanities,Intercultural Linguistics,Interdisziplinäre Antisemitismusforschung,Interdisziplinäre Mediävistik,Interdisziplinäre Polenstudien,Interdisziplinäre Sachbildung,Interdisziplinäre Studien,Interkulturelle Amerika- und Europastudien,Interkulturelle Kommunikation,Interkulturelle Studien: Polen und Deutsche in Europa,Interkulturelle Wirtschaftskommunikation,Interkulturelles Management,International Business,International Business and Economics,International Business and Social Sciences,International Business Communication,International Development Studies,International Fashion Retail,International Human Rights,International Information Systems,International Management,International Studies of Technology,Internationale Agrarwissenschaften,Internationale Beziehungen,Internationale Kulturhistorische Studien,Internationale Migration und Interkulturelle Beziehungen,Internationales Bauwesen,Internationales Marketing,Internationales Produkt- und Servicemanagement,Internationales Recht,Internationales Wirtschaftsingenieurwesen,Internet Science & Technology,Interreligiöse Studien,Iranistik,Islamwissenschaft,IT-Governance, Risk and Compliance Management,IT-Management,Italienisch,Japanologie,Jazz/Popularmusik,Journalism and Bionics,Journalistik,Judaistik/Jüdische Studien,Jugendliche Delinquenz,Kammermusik,Kardiotechnik,Kartographie,Kaukasiologie,Keilschriftkunde/Papyrologie,Keltologie,Keramik,Freie Kerntechnik,Kinder- und Jugendchorleitung,Kinderrecht,Kirche und Kultur,Kirchenmusik,Klassische Altertumswissenschaften,Klassische Philologie,Klimaschutz und -anpassung,Klinische Sozialarbeit,Kognitionswissenschaft,Kommunaler Verwaltungsdienst,Kommunalwirtschaft,Kommunikationsdesign / -technik,Kommunikationspsychologie,Kommunikationswissenschaft,Komposition / Theorie / Tonsatz,Konferenzdolmetschen,Konzertexamen,Koreanistik,Körperpflege,Korrepetition,Kosmetika und Waschmittel,Kosmetologie,Kraftfahrzeugelektronik,Kraftwerkstechnik,Krankenhaus- / Krankenpflege- / Sozialmanagement,Krankenpflege,Krankenversicherungs-Management,Kriminalistik,Kriminologie,Kultur und Medien,Kultur und Technik,Kultur und Wirtschaft,Kultur- und Medienmanagement,Kultur- und Medienpädagogik,Kulturanthropologie,Kulturarbeit,Kulturelle Grundlagen Europas,Kulturgeographie,Kulturgutsicherung,Kulturjournalismus,Kulturmanagement,Kulturpädagogik und Kulturmanagement,Kulturwissenschaft der Antike,Kulturwissenschaft, Wissensmanagement, Logistik: Cultural Engineering,Kulturwissenschaften,Kunst, Freie Kunst, Musik und Medien: Organisation und Vermittlung,Kunsterziehung / Künstlerisches Werken,Kunstgeschichte/Kunstwissenschaft,Kunstmanagement,Kunststofftechnik,Kunsttherapie,Kunstwissenschaft und Medientheorie,Landbau/Landwirtschaft,Landnutzung,Landschaftsökologie,Landschaftsplanung/-architektur,Laser- und Optotechnologien,Latein,Lateinamerikanistik,Lateinische Philologie,Lebensmittelchemie / -technologie,Lebensmittelwirtschaft,Leistungssport,Lernbereich Ästhetische Erziehung,Lernbereich Gesellschaftslehre,Lernbereich Kunst/Musik,Lernbereich Natur- und Gesellschaftswissenschaften,Lernbereich Naturwissenschaften/Technik,Lernbereich Sprachliche Grundbildung,Liberal Arts and Sciences,Lichtdesign,Life Sciences,Linguistische Datenverarbeitung,Literarisches Schreiben,Literatur und Medien,Literaturübersetzen,Literaturwissenschaft,Logik,Logistik - Management,Logistik, technische,Logopädie,Luft- und Raumfahrttechnik,Luftfahrzeugtechnik,Luftverkehrsmanagement,Makromolekülforschung,Management and Economics,Management in Klein- und mittelständischen Unternehmen,Management in Non-Profit-Organisationen,Management und Vertrieb,Management, Philosophy & Economics,Marine Biology,Maritime Technologien,Marketing,Markscheidewesen,Maschinenbau,Maschinenbau-Informatik,Maschinenbaumanagement,Maskenbild,Material- und Nanochemie,Materialtechnik,Materialwissenschaften,Mathematik,Mechanik,Mechatronik,Media Innovation Management,MediaArchitecture,Mediale Räume,Medical Education,Medien und Informationswesen / Medieninformatik,Medien- und Instruktionspsychologie,Medien- und Kulturwissenschaften,Medien- und Wirtschaftspsychologie,Mediengestaltung / Medienmanagement,Medienmanagement / Medienwirtschaft,Medienrecht,Medienwissenschaften,Medizin,Medizin-Ethik-Recht,Medizininformatik,Medizinisch-Biologische Chemie,Medizinische Assistenz,Medizinische Physik,Medizinische Radiologie-Technologie,Medizinische Systeme,Medizinmanagement,Medizinrecht,Medizintechnik,Mehrsprachigkeit,Mensch und Technik,Mergers and Acquisitions,Messe-, Kongress- und Eventmanagement,Messtechnik,Metalltechnik,Meteorologie,Methoden und Didaktik in angewandten Wissenschaften,Mikrobiologie,Mikrosystemtechnik,Milch- und Molkereiwirtschaft,Militärgeschichte/Militärsoziologie,Mineralogie,Mittlere und neuere Geschichte,Mobile Systeme,Mode- und Designmanagement,Mode-Design,Moderne Fremdsprachen,Molekulare Biologie,Molekulare Medizin,Molekulare Zellbiologie,Motologie,Multimedia-Didaktik,Museumskunde,Musical,Musik - Künstlerische Ausbildung,Musik - Solistenausbildung,Musik und Medien,Musikdesign,Musikerziehung,Musikinformatik,Musikinstrumentenbau,Musikjournalismus,Musikpädagogik und Musikvermittlung in Sozialer Arbeit,Musikproduktion,Musiktheater,Musiktherapie,Musikwissenschaft,Nachhaltige Energieökonomie,Nachhaltiger Tourismus,Nachrichtentechnik,Nahoststudien,Namenkunde/Onomastik,Natur- und Ingenieurwissenschaften,Naturheilkunde und komplementäre Medizin,Net Economy,Network Computing,Neuere und neueste Geschichte,Neugriechisch,Neuro-Cognitive Psychology,Neurowissenschaften,Niederdeutsch,Niederländische Philologie,Nordamerikastudien,Norwegisch,Nutzfahrzeugtechnologie,Nutztierwissenschaften,Ökobau,Ökologische Landwirtschaft,Ökotrophologie,Olympic Studies,Online-Medien,Open Media,Oper Chor- / Sologesang,Optotechnik und Bildverarbeitung,Organic and Molecular Electronics,Organizational Management,Orientalistik,Orthobionik,Orthodoxe Theologie,Ost- und südosteuropäische Geschichte,Ostasienwissenschaft,Osteopathie,Osteuropastudien,Ozeanographie,Pädagogik/Erziehungswissenschaft,Parodontologie,Patentingenieurwesen,Pferdewissenschaften,Pflanzenbauwissenschaften,Pflanzenbiotechnologie,Pflegelehrer / Pflegelehrerin,Pflegepädagogik,Pflegewissenschaft,Pharmaceutical Medicine,Pharmazeutische Technik / Chemie,Pharmazie,Philosophie,Phonetik und Sprechkunde,Photonik,Photovoltaik- und Halbleitertechnologie,Physician Assistance,Physik,Physik der Informationstechnologie,Physikalische Technik,Physiotherapie,Planungswissenschaften,Political and Social Studies,Politik & Wirtschaft,Politik und Recht,Politikmanagement,Politikwissenschaft,Politische Kommunikation,Polizeivollzugsdienst,Polnisch / Polonistik,Popmusik,Portugiesische Philologie,Präklinisches Management,Präventions-, Rehabilitations- und Fitnesssport,Präventionsmedizin,Print-Media-Management,Productions and Materials, Mechatronics, Design,Produktentwicklung und Produktion,Produktion und Automatisierung,Produktionsmanagement,Produktionstechnik,Projektmanagement,Projektmanagement und Engineering,Provinzialrömische Geschichte,Prozessmanagement,Psychiatrie,Psychoanalyse,Psychoanalytische Kulturwissenschaft,Psychologie,Psychologie und Mentale Gesundheit,Psychologische Psychotherapie,Psychosoziale Beratung,Psychotherapiewissenschaft,Public Management,Public Relations/Öffentlichkeitsarbeit,Publizistik/Zeitungswissenschaften,Qualität, Umwelt, Sicherheit und Hygiene,Qualitäts- und Umweltmanagement,Quality Engineering (Qualitätsingenieurwesen),Raumkonzept und Design,Rechts- und Wirtschaftswissenschaften,Rechtspflege,Rechtswissenschaft,Regenerative Biology and Medicine,Regie Oper,Regie Schauspiel,Regionalmanagement,Regionalwissenschaft Südostasien,Regionalwissenschaften,Regionalwissenschaften China,Rehabilitation Engineering,Rehabilitationspädagogik,Rehabilitationspsychologie,Religion und Psychotherapie,Religionsgeschichte, allgemeine,Religionspädagogik, evangelische,Religionspädagogik/Kirchliche Bildungsarbeit,Religionsphilosophie,Religionswissenschaft (vergleichende),Rescue Management,Restaurierung,Rettungsingenieurwesen,Rhetorik,Rhythmik,Risk Engineering & Management,Robotik,Romanistik,Rumänisch,Sachunterricht (naturwissenschaftlich),Sachunterricht (sozialwissenschaftlich),Sanitäts- und Rettungsmedizin,Schauspiel,Schiffbau,Schiffsbetriebstechnik,Schiffstechnik,Schmuckdesign,Schulpsychologie,Schwedisch,Seefahrt/Nautik,Seetouristik,Seeverkehrs- und Hafenwirtschaft,Semitistik,Sensortechnik,Service Management,Servicemanagement,Sexualwissenschaft,Sicherheit und Gefahrenabwehr,Sicherheitstechnik,Simulation und Experimentaltechnik,Simulation Technology,Sinologie,Skandinavistik/Nordistik,Slawistik,Softwaretechnik / Softwareengineering,Sonder- und Integrationspädagogik,Sonderpädagogik/Lehramt an Sonderschulen,Sorbisch,Soziale Arbeit,Sozialer Verwaltungsdienst,Sozialkunde,Sozialpädagogik,Sozialpädagogik & Management,Sozialpädagogik in der Ganztagsschule,Sozialpolitikforschung,Sozialwesen,Sozialwissenschaften/Sozialkunde,Soziologie,Sozioökonomie,Spanisch,Sport und Technik,Sportjournalismus und Sportmarketing,Sportmanagement,Sportpädagogik,Sportpsychologie,Sportwissenschaften,Sprache und Sprachförderung in Sozialer Arbeit,Sprache, Literatur, Kultur,Sprachen, angewandte,Sprachlehrforschung,Sprachwissenschaft/-theraphie,Sprachwissenschaft/Linguistik,Sprachwissenschaft/Patholinguistik,Sprecherziehung,Staats-/Verwaltungswissenschaft,Stadtökologie,Stadtplanung,Stahlbau,Statistik,Steuerwesen,Stochastik,Strafrecht,Strafvollzug,Streichinstrumente,Structural Chemistry and Spectroscopy,Suchthilfe,Südostasienwissenschaft,Südosteuropastudien,Südslawische Philologie,Supervision,Sustainability Management,Sustainability Sciences,Systems Engineering,Systemtechnik,Tanz,Tanz- und Bewegungstherapie,Tanzpädagogik,Technik,Technik und Philosophie,Technikgeschichte,Technikpädagogik,Technikrecht,Technisch-wissenschaftliche Kommunikation,Technische Betriebswirtschaftslehre,Technische Gebäudeausrüstung,Technische Informatik,Technische Kybernetik,Technische Orthopädie,Technische Physik,Technische Redaktion und Wissenskommunikation,Technisches Gebäudemanagement,Technologiemanagement,Technologiemanagement und -marketing,Technomathematik,Telekommunikation,Textil Design-Ingenieur,Textil- und Bekleidungstechnik,Textilgestaltung,The Americas - Las Américas - Les Amériques,Theater- und Kulturmanagement,Theater- und Veranstaltungstechnik,Theater-, Film- und Medienwissenschaft,Theaterausstattung,Theaterpädagogik,Theaterwissenschaften,Theologie,Theologie / Religion, katholische,Theologie / Soziale Arbeit im interkulturellen Kontext,Theologie, altkatholische,Theologie/Religion, evangelisch,Therapiewissenschaft,Tibetologie/Birmanistik,Tiefbau,Tiermedizin,Ton- und Bildtechnik,Toningenieur,Tourismus, Catering und Hospitality Services,Tourismusmanagement,Tourismusmarketing,Toxikologie,Transportation Design,Transportmanagement,Tschechische Philologie,Türkisch,Turkologie,Übersetzen (orientalische Sprachen),Umwelt & Bildung,Umwelt und Natur in metropolitanen Räumen,Umweltchemie,Umweltmanagement,Umweltnaturwissenschaften,Umweltplanung,Umweltpolitik,Umweltschutztechnik,Umwelttechnik,Umweltwirtschaft/Umweltrecht/Umweltverwaltung,Umweltwissenschaft Marine,Umweltwissenschaften/Ökologie,Uralistik,Urban Management,Urban Studies,Urgeschichte/Vorgeschichte/Frühgeschichte,Verarbeitungstechnik,Verbrennungsmotoren,Verfahrenstechnik,Vergleichende Kultur- und Religionswissenschaft,Verkehrsbetriebswirtschaft,Verkehrsbetriebswirtschaft und Personenverkehr,Verkehrsinformatik,Verkehrstechnik,Verkehrswesen,Verkehrswirtschaftsingenieurwesen,Verlagsherstellung,Verlagswirtschaft/Buchhandel,Vermessungswesen,Vermögensmanagement,Verpackungstechnik,Versicherungsmanagement,Versorgungstechnik,Versorgungstechnik und Entsorgungstechnik,Vertriebsingenieurwesen,Verwaltung und Recht,Verwaltungsdienst,Verwaltungsinformatik,Verwaltungsmanagement,Verwaltungswissenschaft,Virtual Design,Visuelle Kommunikation,Volksmusik,Volkswirtschaftslehre,Vorderasiatische Altertumswissenschaft/Archäologie,Waldorfpädagogik,Wasser- und Abfallwirtschaft,Wasserbau- und Kulturtechnik,Wasserstofftechnik,Wasserwirtschaft und Bodenmanagement,Wehrtechnik,Weinbau,Weiterbildungsforschung und Organisationsentwicklung,Werbewirtschaft,Werkstoff- und Oberfächentechnik,Werkstoffingenieurwesen,Windenergietechnik,Wirk- und Naturstoffchemie,Wirtschaft,Wirtschaft Technik Haushalt / Soziales,Wirtschafts- und Sozialgeographie,Wirtschafts- und Sozialgeschichte,Wirtschafts- und Sozialkunde,Wirtschafts- und Sozialwissenschaften,Wirtschaftschemie,Wirtschaftsförderung,Wirtschaftsgeographie,Wirtschaftsinformatik,Wirtschaftsingenieurwesen,Wirtschaftsjournalismus,Wirtschaftslehre,Wirtschaftsmathematik,Wirtschaftspädagogik,Wirtschaftsphysik,Wirtschaftspsychologie,Wirtschaftsrecht,Wirtschaftssprachen,Wirtschaftssprachen Asien und Management,Wirtschaftsübersetzen,Wirtschaftswissenschaften/Ökonomie,Wissenschaft vom christlichen Orient,Wissenschaftsforschung,Wissenschaftsgeschichte,Wissenschaftsjournalismus,Wissenschaftsmanagement,Zahnmedizin,Zolldienst,Zukunftsforschungs pp.licenses = CC by - Attribution,\ CC nc - Non-Commercial,\ diff --git a/src/main/resources/log4j-dev.properties b/src/main/resources/log4j-dev.properties index c8a5268ac4d8bda8d9917488730f0e48ee71fb90..a7dd983a1bda9e7bcfa0bf720ac2143bc678e058 100644 --- a/src/main/resources/log4j-dev.properties +++ b/src/main/resources/log4j-dev.properties @@ -9,4 +9,5 @@ log4j.category.org.springframework=INFO log4j.category.com.corundumstudio.socketio=INFO log4j.category.com.corundumstudio.socketio.handler.AuthorizeHandler=ERROR log4j.category.com.fourspaces.couchdb=OFF -log4j.category.net.sf.json=WARN \ No newline at end of file +log4j.category.net.sf.json=WARN +log4j.category.org.springframework.web.servlet.mvc=OFF diff --git a/src/site/site.xml b/src/site/site.xml index cd6bc92b515e5b3f9771b9086524777e607b241d..18f8c2385a23f25810761a0f519451806134bd31 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -14,7 +14,7 @@ <fluidoSkin> <topBarEnabled>false</topBarEnabled> <topBar> - + </topBar> <sideBarEnabled>true</sideBarEnabled> <profile>release</profile> @@ -63,6 +63,6 @@ <item name="Source Repository" href="source-repository.html" /> <item name="Metrics" href="cobertura/index.html" /> </menu> - + </body> </project> diff --git a/src/test/java/de/thm/arsnova/dao/NovaViewTest.java b/src/test/java/de/thm/arsnova/dao/NovaViewTest.java index dfe82630072019e4548c2f1a33af386a0a89823b..2459d502dbe3d122ef0de0fd134b22c4b9db5a52 100644 --- a/src/test/java/de/thm/arsnova/dao/NovaViewTest.java +++ b/src/test/java/de/thm/arsnova/dao/NovaViewTest.java @@ -149,6 +149,18 @@ public class NovaViewTest { assertNull(v4.getQueryString()); } + @Test + public void shouldSupportIncludeDocsParameter() { + final NovaView v1 = new NovaView(null); + final NovaView v2 = new NovaView(null); + final NovaView v3 = new NovaView(null); + v1.setIncludeDocs(true); + v2.setIncludeDocs(false); + assertEncodedEquals("include_docs", "true", v1.getQueryString()); + assertNull(v2.getQueryString()); + assertNull(v3.getQueryString()); + } + private void assertEncodedEquals(final String key, final String expected, final String actual) { try { assertEquals(key + "=" + URLEncoder.encode(expected, "UTF-8"), actual); diff --git a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java index 6d21ab41f2bfc9113bfab7eca7fbef43c4b16f49..a0e26baa9cfab05611458c9b4d82eb68deb2fed9 100644 --- a/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java +++ b/src/test/java/de/thm/arsnova/dao/StubDatabaseDao.java @@ -35,6 +35,7 @@ import de.thm.arsnova.entities.Question; 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 de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.NoContentException; import de.thm.arsnova.exceptions.NotFoundException; @@ -200,6 +201,12 @@ public class StubDatabaseDao implements IDatabaseDao { return null; } + @Override + public List<SessionInfo> getPublicPoolSessionsInfo() { + // TODO Auto-generated method stub + return null; + } + @Override public List<Session> getMyPublicPoolSessions(User user) { // TODO Auto-generated method stub @@ -219,9 +226,9 @@ public class StubDatabaseDao implements IDatabaseDao { } @Override - public void updateSessionOwnerActivity(Session session) { + public Session updateSessionOwnerActivity(Session session) { // TODO Auto-generated method stub - + return null; } @Override @@ -372,12 +379,6 @@ public class StubDatabaseDao implements IDatabaseDao { return null; } - @Override - public Session lockSession(Session session, Boolean lock) { - // TODO Auto-generated method stub - return null; - } - @Override public Session updateSession(Session session) { // TODO Auto-generated method stub @@ -567,4 +568,10 @@ public class StubDatabaseDao implements IDatabaseDao { // TODO Auto-generated method stub return 0; } + + @Override + public SessionInfo importSession(User user, ImportExportSession importSession) { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/test/java/de/thm/arsnova/entities/QuestionTest.java b/src/test/java/de/thm/arsnova/entities/QuestionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d75ab04218a3d7864912d80856c5203e5969f948 --- /dev/null +++ b/src/test/java/de/thm/arsnova/entities/QuestionTest.java @@ -0,0 +1,142 @@ +package de.thm.arsnova.entities; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; + +import org.junit.Test; + +public class QuestionTest { + + @SuppressWarnings("serial") + @Test + public void shouldComputeBasedOnCorrectAnswerWithExactMatch() { + final PossibleAnswer p1 = new PossibleAnswer(); + p1.setText("Yes"); + p1.setCorrect(true); + p1.setValue(10); + final PossibleAnswer p2 = new PossibleAnswer(); + p2.setText("No"); + p2.setCorrect(false); + p2.setValue(-10); + Question q = new Question(); + q.setQuestionType("yesno"); + q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{ + add(p1); + add(p2); + }}); + Answer answer1 = new Answer(); + answer1.setAnswerText("Yes"); + Answer answer2 = new Answer(); + answer2.setAnswerText("No"); + + assertEquals(10, q.calculateValue(answer1)); + assertEquals(-10, q.calculateValue(answer2)); + } + + @SuppressWarnings("serial") + @Test + public void shouldEqualAbstentionToZeroValue() { + final PossibleAnswer p1 = new PossibleAnswer(); + p1.setText("Yes"); + p1.setCorrect(true); + p1.setValue(10); + final PossibleAnswer p2 = new PossibleAnswer(); + p2.setText("No"); + p2.setCorrect(false); + p2.setValue(-10); + Question q = new Question(); + q.setAbstention(true); + q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{ + add(p1); + add(p2); + }}); + Answer answer = new Answer(); + answer.setAbstention(true); + + assertEquals(0, q.calculateValue(answer)); + } + + @SuppressWarnings("serial") + @Test + public void shouldCalculateMultipleChoiceAnswers() { + final PossibleAnswer p1 = new PossibleAnswer(); + p1.setText("Yes"); + p1.setCorrect(true); + p1.setValue(10); + final PossibleAnswer p2 = new PossibleAnswer(); + p2.setText("No"); + p2.setCorrect(false); + p2.setValue(-10); + final PossibleAnswer p3 = new PossibleAnswer(); + p3.setText("Maybe"); + p3.setCorrect(true); + p3.setValue(10); + Question q = new Question(); + q.setQuestionType("mc"); + q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{ + add(p1); + add(p2); + add(p3); + }}); + Answer answer1 = createAnswerWithText("0,0,0"); + Answer answer2 = createAnswerWithText("0,0,1"); + Answer answer3 = createAnswerWithText("0,1,0"); + Answer answer4 = createAnswerWithText("0,1,1"); + Answer answer5 = createAnswerWithText("1,0,0"); + Answer answer6 = createAnswerWithText("1,0,1"); + Answer answer7 = createAnswerWithText("1,1,0"); + Answer answer8 = createAnswerWithText("1,1,1"); + + assertEquals(0, q.calculateValue(answer1)); + assertEquals(10, q.calculateValue(answer2)); + assertEquals(-10, q.calculateValue(answer3)); + assertEquals(0, q.calculateValue(answer4)); + assertEquals(10, q.calculateValue(answer5)); + assertEquals(20, q.calculateValue(answer6)); + assertEquals(0, q.calculateValue(answer7)); + assertEquals(10, q.calculateValue(answer8)); + } + + @SuppressWarnings("serial") + @Test + public void shouldCalculatePictureQuestionAnswers() { + final PossibleAnswer p1 = new PossibleAnswer(); + p1.setText("0;0"); + p1.setCorrect(true); + p1.setValue(10); + final PossibleAnswer p2 = new PossibleAnswer(); + p2.setText("0;1"); + p2.setCorrect(false); + p2.setValue(-10); + final PossibleAnswer p3 = new PossibleAnswer(); + p3.setText("1;0"); + p3.setCorrect(true); + p3.setValue(10); + final PossibleAnswer p4 = new PossibleAnswer(); + p4.setText("1;1"); + p4.setCorrect(true); + p4.setValue(10); + Question q = new Question(); + q.setQuestionType("grid"); + q.setPossibleAnswers(new ArrayList<PossibleAnswer>() {{ + add(p1); + add(p2); + add(p3); + add(p4); + }}); + Answer answer1 = createAnswerWithText("0;0"); + Answer answer2 = createAnswerWithText("0;1,1;1"); + Answer answer3 = createAnswerWithText("0;0,1;0,1;1"); + + assertEquals(10, q.calculateValue(answer1)); + assertEquals(0, q.calculateValue(answer2)); + assertEquals(30, q.calculateValue(answer3)); + } + + private static Answer createAnswerWithText(String text) { + Answer answer = new Answer(); + answer.setAnswerText(text); + return answer; + } +} diff --git a/src/test/resources/arsnova.properties.example b/src/test/resources/arsnova.properties.example index 40f9bc76f48102c978e765a28add9ed1617c948b..628b9fbcf36eacb0377a6c73b909f9e75fc99fbb 100644 --- a/src/test/resources/arsnova.properties.example +++ b/src/test/resources/arsnova.properties.example @@ -179,6 +179,7 @@ features.question-format.grid-square.enabled=false # Without enabled session-import-export feature no sessions can be added to the # public pool +# features.session-import-export.enabled=false features.public-pool.enabled=false @@ -201,8 +202,11 @@ question.answer-option-limit=8 # effect if neither MathJax nor Markdown are enabled. question.parse-answer-option-formatting=false -# optional: demo session keyword to show above session login button -optional.demoSessionKey= +# optional: demo session id to show above session login button +session.demo-id= + +# label underneath ARSnova logo +optional.arsnova-slogan=Audience Response System # Links which are displayed in the frontend applications # @@ -214,7 +218,8 @@ links.organization.url= links.imprint.url= links.privacy-policy.url= -# configuration for the public pool +# Configuration for the public pool +# pp.subjects = Afrikanistik,Agrarbiologie,Agrarmarketing und Agrarmanagement,Agrarökologie,Agrartechnik,Agrarwissenschaften,Ägyptologie,Akkordeon,Alphabetisierung,Altbauinstandsetzung,Alte Geschichte,Altenpflege und Management,Ältere deutsche Literatur und Sprache,Altorientalistik,Ambient Assisted Living,Amerikanistik,Analytische Chemie,Angewandte Ethik,Angewandte Freizeitwissenschaft,Angewandte Informatik,Angewandte Literatur- und Kulturwissenschaften,Angewandte Mathematik,Angewandte Naturwissenschaften,Angewandte Pharmazie,Angewandte Politikwissenschaft,Angewandte Psychologie,Angewandte Sozial- und Bildungswissenschaften,Angewandte Sprachwissenschaften,Angewandte Systemwissenschaft,Anglistik / Englisch,Anthropologie,Applied Polymer Science,Applied Research,Applied System Dynamics,Arabistik,Arbeits- und Organisationspsychologie,Arbeitslehre / Arbeitswissenschaft,Arbeitslehre / Technik,Arbeitsmarktmanagement,Archäologische Restaurierung,Archäometrie,Architectural Lighting Design,Architektur,Archivwesen,Asienwissenschaften,Assyrologie,Astronomie,Astrophysik,Äthiopistik,Audioproduktion,Audiovisuelle Medien,Augenoptik,Ausstellungsdesign,Austronesien, Sprache/Kulturen,Auswärtige Angelegenheiten,Automatisierungstechnik,Automobilwirtschaft,Automotive System Engineering,Bahnbetrieb und Infrastruktur,Balkanphilologie,Baltic Management Studies,Baltische Philologie,Bank,Banking and Finance,Barrierefreie Systeme,Baubetrieb,Bauingenieurwesen,Baumanagement,Bauphysik,Baustoffingenieurwesen,Bautechnik,Beratung im Gesundheits-, Sozial- und Bildungswesen,Beratung und Sozialrecht,Beratungswissenschaft,Berufspädagogik,Betriebswirtschaft und Kultur-, Freizeit- und Sportmanagement,Betriebswirtschaft und Logistik,Betriebswirtschaft und Recht,Betriebswirtschaftslehre / BWL,Bewegung und Sport im Alter,Bibliothekswesen,Bildhauerei/Plastik,Bildung im Alter,Bildung und Erziehung im Kindesalter,Bildung und Medien,Bildungsmanagement,Bildungsplanung,Bilingualer Unterricht,Bio- and Pharmaceutical Analysis,Biochemie,Biografisches und Kreatives Schreiben,Bioinformatik,Biologie,Biologiedidaktik,Biomathematik,Biomedizinische Technik,Bionik,Biophysik,Biosystemtechnik,Biotechnologie,Biotechnologie und Medizintechnik,Bioverfahrenstechnik,Biowissenschaften,Blasinstrumente,Bodenwissenschaften,Botanik,Brand Design,Brand Management,Brauwesen und Getränketechnologie,British and American Studies,Buchkunst,Buchwissenschaften,Bühnenbild,Bühnentanz,Bundeswehrverwaltung,Business and Communication,Business and Organisation,Business Consulting,Business Ethics und CSR-Management,Business Integration,Business Travel Management,Cerebrovascular Medicine,Chemie,Chemie- und Bioingenieurwesen,Chemiedidaktik,Chemieingenieurwesen/Verfahrenstechnik,China Management,Chinesisch,Choreographie,Christliche Sozialethik und Gesellschaftspolitik,Claviorganum,Clinical Research Management,Coaching und Systementwicklung,Communication Engineering,Comparative and European Law,Compliance and Corporate Governance,Computational Mathematics,Computational Physics,Computational Science,Computer Aided Engineering,Computerlinguistik,Computervisualistik,Computing in the Humanities,Consulting und Controlling,Consumer Science,Controlling,Controlling and Risk Management,Customer Relationship Management,Dänisch,Data and Knowledge Engineering,Deaf Studies (Sprache und Kultur der Gehörlosengemeinschaft),Demographie,Denkmalpflege,Dentaltechnologie,Design,Deutsch - Französische Studien,Deutsch als Fremdsprache,Deutsch-Französisches Recht,Deutsch-Italienische Studien,Deutsches Recht,Diabetes und Management,Diakoniewissenschaft,Didaktik der Mathematik,Didaktik der Mittelschule,Dienstleistungsmanagement,Digitale Forensik,Digitale Medienproduktion,Dirigieren, Chor- und Orchesterleitung,Doing culture. Bildung und Reflexion kultureller Prozesse,Dolmetschen,Dramaturgie,Druck- und Medientechnik,E-Bass,E-Business und Social Media,Economics and Finance,Economics, Finance and Philosophy,Editions- und Dokumentwissenschaften,Education - Regelschule,Educational Media,Eigentums- und Wettbewerbsrecht,Elektro- und Informationstechnik,Elektroakustische Musik,Elektromobilität,Elementare Musikpädagogik,Elementarmathematik,Embedded Systems Engineering,Emergency (Education/Management) Practitioner,Energie und Rohstoffe,Energie- und Umweltmanagement,Energie- und Umweltschutztechnik,Energieprozesstechnik,Energietechnik,Energiewirtschaft,Engineering Physics,Entrepreneurship,Entsorgungsingenieurwesen,Epidemiology,Ergotherapie,Ergotherapie, Logopädie, Physiotherapie,Erlebnispädagogik,Erneuerbare Energien,Erwachsenenbildung,Ethik,Ethnologie,Europäische Betriebswirtschaft,Europäische Ethnologie,Europäische Geschichte,Europäische Literatur,Europäische Rechtslinguistik,Europäische Verwaltung und Politik,Europäisches Management und Controlling,Europalehramt,European Business Management,European Design,European Regulation of Network Industries,European Studies,Eurythmie,Evaluation,Evangelikale Theologie,Eventmarketing,Fachdidaktik,Fachübersetzen,Facility Management,Fahrzeug Interieur Design,Fahrzeuginformatik,Fahrzeugtechnik / Fahrzeugbau,Familienpsychologie,Farbtechnik und Raumgestaltung,Fennistik / Finnougristik,Fernsehjournalismus,Fertigungstechnik,Figurentheater,Film und Fernsehen,Filmmusik,Filmwissenschaft,Financial Information Systems,Finanz- und Rechnungswesen,Finanzmanagement,Fischereiwirtschaft,Flöte,Forstwirtschaft,Fotografie/Fotografik,FrankoMedia,Französisch,Französische Kulturwissenschaften,Friedens- und Konfliktforschung,Friesische Philologie,Frühe Hilfen,Funkidentifikation/Nahbereichsfunktechnik (RFID),Furniture and Interior Design,Gamedesign,Gartenbauwissenschaft,Gebärdensprache,Gebäude- und Energietechnik,Geisteswissenschaftliche Grundlagen,Gemeinschaftskunde,Gemeinwesenarbeit,Gender Studies,General Management,Geoarchäologie,Geographie,Geoinformatik,Geoinformationsmanagement,Geoingenieurwissenschaften,Geologie,Geoökologie,Geophysik,Geotechnik,Geotechnik/Bergbau,Geowissenschaften,Germanistik,Gerontologie,Gesang / Musiktheater,Geschichte,Geschichte der Naturwissenschaften und der Technik,Geschichte des Mittelalters,Gesellschaftswissenschaften,Gestaltungstechnik,Gesundheitsmanagement,Gesundheitspsychologie,Gesundheitswissenschaften,Gewerbelehrer,Gitarre,Global Logistics,Global Management,Global Studies,Globaler Wandel,Goldschmiedekunst,Griechische Philologie,Grundschuldidaktik,Handel,Hauptschuldidaktik,Hauswirtschaft und Werken,Hauswirtschaftswissenschaften,Hebammenkunde,Hebraistik,Heilpädagogik,Heimat- und Sachunterricht,Historische Hilfswissenschaften,Historische Instrumente,Historische Kunst- und Bilddiskurse,Historische Sprach-, Text- und Kulturwissenschaften,Hochbau,Holocaust Communication and Tolerance,Holzbau und Ausbau,Holzgestaltung,Holztechnik,Holzwirtschaft,Hörakustik,Hörfunk,Hotel- und Gastronomiemanagement,Human Resource Management - Personalpolitik,Humanbiologie,Humangeographie,Humanitäre Hilfe,Humanities,Hüttenwesen/Metallkunde,Hydrogeology and Environmental Geosciences,Hydrologie,Iberoromanische Sprachen,Immobilienwirtschaft,Indogermanistik,Indoiranistik,Indologie,Industrie-Elektronik,Industrielles Management und Engineering,Industriemarketing und Technischer Vertrieb,Informatik,Informatik und Kommunikationswissenschaften,Informatik-Ingenieurwesen,Information und Kommunikation,Informations- und Medientechnik,Informationsmanagement,Informationsmanagement im Gesundheitswesen,Informationsrecht,Informationstechnik,Informationsverarbeitung,Informationswissenschaften,Infrastruktur und Umwelt,Ingenieurbau,Ingenieurwesen,Ingenieurwissenschaften, allgemeine,Innenarchitektur,Instrumente und Gesang,Integrative Lerntherapie,Integrierte Gerontologie,Interaction Design,Intercultural Humanities,Intercultural Linguistics,Interdisziplinäre Antisemitismusforschung,Interdisziplinäre Mediävistik,Interdisziplinäre Polenstudien,Interdisziplinäre Sachbildung,Interdisziplinäre Studien,Interkulturelle Amerika- und Europastudien,Interkulturelle Kommunikation,Interkulturelle Studien: Polen und Deutsche in Europa,Interkulturelle Wirtschaftskommunikation,Interkulturelles Management,International Business,International Business and Economics,International Business and Social Sciences,International Business Communication,International Development Studies,International Fashion Retail,International Human Rights,International Information Systems,International Management,International Studies of Technology,Internationale Agrarwissenschaften,Internationale Beziehungen,Internationale Kulturhistorische Studien,Internationale Migration und Interkulturelle Beziehungen,Internationales Bauwesen,Internationales Marketing,Internationales Produkt- und Servicemanagement,Internationales Recht,Internationales Wirtschaftsingenieurwesen,Internet Science & Technology,Interreligiöse Studien,Iranistik,Islamwissenschaft,IT-Governance, Risk and Compliance Management,IT-Management,Italienisch,Japanologie,Jazz/Popularmusik,Journalism and Bionics,Journalistik,Judaistik/Jüdische Studien,Jugendliche Delinquenz,Kammermusik,Kardiotechnik,Kartographie,Kaukasiologie,Keilschriftkunde/Papyrologie,Keltologie,Keramik,Freie Kerntechnik,Kinder- und Jugendchorleitung,Kinderrecht,Kirche und Kultur,Kirchenmusik,Klassische Altertumswissenschaften,Klassische Philologie,Klimaschutz und -anpassung,Klinische Sozialarbeit,Kognitionswissenschaft,Kommunaler Verwaltungsdienst,Kommunalwirtschaft,Kommunikationsdesign / -technik,Kommunikationspsychologie,Kommunikationswissenschaft,Komposition / Theorie / Tonsatz,Konferenzdolmetschen,Konzertexamen,Koreanistik,Körperpflege,Korrepetition,Kosmetika und Waschmittel,Kosmetologie,Kraftfahrzeugelektronik,Kraftwerkstechnik,Krankenhaus- / Krankenpflege- / Sozialmanagement,Krankenpflege,Krankenversicherungs-Management,Kriminalistik,Kriminologie,Kultur und Medien,Kultur und Technik,Kultur und Wirtschaft,Kultur- und Medienmanagement,Kultur- und Medienpädagogik,Kulturanthropologie,Kulturarbeit,Kulturelle Grundlagen Europas,Kulturgeographie,Kulturgutsicherung,Kulturjournalismus,Kulturmanagement,Kulturpädagogik und Kulturmanagement,Kulturwissenschaft der Antike,Kulturwissenschaft, Wissensmanagement, Logistik: Cultural Engineering,Kulturwissenschaften,Kunst, Freie Kunst, Musik und Medien: Organisation und Vermittlung,Kunsterziehung / Künstlerisches Werken,Kunstgeschichte/Kunstwissenschaft,Kunstmanagement,Kunststofftechnik,Kunsttherapie,Kunstwissenschaft und Medientheorie,Landbau/Landwirtschaft,Landnutzung,Landschaftsökologie,Landschaftsplanung/-architektur,Laser- und Optotechnologien,Latein,Lateinamerikanistik,Lateinische Philologie,Lebensmittelchemie / -technologie,Lebensmittelwirtschaft,Leistungssport,Lernbereich Ästhetische Erziehung,Lernbereich Gesellschaftslehre,Lernbereich Kunst/Musik,Lernbereich Natur- und Gesellschaftswissenschaften,Lernbereich Naturwissenschaften/Technik,Lernbereich Sprachliche Grundbildung,Liberal Arts and Sciences,Lichtdesign,Life Sciences,Linguistische Datenverarbeitung,Literarisches Schreiben,Literatur und Medien,Literaturübersetzen,Literaturwissenschaft,Logik,Logistik - Management,Logistik, technische,Logopädie,Luft- und Raumfahrttechnik,Luftfahrzeugtechnik,Luftverkehrsmanagement,Makromolekülforschung,Management and Economics,Management in Klein- und mittelständischen Unternehmen,Management in Non-Profit-Organisationen,Management und Vertrieb,Management, Philosophy & Economics,Marine Biology,Maritime Technologien,Marketing,Markscheidewesen,Maschinenbau,Maschinenbau-Informatik,Maschinenbaumanagement,Maskenbild,Material- und Nanochemie,Materialtechnik,Materialwissenschaften,Mathematik,Mechanik,Mechatronik,Media Innovation Management,MediaArchitecture,Mediale Räume,Medical Education,Medien und Informationswesen / Medieninformatik,Medien- und Instruktionspsychologie,Medien- und Kulturwissenschaften,Medien- und Wirtschaftspsychologie,Mediengestaltung / Medienmanagement,Medienmanagement / Medienwirtschaft,Medienrecht,Medienwissenschaften,Medizin,Medizin-Ethik-Recht,Medizininformatik,Medizinisch-Biologische Chemie,Medizinische Assistenz,Medizinische Physik,Medizinische Radiologie-Technologie,Medizinische Systeme,Medizinmanagement,Medizinrecht,Medizintechnik,Mehrsprachigkeit,Mensch und Technik,Mergers and Acquisitions,Messe-, Kongress- und Eventmanagement,Messtechnik,Metalltechnik,Meteorologie,Methoden und Didaktik in angewandten Wissenschaften,Mikrobiologie,Mikrosystemtechnik,Milch- und Molkereiwirtschaft,Militärgeschichte/Militärsoziologie,Mineralogie,Mittlere und neuere Geschichte,Mobile Systeme,Mode- und Designmanagement,Mode-Design,Moderne Fremdsprachen,Molekulare Biologie,Molekulare Medizin,Molekulare Zellbiologie,Motologie,Multimedia-Didaktik,Museumskunde,Musical,Musik - Künstlerische Ausbildung,Musik - Solistenausbildung,Musik und Medien,Musikdesign,Musikerziehung,Musikinformatik,Musikinstrumentenbau,Musikjournalismus,Musikpädagogik und Musikvermittlung in Sozialer Arbeit,Musikproduktion,Musiktheater,Musiktherapie,Musikwissenschaft,Nachhaltige Energieökonomie,Nachhaltiger Tourismus,Nachrichtentechnik,Nahoststudien,Namenkunde/Onomastik,Natur- und Ingenieurwissenschaften,Naturheilkunde und komplementäre Medizin,Net Economy,Network Computing,Neuere und neueste Geschichte,Neugriechisch,Neuro-Cognitive Psychology,Neurowissenschaften,Niederdeutsch,Niederländische Philologie,Nordamerikastudien,Norwegisch,Nutzfahrzeugtechnologie,Nutztierwissenschaften,Ökobau,Ökologische Landwirtschaft,Ökotrophologie,Olympic Studies,Online-Medien,Open Media,Oper Chor- / Sologesang,Optotechnik und Bildverarbeitung,Organic and Molecular Electronics,Organizational Management,Orientalistik,Orthobionik,Orthodoxe Theologie,Ost- und südosteuropäische Geschichte,Ostasienwissenschaft,Osteopathie,Osteuropastudien,Ozeanographie,Pädagogik/Erziehungswissenschaft,Parodontologie,Patentingenieurwesen,Pferdewissenschaften,Pflanzenbauwissenschaften,Pflanzenbiotechnologie,Pflegelehrer / Pflegelehrerin,Pflegepädagogik,Pflegewissenschaft,Pharmaceutical Medicine,Pharmazeutische Technik / Chemie,Pharmazie,Philosophie,Phonetik und Sprechkunde,Photonik,Photovoltaik- und Halbleitertechnologie,Physician Assistance,Physik,Physik der Informationstechnologie,Physikalische Technik,Physiotherapie,Planungswissenschaften,Political and Social Studies,Politik & Wirtschaft,Politik und Recht,Politikmanagement,Politikwissenschaft,Politische Kommunikation,Polizeivollzugsdienst,Polnisch / Polonistik,Popmusik,Portugiesische Philologie,Präklinisches Management,Präventions-, Rehabilitations- und Fitnesssport,Präventionsmedizin,Print-Media-Management,Productions and Materials, Mechatronics, Design,Produktentwicklung und Produktion,Produktion und Automatisierung,Produktionsmanagement,Produktionstechnik,Projektmanagement,Projektmanagement und Engineering,Provinzialrömische Geschichte,Prozessmanagement,Psychiatrie,Psychoanalyse,Psychoanalytische Kulturwissenschaft,Psychologie,Psychologie und Mentale Gesundheit,Psychologische Psychotherapie,Psychosoziale Beratung,Psychotherapiewissenschaft,Public Management,Public Relations/Öffentlichkeitsarbeit,Publizistik/Zeitungswissenschaften,Qualität, Umwelt, Sicherheit und Hygiene,Qualitäts- und Umweltmanagement,Quality Engineering (Qualitätsingenieurwesen),Raumkonzept und Design,Rechts- und Wirtschaftswissenschaften,Rechtspflege,Rechtswissenschaft,Regenerative Biology and Medicine,Regie Oper,Regie Schauspiel,Regionalmanagement,Regionalwissenschaft Südostasien,Regionalwissenschaften,Regionalwissenschaften China,Rehabilitation Engineering,Rehabilitationspädagogik,Rehabilitationspsychologie,Religion und Psychotherapie,Religionsgeschichte, allgemeine,Religionspädagogik, evangelische,Religionspädagogik/Kirchliche Bildungsarbeit,Religionsphilosophie,Religionswissenschaft (vergleichende),Rescue Management,Restaurierung,Rettungsingenieurwesen,Rhetorik,Rhythmik,Risk Engineering & Management,Robotik,Romanistik,Rumänisch,Sachunterricht (naturwissenschaftlich),Sachunterricht (sozialwissenschaftlich),Sanitäts- und Rettungsmedizin,Schauspiel,Schiffbau,Schiffsbetriebstechnik,Schiffstechnik,Schmuckdesign,Schulpsychologie,Schwedisch,Seefahrt/Nautik,Seetouristik,Seeverkehrs- und Hafenwirtschaft,Semitistik,Sensortechnik,Service Management,Servicemanagement,Sexualwissenschaft,Sicherheit und Gefahrenabwehr,Sicherheitstechnik,Simulation und Experimentaltechnik,Simulation Technology,Sinologie,Skandinavistik/Nordistik,Slawistik,Softwaretechnik / Softwareengineering,Sonder- und Integrationspädagogik,Sonderpädagogik/Lehramt an Sonderschulen,Sorbisch,Soziale Arbeit,Sozialer Verwaltungsdienst,Sozialkunde,Sozialpädagogik,Sozialpädagogik & Management,Sozialpädagogik in der Ganztagsschule,Sozialpolitikforschung,Sozialwesen,Sozialwissenschaften/Sozialkunde,Soziologie,Sozioökonomie,Spanisch,Sport und Technik,Sportjournalismus und Sportmarketing,Sportmanagement,Sportpädagogik,Sportpsychologie,Sportwissenschaften,Sprache und Sprachförderung in Sozialer Arbeit,Sprache, Literatur, Kultur,Sprachen, angewandte,Sprachlehrforschung,Sprachwissenschaft/-theraphie,Sprachwissenschaft/Linguistik,Sprachwissenschaft/Patholinguistik,Sprecherziehung,Staats-/Verwaltungswissenschaft,Stadtökologie,Stadtplanung,Stahlbau,Statistik,Steuerwesen,Stochastik,Strafrecht,Strafvollzug,Streichinstrumente,Structural Chemistry and Spectroscopy,Suchthilfe,Südostasienwissenschaft,Südosteuropastudien,Südslawische Philologie,Supervision,Sustainability Management,Sustainability Sciences,Systems Engineering,Systemtechnik,Tanz,Tanz- und Bewegungstherapie,Tanzpädagogik,Technik,Technik und Philosophie,Technikgeschichte,Technikpädagogik,Technikrecht,Technisch-wissenschaftliche Kommunikation,Technische Betriebswirtschaftslehre,Technische Gebäudeausrüstung,Technische Informatik,Technische Kybernetik,Technische Orthopädie,Technische Physik,Technische Redaktion und Wissenskommunikation,Technisches Gebäudemanagement,Technologiemanagement,Technologiemanagement und -marketing,Technomathematik,Telekommunikation,Textil Design-Ingenieur,Textil- und Bekleidungstechnik,Textilgestaltung,The Americas - Las Américas - Les Amériques,Theater- und Kulturmanagement,Theater- und Veranstaltungstechnik,Theater-, Film- und Medienwissenschaft,Theaterausstattung,Theaterpädagogik,Theaterwissenschaften,Theologie,Theologie / Religion, katholische,Theologie / Soziale Arbeit im interkulturellen Kontext,Theologie, altkatholische,Theologie/Religion, evangelisch,Therapiewissenschaft,Tibetologie/Birmanistik,Tiefbau,Tiermedizin,Ton- und Bildtechnik,Toningenieur,Tourismus, Catering und Hospitality Services,Tourismusmanagement,Tourismusmarketing,Toxikologie,Transportation Design,Transportmanagement,Tschechische Philologie,Türkisch,Turkologie,Übersetzen (orientalische Sprachen),Umwelt & Bildung,Umwelt und Natur in metropolitanen Räumen,Umweltchemie,Umweltmanagement,Umweltnaturwissenschaften,Umweltplanung,Umweltpolitik,Umweltschutztechnik,Umwelttechnik,Umweltwirtschaft/Umweltrecht/Umweltverwaltung,Umweltwissenschaft Marine,Umweltwissenschaften/Ökologie,Uralistik,Urban Management,Urban Studies,Urgeschichte/Vorgeschichte/Frühgeschichte,Verarbeitungstechnik,Verbrennungsmotoren,Verfahrenstechnik,Vergleichende Kultur- und Religionswissenschaft,Verkehrsbetriebswirtschaft,Verkehrsbetriebswirtschaft und Personenverkehr,Verkehrsinformatik,Verkehrstechnik,Verkehrswesen,Verkehrswirtschaftsingenieurwesen,Verlagsherstellung,Verlagswirtschaft/Buchhandel,Vermessungswesen,Vermögensmanagement,Verpackungstechnik,Versicherungsmanagement,Versorgungstechnik,Versorgungstechnik und Entsorgungstechnik,Vertriebsingenieurwesen,Verwaltung und Recht,Verwaltungsdienst,Verwaltungsinformatik,Verwaltungsmanagement,Verwaltungswissenschaft,Virtual Design,Visuelle Kommunikation,Volksmusik,Volkswirtschaftslehre,Vorderasiatische Altertumswissenschaft/Archäologie,Waldorfpädagogik,Wasser- und Abfallwirtschaft,Wasserbau- und Kulturtechnik,Wasserstofftechnik,Wasserwirtschaft und Bodenmanagement,Wehrtechnik,Weinbau,Weiterbildungsforschung und Organisationsentwicklung,Werbewirtschaft,Werkstoff- und Oberfächentechnik,Werkstoffingenieurwesen,Windenergietechnik,Wirk- und Naturstoffchemie,Wirtschaft,Wirtschaft Technik Haushalt / Soziales,Wirtschafts- und Sozialgeographie,Wirtschafts- und Sozialgeschichte,Wirtschafts- und Sozialkunde,Wirtschafts- und Sozialwissenschaften,Wirtschaftschemie,Wirtschaftsförderung,Wirtschaftsgeographie,Wirtschaftsinformatik,Wirtschaftsingenieurwesen,Wirtschaftsjournalismus,Wirtschaftslehre,Wirtschaftsmathematik,Wirtschaftspädagogik,Wirtschaftsphysik,Wirtschaftspsychologie,Wirtschaftsrecht,Wirtschaftssprachen,Wirtschaftssprachen Asien und Management,Wirtschaftsübersetzen,Wirtschaftswissenschaften/Ökonomie,Wissenschaft vom christlichen Orient,Wissenschaftsforschung,Wissenschaftsgeschichte,Wissenschaftsjournalismus,Wissenschaftsmanagement,Zahnmedizin,Zolldienst,Zukunftsforschungs pp.licenses = CC by - Attribution,\ CC nc - Non-Commercial,\