Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • arsnova/arsnova-backend
  • pcvl72/arsnova-backend
  • tksl38/arsnova-backend
3 results
Show changes
Showing
with 2982 additions and 1426 deletions
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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.controller;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.AbstractMappingJacksonResponseBodyAdvice;
import org.springframework.web.util.UriComponentsBuilder;
import de.thm.arsnova.model.serialization.View;
/**
* This {@link ControllerAdvice} applies a {@link View} based on the
* <code>view</code> query parameter which is used by
* {@link com.fasterxml.jackson.annotation.JsonView} for serialization and makes
* sure that the user is authorized.
*
* @author Daniel Gerhardt
*/
@ControllerAdvice
public class JsonViewControllerAdvice extends AbstractMappingJacksonResponseBodyAdvice {
private static final String VIEW_PARAMETER = "view";
private static final Logger logger = LoggerFactory.getLogger(JsonViewControllerAdvice.class);
@Override
protected void beforeBodyWriteInternal(final MappingJacksonValue bodyContainer,
final MediaType contentType, final MethodParameter returnType,
final ServerHttpRequest request, final ServerHttpResponse response) {
/* TODO: Why does @ControllerAdvice(assignableTypes = AbstractEntityController.class) not work? */
if (!AbstractEntityController.class.isAssignableFrom(returnType.getContainingClass())) {
return;
}
final List<String> viewList = UriComponentsBuilder.fromUri(request.getURI()).build()
.getQueryParams().getOrDefault(VIEW_PARAMETER, Collections.emptyList());
if (viewList.isEmpty()) {
return;
}
final String view = viewList.get(0);
logger.debug("'{}' parameter found in request URI: {}", VIEW_PARAMETER, view);
if (bodyContainer.getValue() instanceof Collection) {
logger.warn("'{}' parameter is currently not supported for listing endpoints.", VIEW_PARAMETER);
}
tryAccess(bodyContainer.getValue(), view);
switch (view) {
case "owner":
bodyContainer.setSerializationView(View.Owner.class);
break;
case "admin":
bodyContainer.setSerializationView(View.Admin.class);
break;
default:
return;
}
}
@PreAuthorize("hasPermission(#targetDomainObject, #permission)")
protected void tryAccess(final Object targetDomainObject, final Object permission) {
/* Access check is done by aspect. No additional implementation needed. */
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.controller;
import de.thm.arsnova.PaginationListDecorator;
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;
import de.thm.arsnova.web.DeprecatedApi;
import de.thm.arsnova.web.Pagination;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Handles requests related to questions teachers are asking their students.
*/
@RestController
@RequestMapping("/lecturerquestion")
@Api(value = "/lecturerquestion", description = "Operations for Lecture Questions")
public class LecturerQuestionController extends PaginationController {
@Autowired
private IQuestionService questionService;
@ApiOperation(value = "Get question with provided question Id",
nickname = "getQuestion")
@ApiResponses(value = {
@ApiResponse(code = 404, message = HTML_STATUS_404)
})
@RequestMapping(value = "/{questionId}", method = RequestMethod.GET)
public Question getQuestion(@PathVariable final String questionId) {
final Question question = questionService.getQuestion(questionId);
if (question != null) {
return question;
}
throw new NotFoundException();
}
@ApiOperation(value = "Post provided question",
nickname = "postQuestion")
@ApiResponses(value = {
@ApiResponse(code = 400, message = HTML_STATUS_400)
})
@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public Question postQuestion(@RequestBody final Question question) {
if (questionService.saveQuestion(question) != null) {
return question;
}
throw new BadRequestException();
}
@ApiOperation(value = "Post provided questions", nickname = "bulkPostQuestions")
@ApiResponses(value = {
@ApiResponse(code = 400, message = HTML_STATUS_400)
})
@RequestMapping(value = "/bulk", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public List<Question> bulkPostQuestions(@RequestBody final List<Question> questions) {
for (final Question question : questions) {
if (questionService.saveQuestion(question) == null) {
throw new BadRequestException();
}
}
return questions;
}
@ApiOperation(value = "Update the question, identified by provided id, with the provided question in the Request Body",
nickname = "updateQuestion")
@ApiResponses(value = {
@ApiResponse(code = 400, message = HTML_STATUS_400)
})
@RequestMapping(value = "/{questionId}", method = RequestMethod.PUT)
public Question updateQuestion(
@PathVariable final String questionId,
@RequestBody final Question question
) {
try {
return questionService.update(question);
} catch (final Exception e) {
throw new BadRequestException();
}
}
@ApiOperation(value = "Start new Pi Round on question, identified by provided id, with an optional time",
nickname = "startPiRound")
@RequestMapping(value = "/{questionId}/questionimage", method = RequestMethod.GET)
public String getQuestionImage(
@PathVariable final String questionId,
@RequestParam(value = "fcImage", defaultValue = "false", required = false) final boolean fcImage
) {
if (fcImage) {
return questionService.getQuestionFcImage(questionId);
} else {
return questionService.getQuestionImage(questionId);
}
}
@RequestMapping(value = "/{questionId}/startnewpiround", method = RequestMethod.POST)
public void startPiRound(
@PathVariable final String questionId,
@RequestParam(value = "time", defaultValue = "0", required = false) final int time
) {
if (time == 0) {
questionService.startNewPiRound(questionId, null);
} else {
questionService.startNewPiRoundDelayed(questionId, time);
}
}
@RequestMapping(value = "/{questionId}/canceldelayedpiround", method = RequestMethod.POST)
@ApiOperation(value = "Cancel Pi Round on question, identified by provided id",
nickname = "cancelPiRound")
public void cancelPiRound(
@PathVariable final String questionId
) {
questionService.cancelPiRoundChange(questionId);
}
@RequestMapping(value = "/{questionId}/resetpiroundstate", method = RequestMethod.POST)
@ApiOperation(value = "Reset Pi Round on question, identified by provided id",
nickname = "resetPiQuestion")
public void resetPiQuestion(
@PathVariable final String questionId
) {
questionService.resetPiRoundState(questionId);
}
@ApiOperation(value = "Set voting admission on question, identified by provided id",
nickname = "setVotingAdmission")
@RequestMapping(value = "/{questionId}/disablevote", method = RequestMethod.POST)
public void setVotingAdmission(
@PathVariable final String questionId,
@RequestParam(value = "disable", defaultValue = "false", required = false) final Boolean disableVote
) {
boolean disable = false;
if (disableVote != null) {
disable = disableVote;
}
questionService.setVotingAdmission(questionId, disable);
}
@ApiOperation(value = "Set voting admission for all questions",
nickname = "setVotingAdmissionForAllQuestions")
@RequestMapping(value = "/disablevote", method = RequestMethod.POST)
public void setVotingAdmissionForAllQuestions(
@RequestParam final String sessionkey,
@RequestParam(value = "disable", defaultValue = "false", required = false) final Boolean disableVote,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false", required = false) final boolean lectureQuestionsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) final boolean preparationQuestionsOnly
) {
boolean disable = false;
List<Question> questions;
if (disableVote != null) {
disable = disableVote;
}
if (lectureQuestionsOnly) {
questions = questionService.getLectureQuestions(sessionkey);
questionService.setVotingAdmissions(sessionkey, disable, questions);
} else if (preparationQuestionsOnly) {
questions = questionService.getPreparationQuestions(sessionkey);
questionService.setVotingAdmissions(sessionkey, disable, questions);
} else {
questionService.setVotingAdmissionForAllQuestions(sessionkey, disable);
}
}
@ApiOperation(value = "Publish a question, identified by provided id and question in Request Body.",
nickname = "publishQuestion")
@RequestMapping(value = "/{questionId}/publish", method = RequestMethod.POST)
public void publishQuestion(
@PathVariable final String questionId,
@RequestParam(required = false) final Boolean publish,
@RequestBody final Question question
) {
if (publish != null) {
question.setActive(publish);
}
questionService.update(question);
}
@ApiOperation(value = "Publish all questions",
nickname = "publishAllQuestions")
@RequestMapping(value = "/publish", method = RequestMethod.POST)
public void publishAllQuestions(
@RequestParam final String sessionkey,
@RequestParam(required = false) final Boolean publish,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false", required = false) final boolean lectureQuestionsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) final boolean preparationQuestionsOnly
) {
boolean p = publish == null || publish;
List<Question> questions;
if (lectureQuestionsOnly) {
questions = questionService.getLectureQuestions(sessionkey);
questionService.publishQuestions(sessionkey, p, questions);
} else if (preparationQuestionsOnly) {
questions = questionService.getPreparationQuestions(sessionkey);
questionService.publishQuestions(sessionkey, p, questions);
} else {
questionService.publishAll(sessionkey, p);
}
}
@ApiOperation(value = "Publish statistics from question with provided id",
nickname = "publishStatistics")
@RequestMapping(value = "/{questionId}/publishstatistics", method = RequestMethod.POST)
public void publishStatistics(
@PathVariable final String questionId,
@RequestParam(required = false) final Boolean showStatistics,
@RequestBody final Question question
) {
if (showStatistics != null) {
question.setShowStatistic(showStatistics);
}
questionService.update(question);
}
@ApiOperation(value = "Publish correct answer from question with provided id",
nickname = "publishCorrectAnswer")
@RequestMapping(value = "/{questionId}/publishcorrectanswer", method = RequestMethod.POST)
public void publishCorrectAnswer(
@PathVariable final String questionId,
@RequestParam(required = false) final Boolean showCorrectAnswer,
@RequestBody final Question question
) {
if (showCorrectAnswer != null) {
question.setShowAnswer(showCorrectAnswer);
}
questionService.update(question);
}
@ApiOperation(value = "Get skill questions",
nickname = "getSkillQuestions")
@RequestMapping(value = "/", method = RequestMethod.GET)
@Pagination
public List<Question> getSkillQuestions(
@RequestParam final String sessionkey,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
@RequestParam(value = "flashcardsonly", defaultValue = "false") final boolean flashcardsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly,
@RequestParam(value = "requestImageData", defaultValue = "false") final boolean requestImageData,
final HttpServletResponse response
) {
List<Question> questions;
if (lectureQuestionsOnly) {
questions = questionService.getLectureQuestions(sessionkey);
} else if (flashcardsOnly) {
questions = questionService.getFlashcards(sessionkey);
} else if (preparationQuestionsOnly) {
questions = questionService.getPreparationQuestions(sessionkey);
} else {
questions = questionService.getSkillQuestions(sessionkey);
}
if (questions == null || questions.isEmpty()) {
response.setStatus(HttpStatus.NO_CONTENT.value());
return null;
} else if (!requestImageData) {
questions = questionService.replaceImageData(questions);
}
return new PaginationListDecorator<>(questions, offset, limit);
}
@ApiOperation(value = "Delete skill questions",
nickname = "deleteSkillQuestions")
@RequestMapping(value = { "/" }, method = RequestMethod.DELETE)
public void deleteSkillQuestions(
@RequestParam final String sessionkey,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
@RequestParam(value = "flashcardsonly", defaultValue = "false") final boolean flashcardsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly,
final HttpServletResponse response
) {
if (lectureQuestionsOnly) {
questionService.deleteLectureQuestions(sessionkey);
} else if (flashcardsOnly) {
questionService.deleteFlashcards(sessionkey);
} else if (preparationQuestionsOnly) {
questionService.deletePreparationQuestions(sessionkey);
} else {
questionService.deleteAllQuestions(sessionkey);
}
}
@ApiOperation(value = "Get the amount of skill questions by the sessionkey",
nickname = "getSkillQuestionCount")
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/count", method = RequestMethod.GET)
public int getSkillQuestionCount(
@RequestParam final String sessionkey,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
@RequestParam(value = "flashcardsonly", defaultValue = "false") final boolean flashcardsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly
) {
if (lectureQuestionsOnly) {
return questionService.getLectureQuestionCount(sessionkey);
} else if (flashcardsOnly) {
return questionService.getFlashcardCount(sessionkey);
} else if (preparationQuestionsOnly) {
return questionService.getPreparationQuestionCount(sessionkey);
} else {
return questionService.getSkillQuestionCount(sessionkey);
}
}
@ApiOperation(value = "Delete answers and questions",
nickname = "deleteAnswersAndQuestion")
@RequestMapping(value = "/{questionId}", method = RequestMethod.DELETE)
public void deleteAnswersAndQuestion(
@PathVariable final String questionId
) {
questionService.deleteQuestion(questionId);
}
@ApiOperation(value = "Get unanswered skill question ID by provided session ID",
nickname = "getUnAnsweredSkillQuestionIds")
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/unanswered", method = RequestMethod.GET)
public List<String> getUnAnsweredSkillQuestionIds(
@RequestParam final String sessionkey,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly
) {
List<String> answers;
if (lectureQuestionsOnly) {
answers = questionService.getUnAnsweredLectureQuestionIds(sessionkey);
} else if (preparationQuestionsOnly) {
answers = questionService.getUnAnsweredPreparationQuestionIds(sessionkey);
} else {
answers = questionService.getUnAnsweredQuestionIds(sessionkey);
}
if (answers == null || answers.isEmpty()) {
throw new NoContentException();
}
return answers;
}
/**
* returns a JSON document which represents the given answer of a question.
*
* @param questionId
* CouchDB Question ID for which the given answer should be
* retrieved
* @return JSON Document of {@link Answer} or {@link NotFoundException}
* @throws NotFoundException
* if wrong session, wrong question or no answer was given by
* the current user
* @throws ForbiddenException
* if not logged in
*/
@ApiOperation(value = "Get my answer for a question, identified by provided question ID",
nickname = "getMyAnswer")
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/{questionId}/myanswer", method = RequestMethod.GET)
public Answer getMyAnswer(
@PathVariable final String questionId,
final HttpServletResponse response
) {
final Answer answer = questionService.getMyAnswer(questionId);
if (answer == null) {
response.setStatus(HttpStatus.NO_CONTENT.value());
return null;
}
return answer;
}
/**
* returns a list of {@link Answer}s encoded as a JSON document for a given
* question id. In this case only {@link Answer} <tt>questionId</tt>,
* <tt>answerText</tt>, <tt>answerSubject</tt> and <tt>answerCount</tt>
* properties are set
*
* @param questionId
* CouchDB Question ID for which the given answers should be
* retrieved
* @throws NotFoundException
* if wrong session, wrong question or no answers was given
* @throws ForbiddenException
* if not logged in
*/
@ApiOperation(value = "Get answers for a question, identified by provided question ID",
nickname = "getAnswers")
@RequestMapping(value = "/{questionId}/answer/", method = RequestMethod.GET)
public List<Answer> getAnswers(
@PathVariable final String questionId,
@RequestParam(value = "piround", required = false) final Integer piRound,
@RequestParam(value = "all", required = false, defaultValue = "false") final Boolean allAnswers,
final HttpServletResponse response
) {
List<Answer> answers;
if (allAnswers) {
answers = questionService.getAllAnswers(questionId, -1, -1);
} else if (null == piRound) {
answers = questionService.getAnswers(questionId, offset, limit);
} else {
if (piRound < 1 || piRound > 2) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
return null;
}
answers = questionService.getAnswers(questionId, piRound, offset, limit);
}
if (answers == null) {
return new ArrayList<>();
}
return answers;
}
@ApiOperation(value = "Save answer, provided in the Request Body, for a question, identified by provided question ID",
nickname = "saveAnswer")
@RequestMapping(value = "/{questionId}/answer/", method = RequestMethod.POST)
public Answer saveAnswer(
@PathVariable final String questionId,
@RequestBody final de.thm.arsnova.entities.transport.Answer answer,
final HttpServletResponse response
) {
return questionService.saveAnswer(questionId, answer);
}
@ApiOperation(value = "Update answer, provided in Request Body, identified by question ID and answer ID",
nickname = "updateAnswer")
@RequestMapping(value = "/{questionId}/answer/{answerId}", method = RequestMethod.PUT)
public Answer updateAnswer(
@PathVariable final String questionId,
@PathVariable final String answerId,
@RequestBody final Answer answer,
final HttpServletResponse response
) {
return questionService.updateAnswer(answer);
}
@ApiOperation(value = "Get Image, identified by question ID and answer ID",
nickname = "getImage")
@RequestMapping(value = "/{questionId}/answer/{answerId}/image", method = RequestMethod.GET)
public String getImage(
@PathVariable final String questionId,
@PathVariable final String answerId,
final HttpServletResponse response
) {
return questionService.getImage(questionId, answerId);
}
@ApiOperation(value = "Delete answer, identified by question ID and answer ID",
nickname = "deleteAnswer")
@RequestMapping(value = "/{questionId}/answer/{answerId}", method = RequestMethod.DELETE)
public void deleteAnswer(
@PathVariable final String questionId,
@PathVariable final String answerId,
final HttpServletResponse response
) {
questionService.deleteAnswer(questionId, answerId);
}
@ApiOperation(value = "Delete answers from a question, identified by question ID",
nickname = "deleteAnswers")
@RequestMapping(value = "/{questionId}/answer/", method = RequestMethod.DELETE)
public void deleteAnswers(
@PathVariable final String questionId,
final HttpServletResponse response
) {
questionService.deleteAnswers(questionId);
}
@ApiOperation(value = "Delete all answers and questions from a session, identified by sessionkey",
nickname = "deleteAllQuestionsAnswers")
@RequestMapping(value = "/answers", method = RequestMethod.DELETE)
public void deleteAllQuestionsAnswers(
@RequestParam final String sessionkey,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly,
final HttpServletResponse response
) {
if (lectureQuestionsOnly) {
questionService.deleteAllLectureAnswers(sessionkey);
} else if (preparationQuestionsOnly) {
questionService.deleteAllPreparationAnswers(sessionkey);
} else {
questionService.deleteAllQuestionsAnswers(sessionkey);
}
}
/**
*
* @param questionId
* CouchDB Question ID for which the given answers should be
* retrieved
* @return count of answers for given question id
* @throws NotFoundException
* if wrong session or wrong question
* @throws ForbiddenException
* if not logged in
*/
@ApiOperation(value = "Get the amount of answers for a question, identified by question ID",
nickname = "getAnswerCount")
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/{questionId}/answercount", method = RequestMethod.GET)
public int getAnswerCount(@PathVariable final String questionId) {
return questionService.getAnswerCount(questionId);
}
@ApiOperation(value = "Get the amount of answers for a question, identified by the question ID",
nickname = "getAllAnswerCount")
@RequestMapping(value = "/{questionId}/allroundanswercount", method = RequestMethod.GET)
public List<Integer> getAllAnswerCount(@PathVariable final String questionId) {
return Arrays.asList(
questionService.getAnswerCount(questionId, 1),
questionService.getAnswerCount(questionId, 2)
);
}
@ApiOperation(value = "Get the total amount of answers by a question, identified by the question ID",
nickname = "getTotalAnswerCountByQuestion")
@RequestMapping(value = "/{questionId}/totalanswercount", method = RequestMethod.GET)
public int getTotalAnswerCountByQuestion(@PathVariable final String questionId) {
return questionService.getTotalAnswerCountByQuestion(questionId);
}
@ApiOperation(value = "Get the amount of answers and abstention answers by a question, identified by the question ID",
nickname = "getAnswerAndAbstentionCount")
@RequestMapping(value = "/{questionId}/answerandabstentioncount", method = RequestMethod.GET)
public List<Integer> getAnswerAndAbstentionCount(@PathVariable final String questionId) {
return Arrays.asList(
questionService.getAnswerCount(questionId),
questionService.getAbstentionAnswerCount(questionId)
);
}
@ApiOperation(value = "Get all Freetext answers by a question, identified by the question ID",
nickname = "getFreetextAnswers")
@RequestMapping(value = "/{questionId}/freetextanswer/", method = RequestMethod.GET)
@Pagination
public List<Answer> getFreetextAnswers(@PathVariable final String questionId) {
return questionService.getFreetextAnswers(questionId, offset, limit);
}
@ApiOperation(value = "Get my answers of an session, identified by the sessionkey",
nickname = "getMyAnswers")
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/myanswers", method = RequestMethod.GET)
public List<Answer> getMyAnswers(@RequestParam final String sessionkey) {
return questionService.getMyAnswers(sessionkey);
}
@ApiOperation(value = "Get the total amount of answers of an session, identified by the sessionkey",
nickname = "getTotalAnswerCount")
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/answercount", method = RequestMethod.GET)
public int getTotalAnswerCount(
@RequestParam final String sessionkey,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureQuestionsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationQuestionsOnly
) {
if (lectureQuestionsOnly) {
return questionService.countLectureQuestionAnswers(sessionkey);
} else if (preparationQuestionsOnly) {
return questionService.countPreparationQuestionAnswers(sessionkey);
} else {
return questionService.getTotalAnswerCount(sessionkey);
}
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,143 +15,29 @@
* 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.controller;
import de.thm.arsnova.entities.Motd;
import de.thm.arsnova.entities.MotdList;
import de.thm.arsnova.services.IMotdService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import de.thm.arsnova.model.Motd;
import de.thm.arsnova.service.MotdService;
/**
*
*/
@RestController
@RequestMapping("/motd")
@Api(value = "/motd", description = "the Motd Controller API")
public class MotdController extends AbstractController {
@Autowired
private IMotdService motdService;
@RequestMapping(MotdController.REQUEST_MAPPING)
public class MotdController extends AbstractEntityController<Motd> {
protected static final String REQUEST_MAPPING = "/motd";
@ApiOperation(value = "get messages. if adminview=false, only messages with startdate<clientdate<enddate are returned")
@RequestMapping(value = "/", method = RequestMethod.GET)
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204),
@ApiResponse(code = 501, message = HTML_STATUS_501)
})
public List<Motd> getMotd(
@ApiParam(value = "clientdate", required = false) @RequestParam(value = "clientdate", defaultValue = "") final String clientdate,
@ApiParam(value = "adminview", required = false) @RequestParam(value = "adminview", defaultValue = "false") final Boolean adminview,
@ApiParam(value = "audience", required = false) @RequestParam(value = "audience", defaultValue = "all") final String audience,
@ApiParam(value = "sessionkey", required = false) @RequestParam(value = "sessionkey", defaultValue = "null") final String sessionkey
) {
List<Motd> motds;
Date client = new Date(System.currentTimeMillis());
if (!clientdate.isEmpty()) {
client.setTime(Long.parseLong(clientdate));
}
if (adminview) {
if ("null".equals(sessionkey)) {
motds = motdService.getAdminMotds();
} else {
motds = motdService.getAllSessionMotds(sessionkey);
}
} else {
motds = motdService.getCurrentMotds(client, audience, sessionkey);
}
return motds;
}
@ApiOperation(value = "create a new message of the day", nickname = "createMotd")
@ApiResponses(value = {
@ApiResponse(code = 201, message = HTML_STATUS_201),
@ApiResponse(code = 503, message = HTML_STATUS_503)
})
@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public Motd postNewMotd(
@ApiParam(value = "current motd", required = true) @RequestBody final Motd motd,
final HttpServletResponse response
) {
if (motd != null) {
Motd newMotd;
if ("session".equals(motd.getAudience()) && motd.getSessionkey() != null) {
newMotd = motdService.saveSessionMotd(motd.getSessionkey(), motd);
} else {
newMotd = motdService.saveMotd(motd);
}
if (newMotd == null) {
response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
return null;
}
return newMotd;
} else {
response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
return null;
}
}
@ApiOperation(value = "update a message of the day", nickname = "updateMotd")
@RequestMapping(value = "/{motdkey}", method = RequestMethod.PUT)
public Motd updateMotd(
@ApiParam(value = "motdkey from current motd", required = true) @PathVariable final String motdkey,
@ApiParam(value = "current motd", required = true) @RequestBody final Motd motd
) {
if ("session".equals(motd.getAudience()) && motd.getSessionkey() != null) {
return motdService.updateSessionMotd(motd.getSessionkey(), motd);
} else {
return motdService.updateMotd(motd);
}
}
@ApiOperation(value = "deletes a message of the day", nickname = "deleteMotd")
@RequestMapping(value = "/{motdkey}", method = RequestMethod.DELETE)
public void deleteMotd(@ApiParam(value = "Motd-key from the message that shall be deleted", required = true) @PathVariable final String motdkey) {
Motd motd = motdService.getMotd(motdkey);
if ("session".equals(motd.getAudience())) {
motdService.deleteSessionMotd(motd.getSessionkey(), motd);
} else {
motdService.deleteMotd(motd);
}
}
@ApiOperation(value = "get a list of the motdkeys the current user has confirmed to be read")
@RequestMapping(value = "/userlist", method = RequestMethod.GET)
public MotdList getUserMotdList(
@ApiParam(value = "users name", required = true) @RequestParam(value = "username", defaultValue = "null", required = true) final String username) {
return motdService.getMotdListForUser(username);
}
private MotdService motdService;
@ApiOperation(value = "create a list of the motdkeys the current user has confirmed to be read")
@RequestMapping(value = "/userlist", method = RequestMethod.POST)
public MotdList postUserMotdList(
@ApiParam(value = "current motdlist", required = true) @RequestBody final MotdList userMotdList
) {
return motdService.saveUserMotdList(userMotdList);
public MotdController(final MotdService motdService) {
super(motdService);
this.motdService = motdService;
}
@ApiOperation(value = "update a list of the motdkeys the current user has confirmed to be read")
@RequestMapping(value = "/userlist", method = RequestMethod.PUT)
public MotdList updateUserMotdList(
@ApiParam(value = "current motdlist", required = true) @RequestBody final MotdList userMotdList
) {
return motdService.updateUserMotdList(userMotdList);
@Override
protected String getMapping() {
return REQUEST_MAPPING;
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,6 +15,7 @@
* 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.controller;
/**
......@@ -24,7 +25,7 @@ public abstract class PaginationController extends AbstractController {
protected int offset = -1;
protected int limit = -1;
public void setRange(int start, int end) {
public void setRange(final int start, final int end) {
this.offset = start;
this.limit = end != -1 && start <= end ? end - start + 1 : -1;
}
......
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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.controller;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import de.thm.arsnova.model.ContentGroup;
import de.thm.arsnova.model.Room;
import de.thm.arsnova.model.RoomStatistics;
import de.thm.arsnova.service.ContentGroupService;
import de.thm.arsnova.service.RoomService;
@RestController
@RequestMapping(RoomController.REQUEST_MAPPING)
public class RoomController extends AbstractEntityController<Room> {
protected static final String REQUEST_MAPPING = "/room";
private static final String GET_MODERATORS_MAPPING = DEFAULT_ID_MAPPING + "/moderator";
private static final String MODERATOR_MAPPING = DEFAULT_ID_MAPPING + "/moderator/{userId}";
private static final String CONTENTGROUP_MAPPING = DEFAULT_ID_MAPPING + "/contentgroup/{groupName}";
private static final String CONTENTGROUP_ADD_MAPPING = CONTENTGROUP_MAPPING + "/{contentId}";
private static final String STATS_MAPPING = DEFAULT_ID_MAPPING + "/stats";
private RoomService roomService;
private ContentGroupService contentGroupService;
public RoomController(final RoomService roomService, final ContentGroupService contentGroupService) {
super(roomService);
this.roomService = roomService;
this.contentGroupService = contentGroupService;
}
@Override
protected String getMapping() {
return REQUEST_MAPPING;
}
@Override
protected String resolveAlias(final String shortId) {
return roomService.getIdByShortId(shortId);
}
@GetMapping(GET_MODERATORS_MAPPING)
public Set<Room.Moderator> getModerators(@PathVariable final String id) {
return roomService.get(id).getModerators();
}
@PutMapping(MODERATOR_MAPPING)
public void putModerator(@PathVariable final String id, @PathVariable final String userId,
@RequestBody final Room.Moderator moderator, final HttpServletResponse httpServletResponse) {
final Room room = roomService.get(id);
moderator.setUserId(userId);
if (moderator.getRoles().isEmpty()) {
moderator.getRoles().add(Room.Moderator.Role.EXECUTIVE_MODERATOR);
}
room.getModerators().removeIf(m -> m.getUserId().equals(userId));
room.getModerators().add(moderator);
roomService.update(room);
httpServletResponse.setHeader(ENTITY_ID_HEADER, room.getId());
httpServletResponse.setHeader(ENTITY_REVISION_HEADER, room.getRevision());
}
@DeleteMapping(MODERATOR_MAPPING)
public void deleteModerator(@PathVariable final String id, @PathVariable final String userId,
final HttpServletResponse httpServletResponse) {
final Room room = roomService.get(id);
room.getModerators().removeIf(m -> m.getUserId().equals(userId));
roomService.update(room);
httpServletResponse.setHeader(ENTITY_ID_HEADER, room.getId());
httpServletResponse.setHeader(ENTITY_REVISION_HEADER, room.getRevision());
}
@GetMapping(CONTENTGROUP_MAPPING)
public ContentGroup getContentGroup(@PathVariable final String id, @PathVariable final String groupName) {
return contentGroupService.getByRoomIdAndName(id, groupName);
}
@PostMapping(CONTENTGROUP_ADD_MAPPING)
public void addContentToGroup(@PathVariable final String id, @PathVariable final String groupName,
@RequestBody final String contentId) {
contentGroupService.addContentToGroup(id, groupName, contentId);
}
@PutMapping(CONTENTGROUP_MAPPING)
public void updateGroup(@PathVariable final String id, @PathVariable final String groupName,
@RequestBody final ContentGroup contentGroup) {
contentGroupService.updateContentGroup(contentGroup);
}
@GetMapping(STATS_MAPPING)
public RoomStatistics getStats(@PathVariable final String id) {
final RoomStatistics roomStatistics = new RoomStatistics();
final List<ContentGroup> contentGroups = contentGroupService.getByRoomId(id);
roomStatistics.setGroupStats(contentGroups.stream()
.map(cg -> new RoomStatistics.ContentGroupStatistics(cg)).collect(Collectors.toList()));
roomStatistics.setContentCount(contentGroups.stream()
.mapToInt(cg -> cg.getContentIds().size()).reduce((a, b) -> a + b).orElse(0));
return roomStatistics;
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.controller;
import de.thm.arsnova.connector.model.Course;
import de.thm.arsnova.entities.Session;
import de.thm.arsnova.entities.SessionFeature;
import de.thm.arsnova.entities.SessionInfo;
import de.thm.arsnova.entities.transport.ImportExportSession;
import de.thm.arsnova.entities.transport.LearningProgressValues;
import de.thm.arsnova.exceptions.UnauthorizedException;
import de.thm.arsnova.services.ISessionService;
import de.thm.arsnova.services.IUserService;
import de.thm.arsnova.services.SessionService.SessionInfoNameComparator;
import de.thm.arsnova.services.SessionService.SessionInfoShortNameComparator;
import de.thm.arsnova.services.SessionService.SessionNameComparator;
import de.thm.arsnova.services.SessionService.SessionShortNameComparator;
import de.thm.arsnova.web.DeprecatedApi;
import de.thm.arsnova.web.Pagination;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Handles requests related to ARSnova sessions.
*/
@RestController
@RequestMapping("/session")
@Api(value = "/session", description = "the Session Controller API")
public class SessionController extends PaginationController {
@Autowired
private ISessionService sessionService;
@Autowired
private IUserService userService;
@ApiOperation(value = "join a session",
nickname = "joinSession")
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/{sessionkey}", method = RequestMethod.GET)
public Session joinSession(
@ApiParam(value = "Session-Key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "Adminflag", required = false) @RequestParam(value = "admin", defaultValue = "false") final boolean admin
) {
if (admin) {
return sessionService.getSessionForAdmin(sessionkey);
} else {
return sessionService.getSession(sessionkey);
}
}
@ApiOperation(value = "deletes a session",
nickname = "deleteSession")
@RequestMapping(value = "/{sessionkey}", method = RequestMethod.DELETE)
public void deleteSession(@ApiParam(value = "Session-Key from current session", required = true) @PathVariable final String sessionkey) {
sessionService.deleteSession(sessionkey);
}
@ApiOperation(value = "count active users",
nickname = "countActiveUsers")
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/{sessionkey}/activeusercount", method = RequestMethod.GET)
public int countActiveUsers(@ApiParam(value = "Session-Key from current session", required = true) @PathVariable final String sessionkey) {
return sessionService.activeUsers(sessionkey);
}
@ApiOperation(value = "Creates a new Session and returns the Session's data",
nickname = "postNewSession")
@ApiResponses(value = {
@ApiResponse(code = 201, message = HTML_STATUS_201),
@ApiResponse(code = 503, message = HTML_STATUS_503)
})
@RequestMapping(value = "/", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public Session postNewSession(@ApiParam(value = "current session", required = true) @RequestBody final Session session, final HttpServletResponse response) {
if (session != null && session.isCourseSession()) {
final List<Course> courses = new ArrayList<>();
final Course course = new Course();
course.setId(session.getCourseId());
courses.add(course);
final int sessionCount = sessionService.countSessions(courses);
if (sessionCount > 0) {
final String appendix = " (" + (sessionCount + 1) + ")";
session.setName(session.getName() + appendix);
session.setShortName(session.getShortName() + appendix);
}
}
final Session newSession = sessionService.saveSession(session);
if (newSession == null) {
response.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());
return null;
}
return newSession;
}
@ApiOperation(value = "updates a session",
nickname = "postNewSession")
@RequestMapping(value = "/{sessionkey}", method = RequestMethod.PUT)
public Session updateSession(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "current session", required = true) @RequestBody final Session session
) {
return sessionService.updateSession(sessionkey, session);
}
@ApiOperation(value = "change the session creator (owner)", nickname = "changeSessionCreator")
@RequestMapping(value = "/{sessionkey}/changecreator", method = RequestMethod.PUT)
public Session changeSessionCreator(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "new session creator", required = true) @RequestBody final String newCreator
) {
return sessionService.changeSessionCreator(sessionkey, newCreator);
}
@ApiOperation(value = "Retrieves a list of Sessions",
nickname = "getSessions")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204),
@ApiResponse(code = 501, message = HTML_STATUS_501)
})
@RequestMapping(value = "/", method = RequestMethod.GET)
@Pagination
public List<Session> getSessions(
@ApiParam(value = "ownedOnly", required = true) @RequestParam(value = "ownedonly", defaultValue = "false") final boolean ownedOnly,
@ApiParam(value = "visitedOnly", required = true) @RequestParam(value = "visitedonly", defaultValue = "false") final boolean visitedOnly,
@ApiParam(value = "sortby", required = true) @RequestParam(value = "sortby", defaultValue = "name") final String sortby,
@ApiParam(value = "for a given username. admin rights needed", required = false) @RequestParam(value =
"username", defaultValue = "") final String username,
final HttpServletResponse response
) {
List<Session> sessions;
if (!"".equals(username)) {
try {
if (ownedOnly && !visitedOnly) {
sessions = sessionService.getUserSessions(username);
} else if (visitedOnly && !ownedOnly) {
sessions = sessionService.getUserVisitedSessions(username);
} else {
response.setStatus(HttpStatus.NOT_IMPLEMENTED.value());
return null;
}
} catch (final AccessDeniedException e) {
throw new UnauthorizedException();
}
} else {
/* TODO implement all parameter combinations, implement use of user parameter */
try {
if (ownedOnly && !visitedOnly) {
sessions = sessionService.getMySessions(offset, limit);
} else if (visitedOnly && !ownedOnly) {
sessions = sessionService.getMyVisitedSessions(offset, limit);
} else {
response.setStatus(HttpStatus.NOT_IMPLEMENTED.value());
return null;
}
} catch (final AccessDeniedException e) {
throw new UnauthorizedException();
}
}
if (sessions == null || sessions.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
if ("shortname".equals(sortby)) {
Collections.sort(sessions, new SessionShortNameComparator());
} else {
Collections.sort(sessions, new SessionNameComparator());
}
return sessions;
}
/**
* Returns a list of my own sessions with only the necessary information like name, keyword, or counters.
*/
@ApiOperation(value = "Retrieves a Session",
nickname = "getMySessions")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204)
})
@RequestMapping(value = "/", method = RequestMethod.GET, params = "statusonly=true")
@Pagination
public List<SessionInfo> getMySessions(
@ApiParam(value = "visitedOnly", required = true) @RequestParam(value = "visitedonly", defaultValue = "false") final boolean visitedOnly,
@ApiParam(value = "sort by", required = false) @RequestParam(value = "sortby", defaultValue = "name") final String sortby,
final HttpServletResponse response
) {
List<SessionInfo> sessions;
if (!visitedOnly) {
sessions = sessionService.getMySessionsInfo(offset, limit);
} else {
sessions = sessionService.getMyVisitedSessionsInfo(offset, limit);
}
if (sessions == null || sessions.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
if ("shortname".equals(sortby)) {
Collections.sort(sessions, new SessionInfoShortNameComparator());
} else {
Collections.sort(sessions, new SessionInfoNameComparator());
}
return sessions;
}
@ApiOperation(value = "Retrieves all public pool sessions for the current user",
nickname = "getMyPublicPoolSessions")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204)
})
@RequestMapping(value = "/publicpool", method = RequestMethod.GET, params = "statusonly=true")
public List<SessionInfo> getMyPublicPoolSessions(
final HttpServletResponse response
) {
List<SessionInfo> sessions = sessionService.getMyPublicPoolSessionsInfo();
if (sessions == null || sessions.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
return sessions;
}
@ApiOperation(value = "Retrieves all public pool sessions",
nickname = "getMyPublicPoolSessions")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204)
})
@RequestMapping(value = "/publicpool", method = RequestMethod.GET)
public List<SessionInfo> getPublicPoolSessions(
final HttpServletResponse response
) {
List<SessionInfo> sessions = sessionService.getPublicPoolSessionsInfo();
if (sessions == null || sessions.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
return sessions;
}
@ApiOperation(value = "imports a session",
nickname = "importSession")
@RequestMapping(value = "/import", method = RequestMethod.POST)
public SessionInfo importSession(
@ApiParam(value = "current session", required = true) @RequestBody final ImportExportSession session,
final HttpServletResponse response
) {
return sessionService.importSession(session);
}
@ApiOperation(value = "export sessions", nickname = "exportSession")
@RequestMapping(value = "/export", method = RequestMethod.GET)
public List<ImportExportSession> getExport(
@ApiParam(value = "sessionkey", required = true) @RequestParam(value = "sessionkey", defaultValue = "") final List<String> sessionkey,
@ApiParam(value = "wether statistics shall be exported", required = true) @RequestParam(value = "withAnswerStatistics", defaultValue = "false") final Boolean withAnswerStatistics,
@ApiParam(value = "wether interposed questions shall be exported", required = true) @RequestParam(value = "withFeedbackQuestions", defaultValue = "false") final Boolean withFeedbackQuestions,
final HttpServletResponse response
) {
List<ImportExportSession> sessions = new ArrayList<>();
ImportExportSession temp;
for (String key : sessionkey) {
sessionService.setActive(key, false);
temp = sessionService.exportSession(key, withAnswerStatistics, withFeedbackQuestions);
if (temp != null) {
sessions.add(temp);
}
sessionService.setActive(key, true);
}
return sessions;
}
@ApiOperation(value = "copy a session to the public pool if enabled")
@RequestMapping(value = "/{sessionkey}/copytopublicpool", method = RequestMethod.POST)
public SessionInfo copyToPublicPool(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "public pool attributes for session", required = true) @RequestBody final de.thm.arsnova.entities.transport.ImportExportSession.PublicPool publicPool
) {
sessionService.setActive(sessionkey, false);
SessionInfo sessionInfo = sessionService.copySessionToPublicPool(sessionkey, publicPool);
sessionService.setActive(sessionkey, true);
return sessionInfo;
}
@ApiOperation(value = "Locks or unlocks a Session",
nickname = "lockSession")
@ApiResponses(value = {
@ApiResponse(code = 404, message = HTML_STATUS_404)
})
@RequestMapping(value = "/{sessionkey}/lock", method = RequestMethod.POST)
public Session lockSession(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "lock", required = true) @RequestParam(required = false) final Boolean lock,
final HttpServletResponse response
) {
if (lock != null) {
return sessionService.setActive(sessionkey, lock);
}
response.setStatus(HttpStatus.NOT_FOUND.value());
return null;
}
@ApiOperation(value = "retrieves a value for the learning progress",
nickname = "getLearningProgress")
@RequestMapping(value = "/{sessionkey}/learningprogress", method = RequestMethod.GET)
public LearningProgressValues getLearningProgress(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "progress type", required = false) @RequestParam(value = "type", defaultValue = "questions") final String progressType,
@ApiParam(value = "question variant", required = false) @RequestParam(value = "questionVariant", required = false) final String questionVariant,
final HttpServletResponse response
) {
return sessionService.getLearningProgress(sessionkey, progressType, questionVariant);
}
@ApiOperation(value = "retrieves a value for the learning progress for the current user",
nickname = "getMyLearningProgress")
@RequestMapping(value = "/{sessionkey}/mylearningprogress", method = RequestMethod.GET)
public LearningProgressValues getMyLearningProgress(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@RequestParam(value = "type", defaultValue = "questions") final String progressType,
@RequestParam(value = "questionVariant", required = false) final String questionVariant,
final HttpServletResponse response
) {
return sessionService.getMyLearningProgress(sessionkey, progressType, questionVariant);
}
@ApiOperation(value = "retrieves all session features",
nickname = "getSessionFeatures")
@RequestMapping(value = "/{sessionkey}/features", method = RequestMethod.GET)
public SessionFeature getSessionFeatures(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
final HttpServletResponse response
) {
return sessionService.getSessionFeatures(sessionkey);
}
@RequestMapping(value = "/{sessionkey}/features", method = RequestMethod.PUT)
@ApiOperation(value = "change all session features",
nickname = "changeSessionFeatures")
public SessionFeature changeSessionFeatures(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "session feature", required = true) @RequestBody final SessionFeature features,
final HttpServletResponse response
) {
return sessionService.changeSessionFeatures(sessionkey, features);
}
@RequestMapping(value = "/{sessionkey}/lockfeedbackinput", method = RequestMethod.POST)
@ApiOperation(value = "locks input of user live feedback",
nickname = "lockFeedbackInput")
public boolean lockFeedbackInput(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "lock", required = true) @RequestParam(required = true) final Boolean lock,
final HttpServletResponse response
) {
return sessionService.lockFeedbackInput(sessionkey, lock);
}
@RequestMapping(value = "/{sessionkey}/flipflashcards", method = RequestMethod.POST)
@ApiOperation(value = "flip all flashcards in session",
nickname = "lockFeedbackInput")
public boolean flipFlashcards(
@ApiParam(value = "session-key from current session", required = true) @PathVariable final String sessionkey,
@ApiParam(value = "flip", required = true) @RequestParam(required = true) final Boolean flip,
final HttpServletResponse response
) {
return sessionService.flipFlashcards(sessionkey, flip);
}
/* internal redirections */
@RequestMapping(value = "/{sessionKey}/lecturerquestion")
public String redirectLecturerQuestion(
@PathVariable final String sessionKey,
final HttpServletResponse response
) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/lecturerquestion/?sessionkey=%s", sessionKey);
}
@RequestMapping(value = "/{sessionKey}/lecturerquestion/{arg1}")
public String redirectLecturerQuestionWithOneArgument(
@PathVariable final String sessionKey,
@PathVariable final String arg1,
final HttpServletResponse response
) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/lecturerquestion/%s/?sessionkey=%s", arg1, sessionKey);
}
@RequestMapping(value = "/{sessionKey}/lecturerquestion/{arg1}/{arg2}")
public String redirectLecturerQuestionWithTwoArguments(
@PathVariable final String sessionKey,
@PathVariable final String arg1,
@PathVariable final String arg2,
final HttpServletResponse response
) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/lecturerquestion/%s/%s/?sessionkey=%s", arg1, arg2, sessionKey);
}
@RequestMapping(value = "/{sessionKey}/lecturerquestion/{arg1}/{arg2}/{arg3}")
public String redirectLecturerQuestionWithThreeArguments(
@PathVariable final String sessionKey,
@PathVariable final String arg1,
@PathVariable final String arg2,
@PathVariable final String arg3,
final HttpServletResponse response
) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/lecturerquestion/%s/%s/%s/?sessionkey=%s", arg1, arg2, arg3, sessionKey);
}
@RequestMapping(value = "/{sessionKey}/audiencequestion")
public String redirectAudienceQuestion(
@PathVariable final String sessionKey,
final HttpServletResponse response
) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/audiencequestion/?sessionkey=%s", sessionKey);
}
@RequestMapping(value = "/{sessionKey}/audiencequestion/{arg1}")
public String redirectAudienceQuestionWithOneArgument(
@PathVariable final String sessionKey,
@PathVariable final String arg1,
final HttpServletResponse response
) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/audiencequestion/%s/?sessionkey=%s", arg1, sessionKey);
}
@RequestMapping(value = "/{sessionKey}/audiencequestion/{arg1}/{arg2}")
public String redirectAudienceQuestionWithTwoArguments(
@PathVariable final String sessionKey,
@PathVariable final String arg1,
@PathVariable final String arg2,
final HttpServletResponse response
) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/audiencequestion/%s/%s/?sessionkey=%s", arg1, arg2, sessionKey);
}
@RequestMapping(value = "/{sessionKey}/audiencequestion/{arg1}/{arg2}/{arg3}")
public String redirectAudienceQuestionWithThreeArguments(
@PathVariable final String sessionKey,
@PathVariable final String arg1,
@PathVariable final String arg2,
@PathVariable final String arg3,
final HttpServletResponse response
) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/audiencequestion/%s/%s/%s/?sessionkey=%s", arg1, arg2, arg3, sessionKey);
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,96 +15,128 @@
* 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.controller;
import de.thm.arsnova.entities.DbUser;
import de.thm.arsnova.services.IUserService;
import de.thm.arsnova.services.UserSessionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.stereotype.Controller;
import com.fasterxml.jackson.annotation.JsonView;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import de.thm.arsnova.model.LoginCredentials;
import de.thm.arsnova.model.UserProfile;
import de.thm.arsnova.model.serialization.View;
import de.thm.arsnova.service.RoomService;
import de.thm.arsnova.service.UserService;
import de.thm.arsnova.web.exceptions.BadRequestException;
import de.thm.arsnova.web.exceptions.ForbiddenException;
/**
* Handles requests related to ARSnova's own user registration and login process.
*/
@Controller
@RequestMapping("/user")
public class UserController extends AbstractController {
@Autowired
private DaoAuthenticationProvider daoProvider;
@Autowired
private IUserService userService;
@Autowired
private UserSessionService userSessionService;
@RequestMapping(value = "/register", method = RequestMethod.POST)
public void register(@RequestParam final String username,
@RequestParam final String password,
final HttpServletRequest request, final HttpServletResponse response) {
if (null != userService.createDbUser(username, password)) {
return;
@RestController
@RequestMapping(UserController.REQUEST_MAPPING)
public class UserController extends AbstractEntityController<UserProfile> {
protected static final String REQUEST_MAPPING = "/user";
private static final String REGISTER_MAPPING = "/register";
private static final String ACTIVATE_MAPPING = DEFAULT_ID_MAPPING + "/activate";
private static final String RESET_PASSWORD_MAPPING = DEFAULT_ID_MAPPING + "/resetpassword";
private static final String ROOM_HISTORY_MAPPING = DEFAULT_ID_MAPPING + "/roomHistory";
private UserService userService;
private RoomService roomService;
public UserController(final UserService userService, final RoomService roomService) {
super(userService);
this.userService = userService;
this.roomService = roomService;
}
class Activation {
private String key;
public String getKey() {
return key;
}
/* TODO: Improve error handling: send reason to client */
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
@JsonView(View.Public.class)
public void setKey(final String key) {
this.key = key;
}
}
@RequestMapping(value = "/{username}/activate", method = { RequestMethod.POST,
RequestMethod.GET })
public void activate(
@PathVariable final String username,
@RequestParam final String key, final HttpServletRequest request,
final HttpServletResponse response) {
DbUser dbUser = userService.getDbUser(username);
if (null != dbUser && key.equals(dbUser.getActivationKey())) {
dbUser.setActivationKey(null);
userService.updateDbUser(dbUser);
return;
class PasswordReset {
private String key;
private String password;
public String getKey() {
return key;
}
@JsonView(View.Public.class)
public void setKey(final String key) {
this.key = key;
}
public String getPassword() {
return password;
}
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
@JsonView(View.Public.class)
public void setPassword(final String password) {
this.password = password;
}
}
@RequestMapping(value = "/{username}/", method = RequestMethod.DELETE)
@Override
protected String getMapping() {
return REQUEST_MAPPING;
}
@PostMapping(REGISTER_MAPPING)
public void register(@RequestBody final LoginCredentials loginCredentials) {
if (userService.create(loginCredentials.getLoginId(), loginCredentials.getPassword()) == null) {
throw new ForbiddenException();
}
}
@PostMapping(ACTIVATE_MAPPING)
public void activate(
@PathVariable final String username,
final HttpServletRequest request,
final HttpServletResponse response) {
if (null == userService.deleteDbUser(username)) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
@PathVariable final String id,
@RequestParam final String key,
final HttpServletRequest request) {
if (!userService.activateAccount(id, key, request.getRemoteAddr())) {
throw new BadRequestException();
}
}
@RequestMapping(value = "/{username}/resetpassword", method = RequestMethod.POST)
@PostMapping(RESET_PASSWORD_MAPPING)
public void resetPassword(
@PathVariable final String username,
@RequestParam(required = false) final String key,
@RequestParam(required = false) final String password,
final HttpServletRequest request,
final HttpServletResponse response) {
DbUser dbUser = userService.getDbUser(username);
if (null == dbUser) {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return;
@PathVariable final String id,
@RequestBody final PasswordReset passwordReset) {
final UserProfile userProfile = userService.get(id, true);
if (userProfile == null) {
throw new BadRequestException();
}
if (null != key) {
if (!userService.resetPassword(dbUser, key, password)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
if (passwordReset.getKey() != null) {
if (!userService.resetPassword(userProfile, passwordReset.getKey(), passwordReset.getPassword())) {
throw new ForbiddenException();
}
} else {
userService.initiatePasswordReset(username);
userService.initiatePasswordReset(id);
}
}
@PostMapping(ROOM_HISTORY_MAPPING)
public void postRoomHistoryEntry(@PathVariable final String id,
@RequestBody final UserProfile.RoomHistoryEntry roomHistoryEntry) {
userService.addRoomToHistory(userService.get(id), roomService.get(roomHistoryEntry.getRoomId()));
}
@Override
protected String resolveAlias(final String alias) {
return userService.getByUsername(alias).getId();
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,17 +15,23 @@
* 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.controller;
import de.thm.arsnova.exceptions.BadRequestException;
import de.thm.arsnova.exceptions.NoContentException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
......@@ -34,15 +40,9 @@ import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.RedirectView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import de.thm.arsnova.management.VersionInfoContributor;
import de.thm.arsnova.web.exceptions.BadRequestException;
import de.thm.arsnova.web.exceptions.NoContentException;
/**
* Default controller that handles requests which have not set a path.
......@@ -53,37 +53,25 @@ public class WelcomeController extends AbstractController {
@Value("${mobile.path}")
private String mobileContextPath;
@Resource(name = "versionInfoProperties")
private Properties versionInfoProperties;
@Autowired
private VersionInfoContributor versionInfoContributor;
@RequestMapping(value = "/", method = RequestMethod.GET)
@GetMapping("/")
public View home() {
return new RedirectView(mobileContextPath + "/", false);
}
@RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/json")
@GetMapping(value = "/", produces = "application/json")
@ResponseBody
public Map<String, Object> jsonHome() {
Map<String, Object> response = new HashMap<>();
Map<String, Object> version = new HashMap<>();
version.put("string", versionInfoProperties.getProperty("version.string"));
version.put("buildTime", versionInfoProperties.getProperty("version.build-time"));
version.put("gitCommitId", versionInfoProperties.getProperty("version.git.commit-id"));
version.put("gitDirty", Boolean.parseBoolean(versionInfoProperties.getProperty("version.git.dirty")));
response.put("productName", "arsnova-backend");
response.put("version", version);
return response;
return versionInfoContributor.getInfoDetails();
}
@RequestMapping(value = "/checkframeoptionsheader", method = RequestMethod.POST)
@PostMapping("/checkframeoptionsheader")
@ResponseStatus(HttpStatus.OK)
public void checkFrameOptionsHeader(
@RequestParam final String url,
final HttpServletRequest request
) {
final HttpServletRequest request) {
/* Block requests from the server itself to prevent DoS attacks caused by request loops */
if ("127.0.0.1".equals(request.getRemoteAddr()) || "::1".equals(request.getRemoteAddr())) {
throw new BadRequestException("Access to localhost not allowed.");
......@@ -94,21 +82,21 @@ public class WelcomeController extends AbstractController {
if (addr.isSiteLocalAddress()) {
throw new BadRequestException("Access to site-local addresses not allowed.");
}
} catch (UnknownHostException | MalformedURLException e) {
} catch (final UnknownHostException | MalformedURLException e) {
throw new BadRequestException();
}
RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
final RestTemplate restTemplate = new RestTemplate();
final SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory) restTemplate.getRequestFactory();
rf.setConnectTimeout(2000);
rf.setReadTimeout(2000);
try {
HttpHeaders headers = restTemplate.headForHeaders(url);
final HttpHeaders headers = restTemplate.headForHeaders(url);
if (headers.isEmpty() || headers.containsKey("x-frame-options")) {
throw new NoContentException();
}
} catch (RestClientException e) {
} catch (final RestClientException e) {
throw new NoContentException();
}
}
......
/**
* The 'C' in MVC
* Controllers and helper classes which define the public API.
*/
package de.thm.arsnova.controller;
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,230 +15,173 @@
* 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.controller;
import de.thm.arsnova.entities.ServiceDescription;
import de.thm.arsnova.entities.Session;
import de.thm.arsnova.entities.User;
import de.thm.arsnova.exceptions.UnauthorizedException;
import de.thm.arsnova.services.IUserService;
import de.thm.arsnova.services.UserSessionService;
package de.thm.arsnova.controller.v2;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.pac4j.core.context.J2EContext;
import org.pac4j.core.exception.HttpAction;
import org.pac4j.oauth.client.FacebookClient;
import org.pac4j.oauth.client.Google2Client;
import org.pac4j.oauth.client.TwitterClient;
import org.pac4j.oidc.client.GoogleOidcClient;
import org.pac4j.oidc.client.OidcClient;
import org.pac4j.saml.client.SAML2Client;
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.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.cas.web.CasAuthenticationEntryPoint;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.token.Sha512DigestUtils;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.RedirectView;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import de.thm.arsnova.config.SecurityConfig;
import de.thm.arsnova.config.properties.AuthenticationProviderProperties;
import de.thm.arsnova.config.properties.SystemProperties;
import de.thm.arsnova.controller.AbstractController;
import de.thm.arsnova.model.ServiceDescription;
import de.thm.arsnova.model.UserProfile;
import de.thm.arsnova.model.migration.v2.ClientAuthentication;
import de.thm.arsnova.security.LoginAuthenticationFailureHandler;
import de.thm.arsnova.security.LoginAuthenticationSucessHandler;
import de.thm.arsnova.security.User;
import de.thm.arsnova.service.UserService;
import de.thm.arsnova.web.exceptions.UnauthorizedException;
/**
* Handles authentication specific requests.
*/
@Controller
public class LoginController extends AbstractController {
private static final int MAX_USERNAME_LENGTH = 15;
private static final int MAX_GUESTHASH_LENGTH = 10;
@Value("${api.path:}") private String apiPath;
@Controller("v2AuthenticationController")
@RequestMapping("/v2/auth")
public class AuthenticationController extends AbstractController {
private String apiPath;
@Value("${customization.path}") private String customizationPath;
@Value("${security.guest.enabled}") private boolean guestEnabled;
@Value("${security.guest.allowed-roles:speaker,student}") private String[] guestRoles;
@Value("${security.guest.order}") private int guestOrder;
@Value("${security.custom-login.enabled}") private boolean customLoginEnabled;
@Value("${security.custom-login.allowed-roles:speaker,student}") private String[] customLoginRoles;
@Value("${security.custom-login.title:University}") private String customLoginTitle;
@Value("${security.custom-login.login-dialog-path}") private String customLoginDialog;
@Value("${security.custom-login.image:}") private String customLoginImage;
@Value("${security.custom-login.order}") private int customLoginOrder;
@Value("${security.user-db.enabled}") private boolean dbAuthEnabled;
@Value("${security.user-db.allowed-roles:speaker,student}") private String[] dbAuthRoles;
@Value("${security.user-db.title:ARSnova}") private String dbAuthTitle;
@Value("${security.user-db.login-dialog-path}") private String dbAuthDialog;
@Value("${security.user-db.image:}") private String dbAuthImage;
@Value("${security.user-db.order}") private int dbAuthOrder;
@Value("${security.ldap.enabled}") private boolean ldapEnabled;
@Value("${security.ldap.allowed-roles:speaker,student}") private String[] ldapRoles;
@Value("${security.ldap.title:LDAP}") private String ldapTitle;
@Value("${security.ldap.login-dialog-path}") private String ldapDialog;
@Value("${security.ldap.image:}") private String ldapImage;
@Value("${security.ldap.order}") private int ldapOrder;
@Value("${security.cas.enabled}") private boolean casEnabled;
@Value("${security.cas.allowed-roles:speaker,student}") private String[] casRoles;
@Value("${security.cas.title:CAS}") private String casTitle;
@Value("${security.cas.image:}") private String casImage;
@Value("${security.cas.order}") private int casOrder;
@Value("${security.facebook.enabled}") private boolean facebookEnabled;
@Value("${security.facebook.allowed-roles:speaker,student}") private String[] facebookRoles;
@Value("${security.facebook.order}") private int facebookOrder;
@Value("${security.google.enabled}") private boolean googleEnabled;
@Value("${security.google.allowed-roles:speaker,student}") private String[] googleRoles;
@Value("${security.google.order}") private int googleOrder;
@Value("${security.twitter.enabled}") private boolean twitterEnabled;
@Value("${security.twitter.allowed-roles:speaker,student}") private String[] twitterRoles;
@Value("${security.twitter.order}") private int twitterOrder;
private AuthenticationProviderProperties.Guest guestProperties;
private AuthenticationProviderProperties.Registered registeredProperties;
private List<AuthenticationProviderProperties.Ldap> ldapProperties;
private List<AuthenticationProviderProperties.Oidc> oidcProperties;
private AuthenticationProviderProperties.Saml samlProperties;
private AuthenticationProviderProperties.Cas casProperties;
private Map<String, AuthenticationProviderProperties.Oauth> oauthProperties;
@Autowired
private ServletContext servletContext;
@Autowired
private AuthenticationProviderProperties providerProperties;
@Autowired(required = false)
private DaoAuthenticationProvider daoProvider;
private OidcClient oidcClient;
@Autowired(required = false)
private TwitterClient twitterClient;
@Autowired(required = false)
private Google2Client google2Client;
private GoogleOidcClient googleOidcClient;
@Autowired(required = false)
private FacebookClient facebookClient;
@Autowired(required = false)
private LdapAuthenticationProvider ldapAuthenticationProvider;
private SAML2Client saml2Client;
@Autowired(required = false)
private CasAuthenticationEntryPoint casEntryPoint;
@Autowired
private IUserService userService;
@Autowired
private UserSessionService userSessionService;
private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
private UserService userService;
private static final Logger logger = LoggerFactory.getLogger(AuthenticationController.class);
public AuthenticationController(final SystemProperties systemProperties,
final AuthenticationProviderProperties authenticationProviderProperties) {
apiPath = systemProperties.getApi().getProxyPath();
guestProperties = authenticationProviderProperties.getGuest();
registeredProperties = authenticationProviderProperties.getRegistered();
ldapProperties = authenticationProviderProperties.getLdap();
oidcProperties = authenticationProviderProperties.getOidc();
samlProperties = authenticationProviderProperties.getSaml();
casProperties = authenticationProviderProperties.getCas();
oauthProperties = authenticationProviderProperties.getOauth();
}
@PostConstruct
private void init() {
if ("".equals(apiPath)) {
if (apiPath == null || "".equals(apiPath)) {
apiPath = servletContext.getContextPath();
}
}
@RequestMapping(value = { "/auth/login", "/doLogin" }, method = { RequestMethod.POST, RequestMethod.GET })
@PostMapping({ "/login", "/doLogin" })
public void doLogin(
@RequestParam("type") final String type,
@RequestParam(value = "user", required = false) String username,
@RequestParam(value = "user", required = false) final String username,
@RequestParam(required = false) final String password,
@RequestParam(value = "role", required = false) final UserSessionService.Role role,
final HttpServletRequest request,
final HttpServletResponse response
) throws IOException {
String addr = request.getRemoteAddr();
if (userService.isBannedFromLogin(addr)) {
final String address = request.getRemoteAddr();
if (userService.isBannedFromLogin(address)) {
response.sendError(429, "Too Many Requests");
return;
}
final UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(username, password);
userSessionService.setRole(role);
if (dbAuthEnabled && "arsnova".equals(type)) {
Authentication authRequest = new UsernamePasswordAuthenticationToken(username, password);
if (registeredProperties.isEnabled() && "arsnova".equals(type)) {
try {
Authentication auth = daoProvider.authenticate(authRequest);
if (auth.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(auth);
request.getSession(true).setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
return;
}
} catch (AuthenticationException e) {
userService.authenticate(authRequest, UserProfile.AuthProvider.ARSNOVA, address);
} catch (final AuthenticationException e) {
logger.info("Database authentication failed.", e);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
userService.increaseFailedLoginCount(addr);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
} else if (ldapEnabled && "ldap".equals(type)) {
if (!"".equals(username) && !"".equals(password)) {
org.springframework.security.core.userdetails.User user =
new org.springframework.security.core.userdetails.User(
username, password, true, true, true, true, this.getAuthorities()
);
Authentication token = new UsernamePasswordAuthenticationToken(user, password, getAuthorities());
try {
Authentication auth = ldapAuthenticationProvider.authenticate(token);
if (auth.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(auth);
request.getSession(true).setAttribute(
HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
return;
}
logger.info("LDAP authentication failed.");
} catch (AuthenticationException e) {
logger.info("LDAP authentication failed.", e);
}
userService.increaseFailedLoginCount(addr);
} else if (ldapProperties.stream().anyMatch(p -> p.isEnabled()) && "ldap".equals(type)) {
try {
userService.authenticate(authRequest, UserProfile.AuthProvider.LDAP, address);
} catch (final AuthenticationException e) {
logger.info("LDAP authentication failed.", e);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
} else if (guestEnabled && "guest".equals(type)) {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_GUEST"));
if (username == null || !username.startsWith("Guest") || username.length() != MAX_USERNAME_LENGTH) {
username = "Guest" + Sha512DigestUtils.shaHex(request.getSession().getId()).substring(0, MAX_GUESTHASH_LENGTH);
} else if (guestProperties.isEnabled() && "guest".equals(type)) {
try {
userService.authenticate(authRequest, UserProfile.AuthProvider.ARSNOVA_GUEST, address);
} catch (final AuthenticationException e) {
logger.debug("Guest authentication failed.", e);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
org.springframework.security.core.userdetails.User user =
new org.springframework.security.core.userdetails.User(
username, "", true, true, true, true, authorities
);
Authentication token = new UsernamePasswordAuthenticationToken(user, null, authorities);
SecurityContextHolder.getContext().setAuthentication(token);
request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
SecurityContextHolder.getContext());
} else {
response.setStatus(HttpStatus.BAD_REQUEST.value());
}
}
@RequestMapping(value = { "/auth/dialog" }, method = RequestMethod.GET)
@GetMapping("/dialog")
@ResponseBody
public View dialog(
@RequestParam("type") final String type,
......@@ -258,14 +201,9 @@ public class LoginController extends AbstractController {
failureUrl = "/";
}
/* Handle proxy
* TODO: It might be better, to support the proposed standard: http://tools.ietf.org/html/rfc7239 */
String host = null != request.getHeader("X-Forwarded-Host")
? request.getHeader("X-Forwarded-Host") : request.getServerName();
int port = null != request.getHeader("X-Forwarded-Port")
? Integer.valueOf(request.getHeader("X-Forwarded-Port")) : request.getServerPort();
String scheme = null != request.getHeader("X-Forwarded-Proto")
? request.getHeader("X-Forwarded-Proto") : request.getScheme();
final String host = request.getServerName();
final int port = request.getServerPort();
final String scheme = request.getScheme();
String serverUrl = scheme + "://" + host;
if ("https".equals(scheme)) {
......@@ -278,23 +216,28 @@ public class LoginController extends AbstractController {
}
}
request.getSession().setAttribute("ars-login-success-url", serverUrl + successUrl);
request.getSession().setAttribute("ars-login-failure-url", serverUrl + failureUrl);
request.getSession().setAttribute(LoginAuthenticationSucessHandler.URL_ATTRIBUTE, serverUrl + successUrl);
request.getSession().setAttribute(LoginAuthenticationFailureHandler.URL_ATTRIBUTE, serverUrl + failureUrl);
if (casEnabled && "cas".equals(type)) {
if (casProperties.isEnabled() && "cas".equals(type)) {
casEntryPoint.commence(request, response, null);
} else if (twitterEnabled && "twitter".equals(type)) {
} else if (saml2Client != null && "saml".equals(type)) {
result = new RedirectView(
saml2Client.getRedirectAction(new J2EContext(request, response)).getLocation());
} else if (oidcProperties.stream().anyMatch(p -> p.isEnabled()) && "oidc".equals(type)) {
result = new RedirectView(
oidcClient.getRedirectAction(new J2EContext(request, response)).getLocation());
} else if (twitterClient != null && "twitter".equals(type)) {
result = new RedirectView(
twitterClient.getRedirectAction(new J2EContext(request, response)).getLocation());
} else if (facebookEnabled && "facebook".equals(type)) {
facebookClient.setFields("id,link");
} else if (facebookClient != null && "facebook".equals(type)) {
facebookClient.setFields("id");
facebookClient.setScope("");
result = new RedirectView(
facebookClient.getRedirectAction(new J2EContext(request, response)).getLocation());
} else if (googleEnabled && "google".equals(type)) {
google2Client.setScope(Google2Client.Google2Scope.EMAIL);
} else if (googleOidcClient != null && "google".equals(type)) {
result = new RedirectView(
google2Client.getRedirectAction(new J2EContext(request, response)).getLocation());
googleOidcClient.getRedirectAction(new J2EContext(request, response)).getLocation());
} else {
response.setStatus(HttpStatus.BAD_REQUEST.value());
}
......@@ -302,160 +245,150 @@ public class LoginController extends AbstractController {
return result;
}
@RequestMapping(value = { "/auth/", "/whoami" }, method = RequestMethod.GET)
@GetMapping({ "/", "/whoami" })
@ResponseBody
public User whoami() {
userSessionService.setUser(userService.getCurrentUser());
return userService.getCurrentUser();
public ClientAuthentication whoami(@AuthenticationPrincipal final User user) {
if (user == null) {
throw new UnauthorizedException();
}
return new ClientAuthentication(user);
}
@RequestMapping(value = { "/auth/logout", "/logout" }, method = { RequestMethod.POST, RequestMethod.GET })
public View doLogout(final HttpServletRequest request) {
@PostMapping("/logout")
public String doLogout(final HttpServletRequest request) {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
userService.removeUserFromMaps(userService.getCurrentUser());
userService.removeUserIdFromMaps(userService.getCurrentUser().getId());
request.getSession().invalidate();
SecurityContextHolder.clearContext();
if (auth instanceof CasAuthenticationToken) {
return new RedirectView(apiPath + "/j_spring_cas_security_logout");
return "redirect:" + apiPath + SecurityConfig.CAS_LOGOUT_PATH;
}
return new RedirectView(request.getHeader("referer") != null ? request.getHeader("referer") : "/");
return "redirect:" + (request.getHeader("referer") != null ? request.getHeader("referer") : "/");
}
@RequestMapping(value = { "/auth/services" }, method = RequestMethod.GET)
@GetMapping("/services")
@ResponseBody
public List<ServiceDescription> getServices(final HttpServletRequest request) {
List<ServiceDescription> services = new ArrayList<>();
final List<ServiceDescription> services = new ArrayList<>();
/* The first parameter is replaced by the backend, the second one by the frondend */
String dialogUrl = apiPath + "/auth/dialog?type={0}&successurl='{0}'";
if (guestEnabled) {
ServiceDescription sdesc = new ServiceDescription(
"guest",
"Guest",
null,
guestRoles
final String dialogUrl = apiPath + "/v2/auth/dialog?type={0}&successurl='{0}'";
if (guestProperties.isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
"guest",
"Guest",
null,
guestProperties.getAllowedRoles()
);
sdesc.setOrder(guestOrder);
sdesc.setOrder(guestProperties.getOrder());
services.add(sdesc);
}
if (customLoginEnabled && !"".equals(customLoginDialog)) {
ServiceDescription sdesc = new ServiceDescription(
"custom",
customLoginTitle,
customizationPath + "/" + customLoginDialog + "?redirect={0}",
customLoginRoles,
customLoginImage
if (registeredProperties.isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
"arsnova",
registeredProperties.getTitle(),
customizationPath + "/login?provider=arsnova&redirect={0}",
registeredProperties.getAllowedRoles()
);
sdesc.setOrder(customLoginOrder);
sdesc.setOrder(registeredProperties.getOrder());
services.add(sdesc);
}
if (dbAuthEnabled && !"".equals(dbAuthDialog)) {
ServiceDescription sdesc = new ServiceDescription(
"arsnova",
dbAuthTitle,
customizationPath + "/" + dbAuthDialog + "?redirect={0}",
dbAuthRoles,
dbAuthImage
if (ldapProperties.get(0).isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
SecurityConfig.LDAP_PROVIDER_ID,
ldapProperties.get(0).getTitle(),
customizationPath + "/login?provider=" + SecurityConfig.LDAP_PROVIDER_ID + "&redirect={0}",
ldapProperties.get(0).getAllowedRoles()
);
sdesc.setOrder(dbAuthOrder);
sdesc.setOrder(ldapProperties.get(0).getOrder());
services.add(sdesc);
}
if (ldapEnabled && !"".equals(ldapDialog)) {
ServiceDescription sdesc = new ServiceDescription(
"ldap",
ldapTitle,
customizationPath + "/" + ldapDialog + "?redirect={0}",
ldapRoles,
ldapImage
if (samlProperties.isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
SecurityConfig.SAML_PROVIDER_ID,
samlProperties.getTitle(),
MessageFormat.format(dialogUrl, SecurityConfig.SAML_PROVIDER_ID),
samlProperties.getAllowedRoles()
);
sdesc.setOrder(ldapOrder);
sdesc.setOrder(samlProperties.getOrder());
services.add(sdesc);
}
if (casEnabled) {
ServiceDescription sdesc = new ServiceDescription(
"cas",
casTitle,
MessageFormat.format(dialogUrl, "cas"),
casRoles
if (casProperties.isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
SecurityConfig.CAS_PROVIDER_ID,
casProperties.getTitle(),
MessageFormat.format(dialogUrl, SecurityConfig.CAS_PROVIDER_ID),
casProperties.getAllowedRoles()
);
sdesc.setOrder(casOrder);
sdesc.setOrder(casProperties.getOrder());
services.add(sdesc);
}
if (facebookEnabled) {
ServiceDescription sdesc = new ServiceDescription(
"facebook",
"Facebook",
MessageFormat.format(dialogUrl, "facebook"),
facebookRoles
if (oidcProperties.get(0).isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
SecurityConfig.OIDC_PROVIDER_ID,
oidcProperties.get(0).getTitle(),
MessageFormat.format(dialogUrl, SecurityConfig.OIDC_PROVIDER_ID),
oidcProperties.get(0).getAllowedRoles()
);
sdesc.setOrder(facebookOrder);
sdesc.setOrder(oidcProperties.get(0).getOrder());
services.add(sdesc);
}
if (googleEnabled) {
ServiceDescription sdesc = new ServiceDescription(
"google",
"Google",
MessageFormat.format(dialogUrl, "google"),
googleRoles
final AuthenticationProviderProperties.Oauth facebookProperties =
oauthProperties.get(SecurityConfig.FACEBOOK_PROVIDER_ID);
if (facebookProperties != null && facebookProperties.isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
SecurityConfig.FACEBOOK_PROVIDER_ID,
"Facebook",
MessageFormat.format(dialogUrl, SecurityConfig.FACEBOOK_PROVIDER_ID),
facebookProperties.getAllowedRoles()
);
sdesc.setOrder(googleOrder);
sdesc.setOrder(facebookProperties.getOrder());
services.add(sdesc);
}
if (twitterEnabled) {
ServiceDescription sdesc = new ServiceDescription(
"twitter",
"Twitter",
MessageFormat.format(dialogUrl, "twitter"),
twitterRoles
final AuthenticationProviderProperties.Oauth googleProperties =
oauthProperties.get(SecurityConfig.GOOGLE_PROVIDER_ID);
if (googleProperties != null && googleProperties.isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
SecurityConfig.GOOGLE_PROVIDER_ID,
"Google",
MessageFormat.format(dialogUrl, SecurityConfig.GOOGLE_PROVIDER_ID),
googleProperties.getAllowedRoles()
);
sdesc.setOrder(twitterOrder);
sdesc.setOrder(googleProperties.getOrder());
services.add(sdesc);
}
final AuthenticationProviderProperties.Oauth twitterProperties =
oauthProperties.get(SecurityConfig.TWITTER_PROVIDER_ID);
if (twitterProperties != null && twitterProperties.isEnabled()) {
final ServiceDescription sdesc = new ServiceDescription(
SecurityConfig.TWITTER_PROVIDER_ID,
"Twitter",
MessageFormat.format(dialogUrl, SecurityConfig.TWITTER_PROVIDER_ID),
twitterProperties.getAllowedRoles()
);
sdesc.setOrder(twitterProperties.getOrder());
services.add(sdesc);
}
return services;
}
private Collection<GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authList = new ArrayList<>();
private Collection<GrantedAuthority> getAuthorities(final boolean admin) {
final List<GrantedAuthority> authList = new ArrayList<>();
authList.add(new SimpleGrantedAuthority("ROLE_USER"));
return authList;
}
@RequestMapping(value = { "/test/me" }, method = RequestMethod.GET)
@ResponseBody
public User me() {
final User me = userSessionService.getUser();
if (me == null) {
throw new UnauthorizedException();
}
return me;
}
@RequestMapping(value = { "/test/mysession" }, method = RequestMethod.GET)
@ResponseBody
public Session mysession() {
final Session mysession = userSessionService.getSession();
if (mysession == null) {
throw new UnauthorizedException();
if (admin) {
authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
return mysession;
}
@RequestMapping(value = { "/test/myrole" }, method = RequestMethod.GET)
@ResponseBody
public UserSessionService.Role myrole() {
final UserSessionService.Role myrole = userSessionService.getRole();
if (myrole == null) {
throw new UnauthorizedException();
}
return myrole;
return authList;
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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.controller.v2;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import de.thm.arsnova.controller.PaginationController;
import de.thm.arsnova.model.Room;
import de.thm.arsnova.model.migration.FromV2Migrator;
import de.thm.arsnova.model.migration.ToV2Migrator;
import de.thm.arsnova.model.migration.v2.Comment;
import de.thm.arsnova.model.migration.v2.CommentReadingCount;
import de.thm.arsnova.service.CommentService;
import de.thm.arsnova.service.RoomService;
import de.thm.arsnova.service.UserService;
import de.thm.arsnova.web.DeprecatedApi;
import de.thm.arsnova.web.Pagination;
/**
* Handles requests related to comments.
*/
@RestController("v2CommentController")
@RequestMapping("/v2/audiencequestion")
@Api(value = "/audiencequestion", description = "Comment (Interposed/Audience Question) API")
public class CommentController extends PaginationController {
@Autowired
private CommentService commentService;
@Autowired
private RoomService roomService;
@Autowired
private UserService userService;
@Autowired
private ToV2Migrator toV2Migrator;
@Autowired
private FromV2Migrator fromV2Migrator;
@ApiOperation(value = "Count all the comments in current room",
nickname = "getCommentCount")
@GetMapping(value = "/count", produces = MediaType.TEXT_PLAIN_VALUE)
@DeprecatedApi
@Deprecated
public String getCommentCount(
@ApiParam(value = "Room-Key from current room", required = true)
@RequestParam("sessionkey")
final String roomShortId) {
return String.valueOf(commentService.count(roomService.getIdByShortId(roomShortId)));
}
@ApiOperation(value = "count all unread comments",
nickname = "getUnreadCommentCount")
@GetMapping("/readcount")
@DeprecatedApi
@Deprecated
public CommentReadingCount getUnreadCommentCount(
@ApiParam(value = "Room-Key from current room", required = true)
@RequestParam("sessionkey") final String roomShortId, final String user) {
return commentService.countRead(roomService.getIdByShortId(roomShortId), user);
}
@ApiOperation(value = "Retrieves all Comments for a Room",
nickname = "getComments")
@GetMapping("/")
@Pagination
public List<Comment> getComments(
@ApiParam(value = "Room-Key from current room", required = true)
@RequestParam("sessionkey")
final String roomShortId) {
return commentService.getByRoomId(roomService.getIdByShortId(roomShortId), offset, limit).stream()
.map(toV2Migrator::migrate).collect(Collectors.toList());
}
@ApiOperation(value = "Retrieves an Comment",
nickname = "getComment")
@GetMapping("/{commentId}")
public Comment getComment(
@ApiParam(value = "ID of the Comment that needs to be deleted", required = true)
@PathVariable
final String commentId)
throws IOException {
return toV2Migrator.migrate(commentService.getAndMarkRead(commentId));
}
@ApiOperation(value = "Creates a new Comment for a Room and returns the Comment's data",
nickname = "postComment")
@ApiResponses(value = {
@ApiResponse(code = 400, message = HTML_STATUS_400)
})
@PostMapping("/")
@ResponseStatus(HttpStatus.CREATED)
public void postComment(
@ApiParam(value = "Room-Key from current room", required = true)
@RequestParam("sessionkey")
final String roomShortId,
@ApiParam(value = "the body from the new comment", required = true)
@RequestBody
final Comment comment) {
final de.thm.arsnova.model.Comment commentV3 = fromV2Migrator.migrate(comment);
final Room roomV3 = roomService.getByShortId(roomShortId);
commentV3.setRoomId(roomV3.getId());
commentService.create(commentV3);
}
@ApiOperation(value = "Deletes a Comment",
nickname = "deleteComment")
@DeleteMapping("/{commentId}")
public void deleteComment(
@ApiParam(value = "ID of the comment that needs to be deleted", required = true)
@PathVariable
final String commentId) {
commentService.delete(commentService.get(commentId));
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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.controller.v2;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import de.thm.arsnova.config.properties.SystemProperties;
import de.thm.arsnova.controller.AbstractController;
/**
* The ConfigurationController provides frontend clients with information necessary to correctly interact with the
* backend and other frontends as well as settings for ARSnova. The the alternative /arsnova-config route is necessary
* in case the backend application is deployed as root context.
*/
@Controller("v2ConfigurationController")
@RequestMapping({"/v2/configuration", "/v2/arsnova-config"})
public class ConfigurationController extends AbstractController {
private static final Logger logger = LoggerFactory
.getLogger(ConfigurationController.class);
private ServletContext servletContext;
private String apiPath;
private String socketioPath;
@Value("${customization.path}")
private String customizationPath;
@Value("${mobile.path}")
private String mobilePath;
@Value("${presenter.path}")
private String presenterPath;
@Value("${ui.links.overlay.url}")
private String overlayUrl;
@Value("${ui.links.organization.url}")
private String organizationUrl;
@Value("${ui.links.imprint.url}")
private String imprintUrl;
@Value("${ui.links.blog.url:}")
private String blogUrl;
@Value("${ui.links.privacy-policy.url}")
private String privacyPolicyUrl;
@Value("${ui.links.documentation.url}")
private String documentationUrl;
@Value("${ui.links.presenter-documentation.url}")
private String presenterDocumentationUrl;
@Value("${ui.feedback.warning:5}")
private String feedbackWarningOffset;
@Value("${ui.mathjax.enabled:true}")
private String mathJaxEnabled;
@Value("${ui.mathjax.src:}")
private String mathJaxSrc;
@Value("${features.contents.formats.freetext.imageanswer.enabled:false}")
private String imageAnswerEnabled;
@Value("${features.contents.formats.grid-square.enabled:false}")
private String gridSquareEnabled;
private String roomImportExportEnabled = "true";
@Value("${features.content-pool.enabled:false}")
private String publicPoolEnabled;
@Value("${features.contents.answer-option-limit:8}")
private String answerOptionLimit;
@Value("${system.uploads.max-filesize:}")
private String maxUploadFilesize;
@Value("${ui.parse-answer-option-formatting:false}")
private String parseAnswerOptionFormatting;
@Value("${features.content-pool.subjects}")
private String ppSubjects;
@Value("${features.content-pool.licenses}")
private String ppLicenses;
@Value("${features.content-pool.logo-max-filesize}")
private String ppLogoMaxFilesize;
@Value("${system.uploads.max-filesize}")
private String gridImageMaxFileSize;
@Value("${ui.tracking.provider}")
private String trackingProvider;
@Value("${ui.tracking.tracker-url}")
private String trackingTrackerUrl;
@Value("${ui.tracking.site-id}")
private String trackingSiteId;
@Value("${ui.demo-room-id:}")
private String demoRoomShortId;
@Value("${ui.slogan:}")
private String arsnovaSlogan;
@Value("${ui.splashscreen.logo-path:}")
private String splashscreenLogo;
@Value("${ui.splashscreen.slogan:}")
private String splashscreenSlogan;
@Value("${ui.splashscreen.slogan-color:}")
private String splashscreenSloganColor;
@Value("${ui.splashscreen.background-color:}")
private String splashscreenBgColor;
@Value("${ui.splashscreen.loading-ind-color:}")
private String splashscreenLoadingIndColor;
@Value("${ui.splashscreen.min-delay:}")
private String splashscreenDelay;
@Value("${features.content-pool.session-levels.de}")
private String ppLevelsDe;
@Value("${features.content-pool.session-levels.en}")
private String ppLevelsEn;
public ConfigurationController(final SystemProperties systemProperties, final ServletContext servletContext) {
apiPath = systemProperties.getApi().getProxyPath();
socketioPath = systemProperties.getSocketio().getProxyPath();
this.servletContext = servletContext;
}
@PostConstruct
private void init() {
if (apiPath == null || "".equals(apiPath)) {
apiPath = servletContext.getContextPath();
}
}
@GetMapping
@ResponseBody
public Map<String, Object> getConfiguration(final HttpServletRequest request) {
final Map<String, Object> config = new HashMap<>();
final Map<String, Boolean> features = new HashMap<>();
final Map<String, String> publicPool = new HashMap<>();
final Map<String, Object> splashscreen = new HashMap<>();
config.put("apiPath", apiPath + "/v2");
if (!"".equals(socketioPath)) {
config.put("socketioPath", socketioPath);
}
if (!"".equals(customizationPath)) {
config.put("customizationPath", customizationPath);
}
if (!"".equals(mobilePath)) {
config.put("mobilePath", mobilePath);
}
if (!"".equals(presenterPath)) {
config.put("presenterPath", presenterPath);
}
if (!"".equals(documentationUrl)) {
config.put("documentationUrl", documentationUrl);
}
if (!"".equals(blogUrl)) {
config.put("blogUrl", blogUrl);
}
if (!"".equals(presenterDocumentationUrl)) {
config.put("presenterDocumentationUrl", presenterDocumentationUrl);
}
if (!"".equals(overlayUrl)) {
config.put("overlayUrl", overlayUrl);
}
if (!"".equals(organizationUrl)) {
config.put("organizationUrl", organizationUrl);
}
if (!"".equals(imprintUrl)) {
config.put("imprintUrl", imprintUrl);
}
if (!"".equals(privacyPolicyUrl)) {
config.put("privacyPolicyUrl", privacyPolicyUrl);
}
if (!"".equals(demoRoomShortId)) {
config.put("demoRoomShortId", demoRoomShortId);
}
if (!"".equals(arsnovaSlogan)) {
config.put("arsnovaSlogan", arsnovaSlogan);
}
if (!"".equals(maxUploadFilesize)) {
config.put("maxUploadFilesize", maxUploadFilesize);
}
if (!"".equals(mathJaxSrc) && "true".equals(mathJaxEnabled)) {
config.put("mathJaxSrc", mathJaxSrc);
}
config.put("answerOptionLimit", Integer.valueOf(answerOptionLimit));
config.put("feedbackWarningOffset", Integer.valueOf(feedbackWarningOffset));
config.put("parseAnswerOptionFormatting", Boolean.valueOf(parseAnswerOptionFormatting));
config.put("features", features);
features.put("mathJax", "true".equals(mathJaxEnabled));
/* Keep the markdown property for now since the frontend still depends on it */
features.put("markdown", true);
features.put("imageAnswer", "true".equals(imageAnswerEnabled));
features.put("gridSquare", "true".equals(gridSquareEnabled));
features.put("sessionImportExport", "true".equals(roomImportExportEnabled));
features.put("publicPool", "true".equals(publicPoolEnabled));
features.put("exportToClick", false);
// 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);
}
config.put("splashscreen", splashscreen);
if (!"".equals(splashscreenLogo)) {
splashscreen.put("logo", splashscreenLogo);
}
if (!"".equals(splashscreenSlogan)) {
splashscreen.put("slogan", splashscreenSlogan);
}
if (!"".equals(splashscreenSloganColor)) {
splashscreen.put("sloganColor", splashscreenSloganColor);
}
if (!"".equals(splashscreenBgColor)) {
splashscreen.put("bgcolor", splashscreenBgColor);
}
if (!"".equals(splashscreenLoadingIndColor)) {
splashscreen.put("loadIndColor", splashscreenLoadingIndColor);
}
if (!"".equals(splashscreenDelay)) {
splashscreen.put("minDelay", Integer.valueOf(splashscreenDelay));
}
if (!"".equals(trackingTrackerUrl)) {
final Map<String, String> tracking = new HashMap<>();
config.put("tracking", tracking);
tracking.put("provider", trackingProvider);
tracking.put("trackerUrl", trackingTrackerUrl);
tracking.put("siteId", trackingSiteId);
}
config.put("grid", gridImageMaxFileSize);
return config;
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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.controller.v2;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import javax.naming.OperationNotSupportedException;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import de.thm.arsnova.controller.PaginationController;
import de.thm.arsnova.model.ChoiceAnswer;
import de.thm.arsnova.model.ChoiceQuestionContent;
import de.thm.arsnova.model.ContentGroup;
import de.thm.arsnova.model.GridImageContent;
import de.thm.arsnova.model.TextAnswer;
import de.thm.arsnova.model.migration.FromV2Migrator;
import de.thm.arsnova.model.migration.ToV2Migrator;
import de.thm.arsnova.model.migration.v2.Answer;
import de.thm.arsnova.model.migration.v2.Content;
import de.thm.arsnova.service.AnswerService;
import de.thm.arsnova.service.ContentGroupService;
import de.thm.arsnova.service.ContentService;
import de.thm.arsnova.service.RoomService;
import de.thm.arsnova.service.TimerService;
import de.thm.arsnova.util.PaginationListDecorator;
import de.thm.arsnova.web.DeprecatedApi;
import de.thm.arsnova.web.Pagination;
import de.thm.arsnova.web.exceptions.BadRequestException;
import de.thm.arsnova.web.exceptions.ForbiddenException;
import de.thm.arsnova.web.exceptions.NoContentException;
import de.thm.arsnova.web.exceptions.NotFoundException;
import de.thm.arsnova.web.exceptions.NotImplementedException;
/**
* Handles requests related to contents.
*/
@RestController("v2ContentController")
@RequestMapping("/v2/lecturerquestion")
@Api(value = "/lecturerquestion", description = "Content (Skill/Lecturer Question) API")
public class ContentController extends PaginationController {
@Autowired
private ContentService contentService;
@Autowired
private ContentGroupService contentGroupService;
@Autowired
private AnswerService answerService;
@Autowired
private RoomService roomService;
@Autowired
private TimerService timerService;
@Autowired
private ToV2Migrator toV2Migrator;
@Autowired
private FromV2Migrator fromV2Migrator;
@ApiOperation(value = "Get content with provided content Id",
nickname = "getContent")
@ApiResponses(value = {
@ApiResponse(code = 404, message = HTML_STATUS_404)
})
@GetMapping("/{contentId}")
public Content getContent(@PathVariable final String contentId) {
final de.thm.arsnova.model.Content content = contentService.get(contentId);
if (content != null) {
final Optional<ContentGroup> contentGroup = contentGroupService.getByRoomId(content.getRoomId()).stream()
.filter(cg -> cg.getContentIds().contains(contentId)).findFirst();
final Content contentV2 = toV2Migrator.migrate(content);
contentGroup.ifPresent(cg -> contentV2.setQuestionVariant(cg.getName()));
return contentV2;
}
throw new NotFoundException();
}
@ApiOperation(value = "Post provided content",
nickname = "postContent")
@ApiResponses(value = {
@ApiResponse(code = 400, message = HTML_STATUS_400)
})
@PostMapping("/")
@ResponseStatus(HttpStatus.CREATED)
public Content postContent(@RequestBody final Content content) {
final de.thm.arsnova.model.Content contentV3 = fromV2Migrator.migrate(content);
final String roomId = roomService.getIdByShortId(content.getSessionKeyword());
contentV3.setRoomId(roomId);
contentService.create(contentV3);
contentGroupService.addContentToGroup(roomId, content.getQuestionVariant(), contentV3.getId());
return toV2Migrator.migrate(contentV3);
}
@ApiOperation(value = "Post provided contents", nickname = "bulkPostContents")
@ApiResponses(value = {
@ApiResponse(code = 400, message = HTML_STATUS_400)
})
@PostMapping("/bulk")
@ResponseStatus(HttpStatus.CREATED)
public List<Content> bulkPostContents(@RequestBody final List<Content> contents) {
final List<de.thm.arsnova.model.Content> contentsV3 =
contents.stream().map(c -> contentService.create(fromV2Migrator.migrate(c))).collect(Collectors.toList());
return contentsV3.stream().map(toV2Migrator::migrate).collect(Collectors.toList());
}
@ApiOperation(value = "Update the content, identified by provided id, with the provided content in the Request Body",
nickname = "updateContent")
@ApiResponses(value = {
@ApiResponse(code = 400, message = HTML_STATUS_400)
})
@PutMapping("/{contentId}")
public Content updateContent(
@PathVariable final String contentId,
@RequestBody final Content content) {
return toV2Migrator.migrate(contentService.update(fromV2Migrator.migrate(content)));
}
@ApiOperation(value = "Start new Pi Round on content, identified by provided id, with an optional time",
nickname = "startPiRound")
@GetMapping("/{contentId}/questionimage")
public String getContentImage(
@PathVariable final String contentId,
@RequestParam(value = "fcImage", defaultValue = "false", required = false) final boolean fcImage) {
throw new NotImplementedException();
}
@PostMapping("/{contentId}/startnewpiround")
public void startPiRound(
@PathVariable final String contentId,
@RequestParam(value = "time", defaultValue = "0", required = false) final int time) {
if (time == 0) {
timerService.startNewRound(contentId);
} else {
timerService.startNewRoundDelayed(contentId, time);
}
}
@PostMapping("/{contentId}/canceldelayedpiround")
@ApiOperation(value = "Cancel Pi Round on content, identified by provided id",
nickname = "cancelPiRound")
public void cancelPiRound(
@PathVariable final String contentId) {
timerService.cancelRoundChange(contentId);
}
@PostMapping("/{contentId}/resetpiroundstate")
@ApiOperation(value = "Reset Pi Round on content, identified by provided id",
nickname = "resetPiContent")
public void resetPiContent(
@PathVariable final String contentId) {
timerService.resetRoundState(contentId);
}
@ApiOperation(value = "Set voting admission on content, identified by provided id",
nickname = "setVotingAdmission")
@PostMapping("/{contentId}/disablevote")
public void setVotingAdmission(
@PathVariable final String contentId,
@RequestParam(value = "disable", defaultValue = "false", required = false) final Boolean disableVote) {
boolean disable = false;
if (disableVote != null) {
disable = disableVote;
}
contentService.setVotingAdmission(contentId, disable);
}
@ApiOperation(value = "Set voting admission for all contents",
nickname = "setVotingAdmissionForAllContents")
@PostMapping("/disablevote")
public void setVotingAdmissionForAllContents(
@RequestParam(value = "sessionkey")
final String roomShortId,
@RequestParam(value = "disable", defaultValue = "false", required = false)
final Boolean disableVote,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false", required = false) final
boolean lectureContentsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) final
boolean preparationContentsOnly) {
final String roomId = roomService.getIdByShortId(roomShortId);
boolean disable = false;
final Iterable<de.thm.arsnova.model.Content> contents;
if (disableVote != null) {
disable = disableVote;
}
if (lectureContentsOnly) {
contents = contentService.getByRoomIdAndGroup(roomId, "lecture");
contentService.setVotingAdmissions(roomId, disable, contents);
} else if (preparationContentsOnly) {
contents = contentService.getByRoomIdAndGroup(roomId, "preparation");
contentService.setVotingAdmissions(roomId, disable, contents);
} else {
contents = contentService.getByRoomId(roomId);
contentService.setVotingAdmissions(roomId, disable, contents);
}
}
@ApiOperation(value = "Publish a content, identified by provided id and content in Request Body.",
nickname = "publishContent")
@PostMapping("/{contentId}/publish")
public void publishContent(
@PathVariable final String contentId,
@RequestParam(defaultValue = "true", required = false) final boolean publish,
@RequestBody final Content content
) throws IOException {
final de.thm.arsnova.model.Content contentV3 = fromV2Migrator.migrate(content);
boolean p = publish;
if (content != null) {
p = contentV3.getState().isVisible();
}
contentService.patch(contentV3, Collections.singletonMap("visible", p), de.thm.arsnova.model.Content::getState);
}
@ApiOperation(value = "Publish all contents",
nickname = "publishAllContents")
@PostMapping("/publish")
public void publishAllContents(
@RequestParam(value = "sessionkey")
final String roomShortId,
@RequestParam(required = false)
final Boolean publish,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false", required = false) final
boolean lectureContentsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false", required = false) final
boolean preparationContentsOnly
) throws IOException {
final String roomId = roomService.getIdByShortId(roomShortId);
final boolean p = publish == null || publish;
final Iterable<de.thm.arsnova.model.Content> contents;
if (lectureContentsOnly) {
contents = contentService.getByRoomIdAndGroup(roomId, "lecture");
contentService.publishContents(roomId, p, contents);
} else if (preparationContentsOnly) {
contents = contentService.getByRoomIdAndGroup(roomId, "preparation");
contentService.publishContents(roomId, p, contents);
} else {
contentService.publishAll(roomId, p);
}
}
@ApiOperation(value = "Publish statistics from content with provided id",
nickname = "publishStatistics")
@PostMapping("/{contentId}/publishstatistics")
public void publishStatistics(
@PathVariable final String contentId,
@RequestParam(defaultValue = "true", required = false) final Boolean showStatistics,
@RequestBody final Content content
) throws IOException {
final de.thm.arsnova.model.Content contentV3 = fromV2Migrator.migrate(content);
boolean p = showStatistics;
if (content != null) {
p = contentV3.getState().isResponsesVisible();
}
contentService.patch(contentV3, Collections.singletonMap("responsesVisible", p),
de.thm.arsnova.model.Content::getState);
}
@ApiOperation(value = "Publish correct answer from content with provided id",
nickname = "publishCorrectAnswer")
@PostMapping("/{contentId}/publishcorrectanswer")
public void publishCorrectAnswer(
@PathVariable final String contentId,
@RequestParam(defaultValue = "true", required = false) final boolean showCorrectAnswer,
@RequestBody final Content content
) throws IOException {
final de.thm.arsnova.model.Content contentV3 = fromV2Migrator.migrate(content);
boolean p = showCorrectAnswer;
if (content != null) {
p = contentV3.getState().isAdditionalTextVisible();
}
contentService.patch(contentV3, Collections.singletonMap("solutionVisible", p),
de.thm.arsnova.model.Content::getState);
}
@ApiOperation(value = "Get contents",
nickname = "getContents")
@GetMapping("/")
@Pagination
public List<Content> getContents(
@RequestParam(value = "sessionkey") final String roomShortId,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureContentsOnly,
@RequestParam(value = "flashcardsonly", defaultValue = "false") final boolean flashcardsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationContentsOnly,
@RequestParam(value = "requestImageData", defaultValue = "false") final boolean requestImageData,
final HttpServletResponse response) {
final String roomId = roomService.getIdByShortId(roomShortId);
final Iterable<de.thm.arsnova.model.Content> contents;
if (lectureContentsOnly) {
contents = contentService.getByRoomIdAndGroup(roomId, "lecture");
} else if (flashcardsOnly) {
contents = contentService.getByRoomIdAndGroup(roomId, "flashcard");
} else if (preparationContentsOnly) {
contents = contentService.getByRoomIdAndGroup(roomId, "preparation");
} else {
contents = contentService.getByRoomId(roomId);
}
if (contents == null || !contents.iterator().hasNext()) {
response.setStatus(HttpStatus.NO_CONTENT.value());
return null;
}
return new PaginationListDecorator<>(StreamSupport.stream(contents.spliterator(), false)
.map(toV2Migrator::migrate).collect(Collectors.toList()), offset, limit);
}
@ApiOperation(value = "Delete contents",
nickname = "deleteContents")
@DeleteMapping("/")
public void deleteContents(
@RequestParam(value = "sessionkey")
final String roomShortId,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false")
final boolean lectureContentsOnly,
@RequestParam(value = "flashcardsonly", defaultValue = "false")
final boolean flashcardsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false")
final boolean preparationContentsOnly,
final HttpServletResponse response) {
final String roomId = roomService.getIdByShortId(roomShortId);
if (lectureContentsOnly) {
contentService.deleteLectureContents(roomId);
} else if (preparationContentsOnly) {
contentService.deletePreparationContents(roomId);
} else if (flashcardsOnly) {
contentService.deleteFlashcards(roomId);
} else {
contentService.deleteAllContents(roomId);
}
}
@ApiOperation(value = "Get the amount of contents by the room-key",
nickname = "getContentCount")
@DeprecatedApi
@Deprecated
@GetMapping(value = "/count", produces = MediaType.TEXT_PLAIN_VALUE)
public String getContentCount(
@RequestParam(value = "sessionkey") final String roomShortId,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false") final boolean lectureContentsOnly,
@RequestParam(value = "flashcardsonly", defaultValue = "false") final boolean flashcardsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false") final boolean preparationContentsOnly) {
final String roomId = roomService.getIdByShortId(roomShortId);
final int count;
if (lectureContentsOnly) {
count = contentService.countByRoomIdAndGroup(roomId, "lecture");
} else if (preparationContentsOnly) {
count = contentService.countByRoomIdAndGroup(roomId, "preparation");
} else if (flashcardsOnly) {
count = contentService.countByRoomIdAndGroup(roomId, "flashcard");
} else {
count = contentService.countByRoomId(roomId);
}
return String.valueOf(count);
}
@ApiOperation(value = "Delete answers and content",
nickname = "deleteAnswersAndContent")
@DeleteMapping("/{contentId}")
public void deleteAnswersAndContent(
@PathVariable final String contentId) {
contentService.delete(contentId);
}
@ApiOperation(value = "Get unanswered content IDs by provided room short ID",
nickname = "getUnAnsweredContentIds")
@DeprecatedApi
@Deprecated
@GetMapping("/unanswered")
public List<String> getUnAnsweredContentIds(
@RequestParam(value = "sessionkey")
final String roomShortId,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false")
final boolean lectureContentsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false")
final boolean preparationContentsOnly) {
final String roomId = roomService.getIdByShortId(roomShortId);
final List<String> answers;
if (lectureContentsOnly) {
answers = contentService.getUnAnsweredLectureContentIds(roomId);
} else if (preparationContentsOnly) {
answers = contentService.getUnAnsweredPreparationContentIds(roomId);
} else {
answers = contentService.getUnAnsweredContentIds(roomId);
}
if (answers == null || answers.isEmpty()) {
throw new NoContentException();
}
return answers;
}
/**
* Returns a JSON document which represents the given answer of a content.
*
* @param contentId
* CouchDB Content ID for which the given answer should be
* retrieved
* @return JSON Document of {@link Answer} or {@link NotFoundException}
* @throws NotFoundException
* if wrong room, wrong content or no answer was given by
* the current user
* @throws ForbiddenException
* if not logged in
*/
@ApiOperation(value = "Get my answer for a content, identified by provided content ID",
nickname = "getMyAnswer")
@DeprecatedApi
@Deprecated
@GetMapping("/{contentId}/myanswer")
public Answer getMyAnswer(
@PathVariable final String contentId,
final HttpServletResponse response) {
final de.thm.arsnova.model.Content content = contentService.get(contentId);
final de.thm.arsnova.model.Answer answer = answerService.getMyAnswer(contentId);
if (answer == null) {
response.setStatus(HttpStatus.NO_CONTENT.value());
return null;
}
if (content.getFormat().equals(de.thm.arsnova.model.Content.Format.TEXT)) {
return toV2Migrator.migrate((TextAnswer) answer);
} else {
return toV2Migrator.migrate((ChoiceAnswer) answer, content);
}
}
/**
* Returns a list of {@link Answer}s encoded as a JSON document for a given
* content id. In this case only {@link Answer} <tt>contentId</tt>,
* <tt>answerText</tt>, <tt>answerSubject</tt> and <tt>answerCount</tt>
* properties are set.
*
* @param contentId
* CouchDB Content ID for which the given answers should be
* retrieved
* @throws NotFoundException
* if wrong room, wrong content or no answers was given
* @throws ForbiddenException
* if not logged in
*/
@ApiOperation(value = "Get answers for a content, identified by provided content ID",
nickname = "getAnswers")
@GetMapping("/{contentId}/answer/")
public List<Answer> getAnswers(
@PathVariable final String contentId,
@RequestParam(value = "piround", required = false) final Integer piRound,
@RequestParam(value = "all", required = false, defaultValue = "false") final Boolean allAnswers,
final HttpServletResponse response) {
final de.thm.arsnova.model.Content content = contentService.get(contentId);
if (content instanceof ChoiceQuestionContent || content instanceof GridImageContent) {
return toV2Migrator.migrate(answerService.getAllStatistics(contentId),
content, content.getState().getRound());
} else {
final List<de.thm.arsnova.model.TextAnswer> answers;
if (allAnswers) {
answers = answerService.getAllTextAnswers(contentId, -1, -1);
} else if (null == piRound) {
answers = answerService.getTextAnswers(contentId, offset, limit);
} else {
if (piRound < 1 || piRound > 2) {
response.setStatus(HttpStatus.BAD_REQUEST.value());
return null;
}
answers = answerService.getTextAnswers(contentId, piRound, offset, limit);
}
if (answers == null) {
return new ArrayList<>();
}
return answers.stream().map(toV2Migrator::migrate).collect(Collectors.toList());
}
}
@ApiOperation(value = "Save answer, provided in the Request Body, for a content, identified by provided content ID",
nickname = "saveAnswer")
@PostMapping("/{contentId}/answer/")
public Answer saveAnswer(
@PathVariable final String contentId,
@RequestBody final Answer answer,
final HttpServletResponse response) {
final de.thm.arsnova.model.Content content = contentService.get(contentId);
final de.thm.arsnova.model.Answer answerV3 = fromV2Migrator.migrate(answer, content);
if (answerV3.getContentId() == null) {
answerV3.setContentId(contentId);
}
if (!contentId.equals(answerV3.getContentId())) {
throw new BadRequestException("Mismatching content IDs.");
}
if (answerV3 instanceof TextAnswer) {
return toV2Migrator.migrate((TextAnswer) answerService.create(answerV3));
} else {
return toV2Migrator.migrate((ChoiceAnswer) answerService.create(answerV3), content);
}
}
@ApiOperation(value = "Update answer, provided in Request Body, identified by content ID and answer ID",
nickname = "updateAnswer")
@PutMapping("/{contentId}/answer/{answerId}")
public Answer updateAnswer(
@PathVariable final String contentId,
@PathVariable final String answerId,
@RequestBody final Answer answer,
final HttpServletResponse response) {
final de.thm.arsnova.model.Content content = contentService.get(contentId);
final de.thm.arsnova.model.Answer answerV3 = fromV2Migrator.migrate(answer, content);
if (answerV3 instanceof TextAnswer) {
return toV2Migrator.migrate((TextAnswer) answerService.update(answerV3));
} else {
return toV2Migrator.migrate((ChoiceAnswer) answerService.update(answerV3), content);
}
}
@ApiOperation(value = "Get Image, identified by content ID and answer ID",
nickname = "getImage")
@GetMapping("/{contentId}/answer/{answerId}/image")
public String getImage(
@PathVariable final String contentId,
@PathVariable final String answerId,
final HttpServletResponse response) {
throw new NotImplementedException();
}
@ApiOperation(value = "Delete answer, identified by content ID and answer ID",
nickname = "deleteAnswer")
@DeleteMapping("/{contentId}/answer/{answerId}")
public void deleteAnswer(
@PathVariable final String contentId,
@PathVariable final String answerId,
final HttpServletResponse response) {
answerService.delete(answerService.get(answerId));
}
@ApiOperation(value = "Delete answers from a content, identified by content ID",
nickname = "deleteAnswers")
@DeleteMapping("/{contentId}/answer/")
public void deleteAnswers(
@PathVariable final String contentId,
final HttpServletResponse response) {
answerService.deleteAnswers(contentId);
}
@ApiOperation(value = "Delete all answers and contents from a room, identified by room short ID",
nickname = "deleteAllContentsAnswers")
@DeleteMapping("/answers")
public void deleteAllContentsAnswers(
@RequestParam(value = "sessionkey")
final String roomShortId,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false")
final boolean lectureContentsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false")
final boolean preparationContentsOnly,
final HttpServletResponse response) {
final String roomId = roomService.getIdByShortId(roomShortId);
if (lectureContentsOnly) {
contentService.deleteAllLectureAnswers(roomId);
} else if (preparationContentsOnly) {
contentService.deleteAllPreparationAnswers(roomId);
} else {
contentService.deleteAllContentsAnswers(roomId);
}
}
/**
* Returns the count of answers for given content ID.
*
* @param contentId
* Content ID for which the given answers should be
* retrieved
* @throws NotFoundException
* if wrong room or wrong content
* @throws ForbiddenException
* if not logged in
*/
@ApiOperation(value = "Get the amount of answers for a content, identified by content ID",
nickname = "getAnswerCount")
@DeprecatedApi
@Deprecated
@GetMapping(value = "/{contentId}/answercount", produces = MediaType.TEXT_PLAIN_VALUE)
public String getAnswerCount(@PathVariable final String contentId) {
return String.valueOf(answerService.countAnswersByContentIdAndRound(contentId));
}
@ApiOperation(value = "Get the amount of answers for a content, identified by the content ID",
nickname = "getAllAnswerCount")
@GetMapping("/{contentId}/allroundanswercount")
public List<Integer> getAllAnswerCount(@PathVariable final String contentId) {
return Arrays.asList(
answerService.countAnswersByContentIdAndRound(contentId, 1),
answerService.countAnswersByContentIdAndRound(contentId, 2)
);
}
@ApiOperation(value = "Get the total amount of answers by a content, identified by the content ID",
nickname = "getTotalAnswerCountByContent")
@GetMapping(value = "/{contentId}/totalanswercount", produces = MediaType.TEXT_PLAIN_VALUE)
public String getTotalAnswerCountByContent(@PathVariable final String contentId) {
return String.valueOf(answerService.countTotalAnswersByContentId(contentId));
}
@ApiOperation(value = "Get the amount of answers and abstention answers by a content, identified by the content ID",
nickname = "getAnswerAndAbstentionCount")
@GetMapping("/{contentId}/answerandabstentioncount")
public List<Integer> getAnswerAndAbstentionCount(@PathVariable final String contentId) {
return Arrays.asList(
answerService.countAnswersByContentIdAndRound(contentId),
answerService.countTotalAbstentionsByContentId(contentId)
);
}
@ApiOperation(value = "Get all Freetext answers by a content, identified by the content ID",
nickname = "getFreetextAnswers")
@GetMapping("/{contentId}/freetextanswer/")
@Pagination
public List<Answer> getFreetextAnswers(@PathVariable final String contentId) {
return answerService.getTextAnswersByContentId(contentId, offset, limit).stream()
.map(toV2Migrator::migrate).collect(Collectors.toList());
}
@ApiOperation(value = "Get my answers of an room, identified by the room short ID",
nickname = "getMyAnswers")
@DeprecatedApi
@Deprecated
@GetMapping("/myanswers")
public List<Answer> getMyAnswers(@RequestParam(value = "sessionkey") final String roomShortId)
throws OperationNotSupportedException {
return answerService.getMyAnswersByRoomId(roomService.getIdByShortId(roomShortId)).stream()
.map(a -> {
if (a instanceof ChoiceAnswer) {
return toV2Migrator.migrate(
(ChoiceAnswer) a, contentService.get(a.getContentId()));
} else {
return toV2Migrator.migrate((TextAnswer) a);
}
}).collect(Collectors.toList());
}
@ApiOperation(value = "Get the total amount of answers of a room, identified by the room short ID",
nickname = "getTotalAnswerCount")
@DeprecatedApi
@Deprecated
@GetMapping(value = "/answercount", produces = MediaType.TEXT_PLAIN_VALUE)
public String getTotalAnswerCount(
@RequestParam(value = "sessionkey")
final String roomShortId,
@RequestParam(value = "lecturequestionsonly", defaultValue = "false")
final boolean lectureContentsOnly,
@RequestParam(value = "preparationquestionsonly", defaultValue = "false")
final boolean preparationContentsOnly) {
final String roomId = roomService.getIdByShortId(roomShortId);
int count = 0;
if (lectureContentsOnly) {
count = answerService.countLectureContentAnswers(roomId);
} else if (preparationContentsOnly) {
count = answerService.countPreparationContentAnswers(roomId);
} else {
count = answerService.countTotalAnswersByRoomId(roomId);
}
return String.valueOf(count);
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,44 +15,44 @@
* 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.controller;
import de.thm.arsnova.connector.client.ConnectorClient;
import de.thm.arsnova.connector.model.Course;
import de.thm.arsnova.connector.model.UserRole;
import de.thm.arsnova.entities.User;
import de.thm.arsnova.exceptions.NotImplementedException;
import de.thm.arsnova.exceptions.UnauthorizedException;
import de.thm.arsnova.services.IUserService;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
package de.thm.arsnova.controller.v2;
import io.swagger.annotations.ApiParam;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import de.thm.arsnova.connector.client.ConnectorClient;
import de.thm.arsnova.connector.model.Course;
import de.thm.arsnova.connector.model.UserRole;
import de.thm.arsnova.controller.AbstractController;
import de.thm.arsnova.security.User;
import de.thm.arsnova.service.UserService;
import de.thm.arsnova.web.exceptions.NotImplementedException;
import de.thm.arsnova.web.exceptions.UnauthorizedException;
/**
* Provides access to a user's courses in an LMS such as Moodle.
*/
@RestController
@RestController("v2CourseController")
public class CourseController extends AbstractController {
@Autowired(required = false)
private ConnectorClient connectorClient;
@Autowired
private IUserService userService;
private UserService userService;
@RequestMapping(value = "/mycourses", method = RequestMethod.GET)
@GetMapping("/v2/mycourses")
public List<Course> myCourses(
@ApiParam(value = "sort my courses by name", required = true)
@RequestParam(value = "sortby", defaultValue = "name") final String sortby
) {
@RequestParam(value = "sortby", defaultValue = "name") final String sortby) {
final User currentUser = userService.getCurrentUser();
......@@ -69,8 +69,7 @@ public class CourseController extends AbstractController {
for (final Course course : connectorClient.getCourses(currentUser.getUsername()).getCourse()) {
if (
course.getMembership().isMember()
&& course.getMembership().getUserrole().equals(UserRole.TEACHER)
) {
&& course.getMembership().getUserrole().equals(UserRole.TEACHER)) {
result.add(course);
}
}
......
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,87 +15,99 @@
* 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.controller;
import de.thm.arsnova.entities.Feedback;
import de.thm.arsnova.entities.User;
import de.thm.arsnova.exceptions.NotFoundException;
import de.thm.arsnova.services.IFeedbackService;
import de.thm.arsnova.services.IUserService;
import de.thm.arsnova.web.DeprecatedApi;
package de.thm.arsnova.controller.v2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import de.thm.arsnova.controller.AbstractController;
import de.thm.arsnova.model.Feedback;
import de.thm.arsnova.security.User;
import de.thm.arsnova.service.FeedbackService;
import de.thm.arsnova.service.RoomService;
import de.thm.arsnova.service.UserService;
import de.thm.arsnova.web.DeprecatedApi;
import de.thm.arsnova.web.exceptions.NotFoundException;
import de.thm.arsnova.websocket.ArsnovaSocketioServerImpl;
/**
* Handles requests concerning the user's feedback, i.e., "too fast" or "faster, please". This HTTP API is
* deprecated in favor of the socket implementation.
*
* @see de.thm.arsnova.socket.ARSnovaSocketIOServer
* @see ArsnovaSocketioServerImpl
*/
@RestController
@RestController("v2FeedbackController")
@RequestMapping("/v2/session/{shortId}")
public class FeedbackController extends AbstractController {
@Autowired
private IFeedbackService feedbackService;
private FeedbackService feedbackService;
@Autowired
private RoomService roomService;
@Autowired
private IUserService userService;
private UserService userService;
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/session/{sessionkey}/feedback", method = RequestMethod.GET)
public Feedback getFeedback(@PathVariable final String sessionkey) {
return feedbackService.getFeedback(sessionkey);
@GetMapping("/feedback")
public Feedback getFeedback(@PathVariable final String shortId) {
return feedbackService.getByRoomId(roomService.getIdByShortId(shortId));
}
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/session/{sessionkey}/myfeedback", method = RequestMethod.GET)
public Integer getMyFeedback(@PathVariable final String sessionkey) {
Integer value = feedbackService.getMyFeedback(sessionkey, userService.getCurrentUser());
@GetMapping(value = "/myfeedback", produces = MediaType.TEXT_PLAIN_VALUE)
public String getMyFeedback(@PathVariable final String shortId) {
final String roomId = roomService.getIdByShortId(shortId);
final Integer value = feedbackService.getByRoomIdAndUserId(roomId, userService.getCurrentUser().getId());
if (value != null && value >= Feedback.MIN_FEEDBACK_TYPE && value <= Feedback.MAX_FEEDBACK_TYPE) {
return value;
return value.toString();
}
throw new NotFoundException();
}
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/session/{sessionkey}/feedbackcount", method = RequestMethod.GET)
public int getFeedbackCount(@PathVariable final String sessionkey) {
return feedbackService.getFeedbackCount(sessionkey);
@GetMapping(value = "/feedbackcount", produces = MediaType.TEXT_PLAIN_VALUE)
public String getFeedbackCount(@PathVariable final String shortId) {
return String.valueOf(feedbackService.countFeedbackByRoomId(roomService.getIdByShortId(shortId)));
}
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/session/{sessionkey}/roundedaveragefeedback", method = RequestMethod.GET)
public long getAverageFeedbackRounded(@PathVariable final String sessionkey) {
return feedbackService.getAverageFeedbackRounded(sessionkey);
@GetMapping(value = "/roundedaveragefeedback", produces = MediaType.TEXT_PLAIN_VALUE)
public String getAverageFeedbackRounded(@PathVariable final String shortId) {
return String.valueOf(feedbackService.calculateRoundedAverageFeedback(roomService.getIdByShortId(shortId)));
}
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/session/{sessionkey}/averagefeedback", method = RequestMethod.GET)
public double getAverageFeedback(@PathVariable final String sessionkey) {
return feedbackService.getAverageFeedback(sessionkey);
@GetMapping(value = "/averagefeedback", produces = MediaType.TEXT_PLAIN_VALUE)
public String getAverageFeedback(@PathVariable final String shortId) {
return String.valueOf(feedbackService.calculateAverageFeedback(roomService.getIdByShortId(shortId)));
}
@DeprecatedApi
@Deprecated
@RequestMapping(value = "/session/{sessionkey}/feedback", method = RequestMethod.POST)
@PostMapping("/feedback")
@ResponseStatus(HttpStatus.CREATED)
public Feedback postFeedback(
@PathVariable final String sessionkey,
@RequestBody final int value
) {
User user = userService.getCurrentUser();
feedbackService.saveFeedback(sessionkey, value, user);
Feedback feedback = feedbackService.getFeedback(sessionkey);
@PathVariable final String shortId,
@RequestBody final int value) {
final String roomId = roomService.getIdByShortId(shortId);
final User user = userService.getCurrentUser();
feedbackService.save(roomId, value, user.getId());
final Feedback feedback = feedbackService.getByRoomId(roomId);
return feedback;
}
......
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,140 +15,158 @@
* 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.controller;
import de.thm.arsnova.services.IQuestionService;
import de.thm.arsnova.web.DeprecatedApi;
package de.thm.arsnova.controller.v2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import de.thm.arsnova.controller.AbstractController;
import de.thm.arsnova.service.CommentService;
import de.thm.arsnova.service.ContentService;
import de.thm.arsnova.web.DeprecatedApi;
/**
* This controller forwards requests from deprecated URLs to their new controller, where the requests are handled.
*/
@Controller
@Controller("v2LegacyController")
@RequestMapping("/v2")
public class LegacyController extends AbstractController {
@Autowired
private IQuestionService questionService;
private ContentService contentService;
@Autowired
private CommentService commentService;
/* specific routes */
@DeprecatedApi
@RequestMapping(value = "/session/mysessions", method = RequestMethod.GET)
@GetMapping("/session/mysessions")
public String redirectSessionMy() {
return "forward:/session/?ownedonly=true";
return "forward:/v2/session/?ownedonly=true";
}
@DeprecatedApi
@RequestMapping(value = "/session/visitedsessions", method = RequestMethod.GET)
@GetMapping("/session/visitedsessions")
public String redirectSessionVisited() {
return "forward:/session/?visitedonly=true";
return "forward:/v2/session/?visitedonly=true";
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/question")
public String redirectQuestionByLecturer(@PathVariable final String sessionKey) {
return String.format("forward:/lecturerquestion/?sessionkey=%s", sessionKey);
@RequestMapping(value = "/session/{shortId}/question")
public String redirectQuestionByLecturer(@PathVariable final String shortId) {
return String.format("forward:/v2/lecturerquestion/?sessionkey=%s", shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/skillquestions", method = RequestMethod.GET)
public String redirectQuestionByLecturerList(@PathVariable final String sessionKey) {
return String.format("forward:/lecturerquestion/?sessionkey=%s", sessionKey);
@GetMapping("/session/{shortId}/skillquestions")
public String redirectQuestionByLecturerList(@PathVariable final String shortId) {
return String.format("forward:/v2/lecturerquestion/?sessionkey=%s", shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/skillquestioncount", method = RequestMethod.GET)
public String redirectQuestionByLecturerCount(@PathVariable final String sessionKey) {
return String.format("forward:/lecturerquestion/count?sessionkey=%s", sessionKey);
@GetMapping("/session/{shortId}/skillquestioncount")
public String redirectQuestionByLecturerCount(@PathVariable final String shortId) {
return String.format("forward:/v2/lecturerquestion/count?sessionkey=%s", shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/answercount", method = RequestMethod.GET)
public String redirectQuestionByLecturerAnswerCount(@PathVariable final String sessionKey) {
return String.format("forward:/lecturerquestion/answercount?sessionkey=%s", sessionKey);
@GetMapping("/session/{shortId}/answercount")
public String redirectQuestionByLecturerAnswerCount(@PathVariable final String shortId) {
return String.format("forward:/v2/lecturerquestion/answercount?sessionkey=%s", shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/unanswered", method = RequestMethod.GET)
public String redirectQuestionByLecturerUnnsweredCount(@PathVariable final String sessionKey) {
return String.format("forward:/lecturerquestion/answercount?sessionkey=%s", sessionKey);
@GetMapping("/session/{shortId}/unanswered")
public String redirectQuestionByLecturerUnnsweredCount(@PathVariable final String shortId) {
return String.format("forward:/v2/lecturerquestion/answercount?sessionkey=%s", shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/myanswers", method = RequestMethod.GET)
public String redirectQuestionByLecturerMyAnswers(@PathVariable final String sessionKey) {
return String.format("forward:/lecturerquestion/myanswers?sessionkey=%s", sessionKey);
@GetMapping("/session/{shortId}/myanswers")
public String redirectQuestionByLecturerMyAnswers(@PathVariable final String shortId) {
return String.format("forward:/v2/lecturerquestion/myanswers?sessionkey=%s", shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/interposed")
public String redirectQuestionByAudience(@PathVariable final String sessionKey) {
return String.format("forward:/audiencequestion/?sessionkey=%s", sessionKey);
@RequestMapping(value = "/session/{shortId}/interposed")
public String redirectQuestionByAudience(@PathVariable final String shortId) {
return String.format("forward:/v2/audiencequestion/?sessionkey=%s", shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/interposed", method = RequestMethod.DELETE)
@DeleteMapping("/session/{shortId}/interposed")
@ResponseBody
public void deleteAllInterposedQuestions(@PathVariable final String sessionKey) {
questionService.deleteAllInterposedQuestions(sessionKey);
public void deleteAllInterposedQuestions(@PathVariable final String shortId) {
commentService.deleteByRoomId(shortId);
}
@DeprecatedApi
@GetMapping("/session/{shortId}/interposedcount")
public String redirectQuestionByAudienceCount(@PathVariable final String shortId) {
return String.format("forward:/v2/audiencequestion/count?sessionkey=%s", shortId);
}
@DeprecatedApi
@GetMapping("/session/{shortId}/interposedreadingcount")
public String redirectQuestionByAudienceReadCount(@PathVariable final String shortId) {
return String.format("forward:/v2/audiencequestion/readcount?sessionkey=%s", shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/interposedcount", method = RequestMethod.GET)
public String redirectQuestionByAudienceCount(@PathVariable final String sessionKey) {
return String.format("forward:/audiencequestion/count?sessionkey=%s", sessionKey);
@GetMapping(value = { "/whoami", "/whoami.json" })
public String redirectWhoami() {
return "forward:/v2/auth/whoami";
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/interposedreadingcount", method = RequestMethod.GET)
public String redirectQuestionByAudienceReadCount(@PathVariable final String sessionKey) {
return String.format("forward:/audiencequestion/readcount?sessionkey=%s", sessionKey);
@PostMapping(value = "/doLogin")
public String redirectLogin() {
return "forward:/v2/auth/login";
}
/* generalized routes */
@DeprecatedApi
@RequestMapping(value = { "/session/{sessionKey}/question/{arg1}", "/session/{sessionKey}/questions/{arg1}" })
@RequestMapping(value = { "/session/{shortId}/question/{arg1}", "/session/{shortId}/questions/{arg1}" })
public String redirectQuestionByLecturerWithOneArgument(
@PathVariable final String sessionKey,
@PathVariable final String arg1
) {
return String.format("forward:/lecturerquestion/%s/?sessionkey=%s", arg1, sessionKey);
@PathVariable final String shortId,
@PathVariable final String arg1) {
return String.format("forward:/v2/lecturerquestion/%s/?sessionkey=%s", arg1, shortId);
}
@DeprecatedApi
@RequestMapping(
value = { "/session/{sessionKey}/question/{arg1}/{arg2}", "/session/{sessionKey}/questions/{arg1}/{arg2}" }
value = { "/session/{shortId}/question/{arg1}/{arg2}", "/session/{shortId}/questions/{arg1}/{arg2}" }
)
public String redirectQuestionByLecturerWithTwoArguments(
@PathVariable final String sessionKey,
@PathVariable final String shortId,
@PathVariable final String arg1,
@PathVariable final String arg2
) {
return String.format("forward:/lecturerquestion/%s/%s/?sessionkey=%s", arg1, arg2, sessionKey);
@PathVariable final String arg2) {
return String.format("forward:/v2/lecturerquestion/%s/%s/?sessionkey=%s", arg1, arg2, shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/interposed/{arg1}")
@RequestMapping(value = "/session/{shortId}/interposed/{arg1}")
public String redirectQuestionByAudienceWithOneArgument(
@PathVariable final String sessionKey,
@PathVariable final String arg1
) {
return String.format("forward:/audiencequestion/%s/?sessionkey=%s", arg1, sessionKey);
@PathVariable final String shortId,
@PathVariable final String arg1) {
return String.format("forward:/v2/audiencequestion/%s/?sessionkey=%s", arg1, shortId);
}
@DeprecatedApi
@RequestMapping(value = "/session/{sessionKey}/interposed/{arg1}/{arg2}")
@RequestMapping(value = "/session/{shortId}/interposed/{arg1}/{arg2}")
public String redirectQuestionByAudienceWithTwoArguments(
@PathVariable final String sessionKey,
@PathVariable final String shortId,
@PathVariable final String arg1,
@PathVariable final String arg2
) {
return String.format("forward:/audiencequestion/%s/%s/?sessionkey=%s", arg1, arg2, sessionKey);
@PathVariable final String arg2) {
return String.format("forward:/v2/audiencequestion/%s/%s/?sessionkey=%s", arg1, arg2, shortId);
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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.controller.v2;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import de.thm.arsnova.controller.AbstractController;
import de.thm.arsnova.model.UserProfile;
import de.thm.arsnova.model.migration.FromV2Migrator;
import de.thm.arsnova.model.migration.ToV2Migrator;
import de.thm.arsnova.model.migration.v2.Motd;
import de.thm.arsnova.model.migration.v2.MotdList;
import de.thm.arsnova.security.User;
import de.thm.arsnova.service.MotdService;
import de.thm.arsnova.service.RoomService;
import de.thm.arsnova.service.UserService;
import de.thm.arsnova.web.exceptions.ForbiddenException;
@RestController("v2MotdController")
@RequestMapping("/v2/motd")
@Api(value = "/motd", description = "Message of the Day API")
public class MotdController extends AbstractController {
@Autowired
private MotdService motdService;
@Autowired
private RoomService roomService;
@Autowired
private UserService userService;
@Autowired
private ToV2Migrator toV2Migrator;
@Autowired
private FromV2Migrator fromV2Migrator;
@ApiOperation(value = "get messages. if adminview=false,"
+ " only messages with startdate<clientdate<enddate are returned")
@GetMapping("/")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204),
@ApiResponse(code = 501, message = HTML_STATUS_501)
})
public List<Motd> getMotd(
@ApiParam(value = "clientdate", required = false)
@RequestParam(value = "clientdate", defaultValue = "")
final String clientdate,
@ApiParam(value = "adminview", required = false)
@RequestParam(value = "adminview", defaultValue = "false")
final Boolean adminview,
@ApiParam(value = "audience", required = false)
@RequestParam(value = "audience", defaultValue = "all")
final String audience,
@ApiParam(value = "sessionkey", required = false)
@RequestParam(value = "sessionkey", required = false)
final String roomShortId) {
final List<de.thm.arsnova.model.Motd> motds;
final Date date = new Date(System.currentTimeMillis());
if (!clientdate.isEmpty()) {
date.setTime(Long.parseLong(clientdate));
}
String roomId = "";
if (roomShortId != null) {
roomId = roomService.getIdByShortId(roomShortId);
}
if (adminview) {
motds = roomShortId != null
? motdService.getAllRoomMotds(roomId)
: motdService.getAdminMotds();
} else {
motds = roomShortId != null
? motdService.getCurrentRoomMotds(date, roomId)
: motdService.getCurrentMotds(date, audience);
}
return motds.stream().map(toV2Migrator::migrate).collect(Collectors.toList());
}
@ApiOperation(value = "create a new message of the day", nickname = "createMotd")
@ApiResponses(value = {
@ApiResponse(code = 201, message = HTML_STATUS_201),
@ApiResponse(code = 503, message = HTML_STATUS_503)
})
@PostMapping("/")
@ResponseStatus(HttpStatus.CREATED)
public Motd postNewMotd(
@ApiParam(value = "current motd", required = true) @RequestBody final Motd motd,
final HttpServletResponse response) {
final de.thm.arsnova.model.Motd motdV3 = fromV2Migrator.migrate(motd);
final String roomId = roomService.getIdByShortId(motd.getSessionkey());
if (de.thm.arsnova.model.Motd.Audience.ROOM == motdV3.getAudience() && roomId != null) {
motdService.save(roomId, motdV3);
} else {
motdService.save(motdV3);
}
return toV2Migrator.migrate(motdV3);
}
@ApiOperation(value = "update a message of the day", nickname = "updateMotd")
@PutMapping("/{motdId}")
public Motd updateMotd(
@ApiParam(value = "motdkey from current motd", required = true) @PathVariable final String motdId,
@ApiParam(value = "current motd", required = true) @RequestBody final Motd motd) {
final de.thm.arsnova.model.Motd motdV3 = fromV2Migrator.migrate(motd);
final String roomId = roomService.getIdByShortId(motd.getSessionkey());
if (motdV3.getAudience() == de.thm.arsnova.model.Motd.Audience.ROOM && roomId != null) {
motdService.update(roomId, motdV3);
} else {
motdService.update(motdV3);
}
return toV2Migrator.migrate(motdV3);
}
@ApiOperation(value = "deletes a message of the day", nickname = "deleteMotd")
@DeleteMapping("/{motdId}")
public void deleteMotd(
@ApiParam(value = "Motd-key from the message that shall be deleted", required = true)
@PathVariable
final String motdId) {
final de.thm.arsnova.model.Motd motd = motdService.get(motdId);
motdService.delete(motd);
}
@GetMapping("/userlist")
public MotdList getAcknowledgedIds(@AuthenticationPrincipal final User user, @RequestParam final String username) {
if (user == null || !user.getUsername().equals(username)) {
throw new ForbiddenException();
}
final UserProfile profile = userService.get(user.getId());
return toV2Migrator.migrateMotdList(profile);
}
@PutMapping("/userlist")
public void putAcknowledgedIds(@AuthenticationPrincipal final User user, @RequestBody final MotdList motdList) {
if (user == null || !user.getUsername().equals(motdList.getUsername())) {
throw new ForbiddenException();
}
final UserProfile profile = userService.get(user.getId());
profile.setAcknowledgedMotds(fromV2Migrator.migrate(motdList));
userService.update(profile);
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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.controller.v2;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import de.thm.arsnova.controller.PaginationController;
import de.thm.arsnova.model.migration.FromV2Migrator;
import de.thm.arsnova.model.migration.ToV2Migrator;
import de.thm.arsnova.model.migration.v2.Room;
import de.thm.arsnova.model.migration.v2.RoomFeature;
import de.thm.arsnova.model.migration.v2.RoomInfo;
import de.thm.arsnova.model.transport.ImportExportContainer;
import de.thm.arsnova.model.transport.ScoreStatistics;
import de.thm.arsnova.service.RoomService;
import de.thm.arsnova.service.RoomServiceImpl;
import de.thm.arsnova.service.RoomServiceImpl.RoomNameComparator;
import de.thm.arsnova.service.RoomServiceImpl.RoomShortNameComparator;
import de.thm.arsnova.service.UserService;
import de.thm.arsnova.web.DeprecatedApi;
import de.thm.arsnova.web.Pagination;
import de.thm.arsnova.web.exceptions.NotImplementedException;
import de.thm.arsnova.web.exceptions.UnauthorizedException;
/**
* Handles requests related to ARSnova Rooms.
*/
@RestController("v2RoomController")
@RequestMapping("/v2/session")
@Api(value = "/session", description = "Room (Session) API")
public class RoomController extends PaginationController {
@Autowired
private RoomService roomService;
@Autowired
private UserService userService;
@Autowired
private ToV2Migrator toV2Migrator;
@Autowired
private FromV2Migrator fromV2Migrator;
@ApiOperation(value = "join a Room",
nickname = "joinRoom")
@DeprecatedApi
@Deprecated
@GetMapping("/{shortId}")
public Room joinRoom(
@ApiParam(value = "Room-Key from current Room", required = true)
@PathVariable final String shortId,
@ApiParam(value = "Adminflag", required = false)
@RequestParam(value = "admin", defaultValue = "false")
final boolean admin) {
if (admin) {
return toV2Migrator.migrate(roomService.getForAdmin(shortId));
} else {
return toV2Migrator.migrate(roomService.getByShortId(shortId));
}
}
@ApiOperation(value = "deletes a Room",
nickname = "deleteRoom")
@DeleteMapping("/{shortId}")
public void deleteRoom(
@ApiParam(value = "Room-Key from current Room", required = true)
@PathVariable
final String shortId) {
final de.thm.arsnova.model.Room room = roomService.getByShortId(shortId);
roomService.delete(room);
}
@ApiOperation(value = "count active users",
nickname = "countActiveUsers")
@DeprecatedApi
@Deprecated
@GetMapping(value = "/{shortId}/activeusercount", produces = MediaType.TEXT_PLAIN_VALUE)
public String countActiveUsers(
@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId) {
return String.valueOf(roomService.activeUsers(roomService.getIdByShortId(shortId)));
}
@ApiOperation(value = "Creates a new Room and returns the Room's data",
nickname = "postNewRoom")
@ApiResponses(value = {
@ApiResponse(code = 201, message = HTML_STATUS_201),
@ApiResponse(code = 503, message = HTML_STATUS_503)
})
@PostMapping("/")
@ResponseStatus(HttpStatus.CREATED)
public Room postNewRoom(
@ApiParam(value = "current Room", required = true)
@RequestBody
final Room room,
final HttpServletResponse response) {
/* FIXME: migrate LMS course support
if (room != null && room.isCourseSession()) {
final List<Course> courses = new ArrayList<>();
final Course course = new Course();
course.setId(room.getCourseId());
courses.add(course);
final int sessionCount = roomService.countSessionsByCourses(courses);
if (sessionCount > 0) {
final String appendix = " (" + (sessionCount + 1) + ")";
room.setName(room.getName() + appendix);
room.setAbbreviation(room.getAbbreviation() + appendix);
}
}
*/
return toV2Migrator.migrate(roomService.create(fromV2Migrator.migrate(room)));
}
@ApiOperation(value = "updates a Room",
nickname = "postNewRoom")
@PutMapping("/{shortId}")
public Room updateRoom(
@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
@ApiParam(value = "current room", required = true) @RequestBody final Room room) {
return toV2Migrator.migrate(roomService.update(fromV2Migrator.migrate(room)));
}
@ApiOperation(value = "change the Room creator (owner)", nickname = "changeRoomCreator")
@RequestMapping(value = "/{shortId}/changecreator", method = RequestMethod.PUT)
public Room changeRoomCreator(
@ApiParam(value = "Room-key from current Room", required = true) @PathVariable final String shortId,
@ApiParam(value = "new Room creator", required = true) @RequestBody final String newCreator) {
return toV2Migrator.migrate(roomService.updateCreator(roomService.getIdByShortId(shortId), newCreator));
}
@ApiOperation(value = "Retrieves a list of Rooms",
nickname = "getRooms")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204),
@ApiResponse(code = 501, message = HTML_STATUS_501)
})
@GetMapping("/")
@Pagination
public List<Room> getRooms(
@ApiParam(value = "ownedOnly", required = true)
@RequestParam(value = "ownedonly", defaultValue = "false")
final boolean ownedOnly,
@ApiParam(value = "visitedOnly", required = true)
@RequestParam(value = "visitedonly", defaultValue = "false")
final boolean visitedOnly,
@ApiParam(value = "sortby", required = true)
@RequestParam(value = "sortby", defaultValue = "name")
final String sortby,
@ApiParam(value = "for a given username. admin rights needed", required = false)
@RequestParam(value = "username", defaultValue = "")
final String username,
final HttpServletResponse response) {
final List<de.thm.arsnova.model.Room> rooms;
if (!"".equals(username)) {
final String userId = userService.getByUsername(username).getId();
try {
if (ownedOnly && !visitedOnly) {
rooms = roomService.getUserRooms(userId);
} else if (visitedOnly && !ownedOnly) {
rooms = roomService.getUserRoomHistory(username);
} else {
response.setStatus(HttpStatus.NOT_IMPLEMENTED.value());
return null;
}
} catch (final AccessDeniedException e) {
throw new UnauthorizedException();
}
} else {
/* TODO implement all parameter combinations, implement use of user parameter */
try {
if (ownedOnly && !visitedOnly) {
rooms = roomService.getMyRooms(offset, limit);
} else if (visitedOnly && !ownedOnly) {
rooms = roomService.getMyRoomHistory(offset, limit);
} else {
response.setStatus(HttpStatus.NOT_IMPLEMENTED.value());
return null;
}
} catch (final AccessDeniedException e) {
throw new UnauthorizedException();
}
}
if (rooms == null || rooms.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
if ("shortname".equals(sortby)) {
Collections.sort(rooms, new RoomShortNameComparator());
} else {
Collections.sort(rooms, new RoomServiceImpl.RoomNameComparator());
}
return rooms.stream().map(toV2Migrator::migrate).collect(Collectors.toList());
}
/**
* Returns a list of my own Rooms with only the necessary information like name, keyword, or counters.
*/
@ApiOperation(value = "Retrieves a Room",
nickname = "getMyRooms")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204)
})
@GetMapping(value = "/", params = "statusonly=true")
@Pagination
public List<RoomInfo> getMyRooms(
@ApiParam(value = "visitedOnly", required = true)
@RequestParam(value = "visitedonly", defaultValue = "false")
final boolean visitedOnly,
@ApiParam(value = "sort by", required = false)
@RequestParam(value = "sortby", defaultValue = "name")
final String sortby,
final HttpServletResponse response) {
final List<de.thm.arsnova.model.Room> rooms;
if (!visitedOnly) {
rooms = roomService.getMyRoomsInfo(offset, limit);
} else {
rooms = roomService.getMyRoomHistoryInfo(offset, limit);
}
if (rooms == null || rooms.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
if ("shortname".equals(sortby)) {
Collections.sort(rooms, new RoomShortNameComparator());
} else {
Collections.sort(rooms, new RoomNameComparator());
}
return rooms.stream().map(toV2Migrator::migrateStats).collect(Collectors.toList());
}
@ApiOperation(value = "Retrieves all public pool Rooms for the current user",
nickname = "getMyPublicPoolRooms")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204)
})
@GetMapping(value = "/publicpool", params = "statusonly=true")
public List<RoomInfo> getMyPublicPoolRooms(
final HttpServletResponse response) {
final List<de.thm.arsnova.model.Room> rooms = roomService.getMyPublicPoolRoomsInfo();
if (rooms == null || rooms.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
return rooms.stream().map(toV2Migrator::migrateStats).collect(Collectors.toList());
}
@ApiOperation(value = "Retrieves all public pool Rooms",
nickname = "getMyPublicPoolRooms")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204)
})
@GetMapping("/publicpool")
public List<Room> getPublicPoolRooms(
final HttpServletResponse response) {
final List<de.thm.arsnova.model.Room> rooms = roomService.getPublicPoolRoomsInfo();
if (rooms == null || rooms.isEmpty()) {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
return null;
}
return rooms.stream().map(toV2Migrator::migrate).collect(Collectors.toList());
}
@ApiOperation(value = "imports a Room",
nickname = "importRoom")
@PostMapping("/import")
public Room importRoom(
@ApiParam(value = "current Room", required = true) @RequestBody final ImportExportContainer room,
final HttpServletResponse response) {
return toV2Migrator.migrate(roomService.importRooms(room));
}
@ApiOperation(value = "export Rooms", nickname = "exportRoom")
@GetMapping("/export")
public List<ImportExportContainer> getExport(
@ApiParam(value = "Room-Key", required = true)
@RequestParam(value = "sessionkey", defaultValue = "")
final List<String> shortIds,
@ApiParam(value = "wether statistics shall be exported", required = true)
@RequestParam(value = "withAnswerStatistics", defaultValue = "false")
final Boolean withAnswerStatistics,
@ApiParam(value = "wether comments shall be exported", required = true)
@RequestParam(value = "withFeedbackQuestions", defaultValue = "false")
final Boolean withFeedbackQuestions,
final HttpServletResponse response) throws IOException {
final List<ImportExportContainer> rooms = new ArrayList<>();
ImportExportContainer temp;
for (final String shortId : shortIds) {
final String id = roomService.getIdByShortId(shortId);
roomService.setActive(id, false);
temp = roomService.exportRoom(id, withAnswerStatistics, withFeedbackQuestions);
if (temp != null) {
rooms.add(temp);
}
roomService.setActive(id, true);
}
return rooms;
}
@ApiOperation(value = "copy a Rooms to the public pool if enabled")
@PostMapping("/{shortId}/copytopublicpool")
public Room copyToPublicPool(
@ApiParam(value = "Room-Key from current Room", required = true)
@PathVariable
final String shortId,
@ApiParam(value = "public pool attributes for Room", required = true)
@RequestBody
final ImportExportContainer.PublicPool publicPool)
throws IOException {
final String id = roomService.getIdByShortId(shortId);
roomService.setActive(id, false);
final de.thm.arsnova.model.Room roomInfo = roomService.copyRoomToPublicPool(shortId, publicPool);
roomService.setActive(id, true);
return toV2Migrator.migrate(roomInfo);
}
@ApiOperation(value = "copy a Room from the public pool if enabled")
@PostMapping("/{shortId}/copyfrompublicpool")
public Room copyFromPublicPool(
@ApiParam(value = "Short ID of the Room", required = true) @PathVariable final String shortId,
@ApiParam(value = "custom attributes for Room", required = true) @RequestBody final Room sessionAttributes) {
throw new NotImplementedException();
}
@ApiOperation(value = "Locks or unlocks a Room",
nickname = "lockRoom")
@ApiResponses(value = {
@ApiResponse(code = 404, message = HTML_STATUS_404)
})
@PostMapping("/{shortId}/lock")
public Room lockRoom(
@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
@ApiParam(value = "lock", required = true) @RequestParam(required = false) final Boolean lock,
final HttpServletResponse response) throws IOException {
if (lock != null) {
return toV2Migrator.migrate(roomService.setActive(roomService.getIdByShortId(shortId), lock));
}
response.setStatus(HttpStatus.NOT_FOUND.value());
return null;
}
@ApiOperation(value = "retrieves a value for the score",
nickname = "getLearningProgress")
@GetMapping("/{shortId}/learningprogress")
public ScoreStatistics getLearningProgress(
@ApiParam(value = "Room-Key from current Room", required = true)
@PathVariable
final String shortId,
@ApiParam(value = "type", required = false)
@RequestParam(value = "type", defaultValue = "questions")
final String type,
@ApiParam(value = "question variant", required = false)
@RequestParam(value = "questionVariant", required = false)
final String questionVariant,
final HttpServletResponse response) {
return roomService.getLearningProgress(roomService.getIdByShortId(shortId), type, questionVariant);
}
@ApiOperation(value = "retrieves a value for the learning progress for the current user",
nickname = "getMyLearningProgress")
@GetMapping("/{shortId}/mylearningprogress")
public ScoreStatistics getMyLearningProgress(
@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
@RequestParam(value = "type", defaultValue = "questions") final String type,
@RequestParam(value = "questionVariant", required = false) final String questionVariant,
final HttpServletResponse response) {
return roomService.getMyLearningProgress(roomService.getIdByShortId(shortId), type, questionVariant);
}
@ApiOperation(value = "retrieves all Room features",
nickname = "getRoomFeatures")
@GetMapping("/{shortId}/features")
public RoomFeature getRoomFeatures(
@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
final HttpServletResponse response) {
final de.thm.arsnova.model.Room room = roomService.getByShortId(shortId);
return toV2Migrator.migrate(room.getSettings());
}
@PutMapping("/{shortId}/features")
@ApiOperation(value = "change all Room features",
nickname = "changeRoomFeatures")
public RoomFeature changeRoomFeatures(
@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
@ApiParam(value = "Room feature", required = true) @RequestBody final RoomFeature features,
final HttpServletResponse response) {
final de.thm.arsnova.model.Room room = roomService.getByShortId(shortId);
room.setSettings(fromV2Migrator.migrate(features));
roomService.update(room);
return toV2Migrator.migrate(room.getSettings());
}
@PostMapping(value = "/{shortId}/lockfeedbackinput", produces = MediaType.TEXT_PLAIN_VALUE)
@ApiOperation(value = "locks input of user live feedback",
nickname = "lockFeedbackInput")
public String lockFeedbackInput(
@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
@ApiParam(value = "lock", required = true) @RequestParam(required = true) final Boolean lock,
final HttpServletResponse response) throws IOException {
return String.valueOf(roomService.lockFeedbackInput(roomService.getIdByShortId(shortId), lock));
}
@PostMapping(value = "/{shortId}/flipflashcards", produces = MediaType.TEXT_PLAIN_VALUE)
@ApiOperation(value = "flip all flashcards in Room",
nickname = "lockFeedbackInput")
public String flipFlashcards(
@ApiParam(value = "Room-Key from current Room", required = true) @PathVariable final String shortId,
@ApiParam(value = "flip", required = true) @RequestParam(required = true) final Boolean flip,
final HttpServletResponse response) {
return String.valueOf(roomService.flipFlashcards(roomService.getIdByShortId(shortId), flip));
}
/* internal redirections */
@RequestMapping(value = "/{shortId}/lecturerquestion")
public String redirectLecturerQuestion(
@PathVariable final String shortId,
final HttpServletResponse response) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/lecturerquestion/?sessionkey=%s", shortId);
}
@RequestMapping(value = "/{shortId}/lecturerquestion/{arg1}")
public String redirectLecturerQuestionWithOneArgument(
@PathVariable final String shortId,
@PathVariable final String arg1,
final HttpServletResponse response) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/lecturerquestion/%s/?sessionkey=%s", arg1, shortId);
}
@RequestMapping(value = "/{shortId}/lecturerquestion/{arg1}/{arg2}")
public String redirectLecturerQuestionWithTwoArguments(
@PathVariable final String shortId,
@PathVariable final String arg1,
@PathVariable final String arg2,
final HttpServletResponse response) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/lecturerquestion/%s/%s/?sessionkey=%s", arg1, arg2, shortId);
}
@RequestMapping(value = "/{shortId}/lecturerquestion/{arg1}/{arg2}/{arg3}")
public String redirectLecturerQuestionWithThreeArguments(
@PathVariable final String shortId,
@PathVariable final String arg1,
@PathVariable final String arg2,
@PathVariable final String arg3,
final HttpServletResponse response) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/lecturerquestion/%s/%s/%s/?sessionkey=%s", arg1, arg2, arg3, shortId);
}
@RequestMapping(value = "/{shortId}/audiencequestion")
public String redirectAudienceQuestion(
@PathVariable final String shortId,
final HttpServletResponse response) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/audiencequestion/?sessionkey=%s", shortId);
}
@RequestMapping(value = "/{shortId}/audiencequestion/{arg1}")
public String redirectAudienceQuestionWithOneArgument(
@PathVariable final String shortId,
@PathVariable final String arg1,
final HttpServletResponse response) {
response.addHeader(X_FORWARDED, "1");
return String.format("forward:/audiencequestion/%s/?sessionkey=%s", arg1, shortId);
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,81 +15,82 @@
* 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.controller;
import de.thm.arsnova.entities.User;
import de.thm.arsnova.services.IUserService;
import de.thm.arsnova.services.UserSessionService;
import de.thm.arsnova.socket.ARSnovaSocket;
package de.thm.arsnova.controller.v2;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.UUID;
import de.thm.arsnova.controller.AbstractController;
import de.thm.arsnova.security.User;
import de.thm.arsnova.service.UserService;
import de.thm.arsnova.websocket.ArsnovaSocketioServer;
/**
* Initiates the socket communication.
*/
@RestController
@RequestMapping("/socket")
@Api(value = "/socket", description = "the Socket API")
@RestController("v2SocketController")
@RequestMapping("/v2/socket")
@Api(value = "/socket", description = "WebSocket Initialization API")
public class SocketController extends AbstractController {
@Autowired
private IUserService userService;
@Autowired
private UserSessionService userSessionService;
private UserService userService;
@Autowired
private ARSnovaSocket server;
private ArsnovaSocketioServer server;
private static final Logger logger = LoggerFactory.getLogger(SocketController.class);
@ApiOperation(value = "requested to assign Websocket session",
nickname = "authorize")
@ApiResponses(value = {
@ApiResponse(code = 204, message = HTML_STATUS_204),
@ApiResponse(code = 400, message = HTML_STATUS_400),
@ApiResponse(code = 403, message = HTML_STATUS_403)
@ApiResponse(code = 204, message = HTML_STATUS_204),
@ApiResponse(code = 400, message = HTML_STATUS_400),
@ApiResponse(code = 403, message = HTML_STATUS_403)
})
@RequestMapping(method = RequestMethod.POST, value = "/assign")
public void authorize(@ApiParam(value = "sessionMap", required = true) @RequestBody final Map <String, String> sessionMap, @ApiParam(value = "response", required = true) final HttpServletResponse response) {
String socketid = sessionMap.get("session");
@PostMapping("/assign")
public void authorize(
@ApiParam(value = "sessionMap", required = true) @RequestBody final Map<String, String> sessionMap,
@ApiParam(value = "response", required = true) final HttpServletResponse response) {
final String socketid = sessionMap.get("session");
if (null == socketid) {
logger.debug("Expected property 'session' missing.");
response.setStatus(HttpStatus.BAD_REQUEST.value());
return;
}
User u = userService.getCurrentUser();
if (null == u) {
final User user = userService.getCurrentUser();
if (null == user) {
logger.debug("Client {} requested to assign Websocket session but has not authenticated.", socketid);
response.setStatus(HttpStatus.FORBIDDEN.value());
return;
}
userService.putUser2SocketId(UUID.fromString(socketid), u);
userSessionService.setSocketId(UUID.fromString(socketid));
userService.putUserIdToSocketId(UUID.fromString(socketid), user.getId());
response.setStatus(HttpStatus.NO_CONTENT.value());
}
@ApiOperation(value = "retrieves a socket url",
nickname = "getSocketUrl")
@RequestMapping(value = "/url", method = RequestMethod.GET)
@GetMapping(value = "/url", produces = MediaType.TEXT_PLAIN_VALUE)
public String getSocketUrl(final HttpServletRequest request) {
return (server.isUseSSL() ? "https://" : "http://") + request.getServerName() + ":" + server.getPortNumber();
return (server.isUseSsl() ? "https://" : "http://") + request.getServerName() + ":" + server.getPortNumber();
}
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
* Copyright (C) 2012-2019 The ARSnova Team and Contributors
*
* 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
......@@ -15,32 +15,37 @@
* 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.controller;
import de.thm.arsnova.entities.Statistics;
import de.thm.arsnova.services.IStatisticsService;
import de.thm.arsnova.web.CacheControl;
import de.thm.arsnova.web.DeprecatedApi;
package de.thm.arsnova.controller.v2;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import de.thm.arsnova.controller.AbstractController;
import de.thm.arsnova.model.Statistics;
import de.thm.arsnova.service.StatisticsService;
import de.thm.arsnova.web.CacheControl;
import de.thm.arsnova.web.DeprecatedApi;
/**
* Allows retrieval of several statistics such as the number of active users.
*/
@RestController
@Api(value = "/statistics", description = "the Statistic API")
@RestController("v2StatisticsController")
@Api(value = "/statistics", description = "Statistics API")
@RequestMapping("/v2/statistics")
public class StatisticsController extends AbstractController {
@Autowired
private IStatisticsService statisticsService;
private StatisticsService statisticsService;
@ApiOperation(value = "Retrieves global statistics",
nickname = "getStatistics")
@RequestMapping(method = RequestMethod.GET, value = "/statistics")
@GetMapping("/")
@CacheControl(maxAge = 60, policy = CacheControl.Policy.PUBLIC)
public Statistics getStatistics() {
return statisticsService.getStatistics();
......@@ -50,27 +55,27 @@ public class StatisticsController extends AbstractController {
nickname = "countActiveUsers")
@DeprecatedApi
@Deprecated
@RequestMapping(method = RequestMethod.GET, value = "/statistics/activeusercount", produces = "text/plain")
@GetMapping(value = "/activeusercount", produces = MediaType.TEXT_PLAIN_VALUE)
public String countActiveUsers() {
return Integer.toString(statisticsService.getStatistics().getActiveUsers());
return String.valueOf(statisticsService.getStatistics().getActiveUsers());
}
@ApiOperation(value = "Retrieves the amount of all currently logged in users",
nickname = "countLoggedInUsers")
@DeprecatedApi
@Deprecated
@RequestMapping(method = RequestMethod.GET, value = "/statistics/loggedinusercount", produces = "text/plain")
@GetMapping(value = "/loggedinusercount", produces = MediaType.TEXT_PLAIN_VALUE)
public String countLoggedInUsers() {
return Integer.toString(statisticsService.getStatistics().getLoggedinUsers());
return String.valueOf(statisticsService.getStatistics().getLoggedinUsers());
}
@ApiOperation(value = "Retrieves the total amount of all sessions",
nickname = "countSessions")
@DeprecatedApi
@Deprecated
@RequestMapping(method = RequestMethod.GET, value = "/statistics/sessioncount", produces = "text/plain")
@GetMapping(value = "/sessioncount", produces = MediaType.TEXT_PLAIN_VALUE)
public String countSessions() {
return Integer.toString(statisticsService.getStatistics().getOpenSessions()
return String.valueOf(statisticsService.getStatistics().getOpenSessions()
+ statisticsService.getStatistics().getClosedSessions());
}
}