Ajout d'une page d'édition des quizz

This commit is contained in:
Samy Avrillon 2025-06-10 23:53:48 +02:00
parent 8ae7457016
commit c62f070e39
Signed by: Mysaa
GPG Key ID: 0220AC4A3D6A328B
14 changed files with 684 additions and 7 deletions

View File

@ -41,6 +41,7 @@ dependencies {
implementation Spring.session.jdbc
implementation 'jakarta.validation:jakarta.validation-api:_'
runtimeOnly 'org.webjars:jquery:_'
runtimeOnly 'org.webjars:lodash:_'
developmentOnly Spring.boot.devTools
runtimeOnly 'org.postgresql:postgresql:_'
testImplementation Spring.boot.test

View File

@ -7,15 +7,12 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="questions")
public class Question {
@ -26,15 +23,19 @@ public class Question {
private long id;
@Column(nullable=false)
@Setter
QTypes type;
@Column(nullable=false)
@Setter
private int index;
@ManyToOne
@JoinColumn(name = "quizz",nullable = false)
@Setter
private Quizz quizz;
@Setter
private String value;
transient QuestionType qtype = null;
@ -44,7 +45,7 @@ public class Question {
JsonNode jsNode;
try {
jsNode = om.readTree(value);
qtype = type.getConstructor().apply(jsNode);
qtype = type.construct(jsNode);
} catch (JsonProcessingException e) {
throw new IllegalStateException(e);
}

View File

@ -4,12 +4,20 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bernard.misael.service.exception.MalformedAnswerException;
import com.bernard.misael.service.exception.MalformedClientAnswerException;
import com.bernard.misael.service.exception.QuestionTypeException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -82,4 +90,28 @@ public class DccQuestion implements QuestionType{
}
}
public static Logger LOG = LoggerFactory.getLogger(DccQuestion.class);
public static boolean validate(JsonNode data) {
LOG.info(data.toPrettyString());
LOG.info(Stream.of(data.get("wrong")).map(p -> p.isTextual()+"->"+p).collect(Collectors.joining(";")));
return data.isObject() &&
data.has("text") && data.get("text").isTextual() &&
data.has("good") && data.get("good").isTextual() &&
data.has("wrong") && data.get("wrong").isArray() &&
data.get("wrong").size() >= 3 &&
StreamSupport.stream(Spliterators.spliterator(data.get("wrong").elements(),data.get("wrong").size(),Spliterator.SIZED),false).allMatch(p ->
p.isTextual()
);
}
public static final JsonNode getDefaultDccQuestion() {
ObjectNode q = JsonNodeFactory.instance.objectNode();
q.set("text",JsonNodeFactory.instance.textNode(""));
q.set("good",JsonNodeFactory.instance.textNode(""));
ArrayNode wrong = JsonNodeFactory.instance.arrayNode(3);
for(int i=0;i<3;i++)wrong.add(JsonNodeFactory.instance.textNode(""));
q.set("wrong",wrong);
return q;
}
}

View File

@ -7,15 +7,18 @@ import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum QTypes {
DCC(1,DccQuestion.class,DccQuestion::new);
DCC(1,DccQuestion.class,DccQuestion::new,DccQuestion::validate,DccQuestion.getDefaultDccQuestion());
private int id;
@SuppressWarnings("unused")
private Class<? extends QuestionType> type;
private Function<JsonNode,QuestionType> constructor;
private Function<JsonNode,Boolean> validator;
@Getter
private JsonNode defaultQuestion;
public static QTypes findById(final int id) {
for(QTypes value : QTypes.values())
@ -24,4 +27,12 @@ public enum QTypes {
return null;
}
public QuestionType construct(JsonNode data){
return this.constructor.apply(data);
}
public boolean validate(JsonNode data){
return this.validator.apply(data);
}
}

View File

@ -1,5 +1,7 @@
package com.bernard.misael.repository;
import java.util.Set;
import org.springframework.data.jpa.repository.JpaRepository;
import com.bernard.misael.model.Question;
@ -9,5 +11,6 @@ import com.bernard.misael.model.Quizz;
public interface QuestionRepository extends JpaRepository<Question,Long> {
public Question findByQuizzAndIndex(Quizz quizz, int index);
public Set<Question> findByQuizz(Quizz quizz);
}

View File

@ -4,6 +4,7 @@ import java.util.List;
import com.bernard.misael.model.Quizz;
import com.bernard.misael.model.User;
import com.bernard.misael.questions.QTypes;
import com.fasterxml.jackson.databind.JsonNode;
public interface QuizzManager {
@ -14,4 +15,14 @@ public interface QuizzManager {
public boolean canAccessQuizz(User user, long quizzId);
public List<Quizz> accessibleQuizz(User user);
public boolean canEditQuizz(User user, long quizzId);
public JsonNode getQuizzInfo(User user, long quizzId);
public JsonNode setQuizzName(User user, long quizzId, String newName);
public JsonNode addQuestion(User user, long quizzId);
public JsonNode removeQuestion(User user, long quizzId, long questionId);
public JsonNode reorderQuestions(User user, long quizzId, List<Long> newOrder);
public JsonNode editQuestion(User user, long quizzId, long questionId, JsonNode value);
public JsonNode setQuestionType(User user, long quizzId, long questionId, QTypes type);
}

View File

@ -1,11 +1,15 @@
package com.bernard.misael.service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -14,6 +18,7 @@ import com.bernard.misael.model.Question;
import com.bernard.misael.model.Quizz;
import com.bernard.misael.model.QuizzForm;
import com.bernard.misael.model.User;
import com.bernard.misael.questions.QTypes;
import com.bernard.misael.questions.QuestionType.AnswerResult;
import com.bernard.misael.repository.AnswerRepository;
import com.bernard.misael.repository.QuestionRepository;
@ -22,7 +27,11 @@ import com.bernard.misael.repository.QuizzRepository;
import com.bernard.misael.service.exception.MalformedAnswerException;
import com.bernard.misael.service.exception.MalformedClientAnswerException;
import com.bernard.misael.service.exception.QuestionTypeException;
import com.bernard.misael.web.QuestionsController;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -198,4 +207,227 @@ public class QuizzManagerImpl implements QuizzManager {
).collect(Collectors.toList());
}
Logger logger = LoggerFactory.getLogger(QuestionsController.class);
@Override
public boolean canEditQuizz(User user, long quizzId) {
try {
Quizz quizz = qRepository.getReferenceById(quizzId);
logger.info("Quizz owner is "+quizz.getOwner().getName());
return quizz.getOwner().equals(user);
} catch (EntityNotFoundException e) {
logger.info("Could not find quizz of id "+quizzId);
return false;
}
}
private Optional<JsonNode> checkEditQuizz(User user, long quizzId) {
if(user == null)
return Optional.of(errorNode("You need to be logged in to edit the quizz"));
if(!canEditQuizz(user, quizzId))
return Optional.of(errorNode("User has no right to edit quizz"));
Optional<Quizz> oquizz = qRepository.findById(quizzId);
if(!oquizz.isPresent())
return Optional.of(errorNode("Could not find quizz with id "+quizzId));
return Optional.empty();
}
@Override
public JsonNode getQuizzInfo(User user, long quizzId) {
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
if(authCheck.isPresent()) return authCheck.get();
Quizz quizz = qRepository.findById(quizzId).get();
ArrayNode n = JsonNodeFactory.instance.arrayNode(quizz.getQuestionCount());
for(int i = 0;i<quizz.getQuestionCount();i++)n.add(JsonNodeFactory.instance.nullNode());
for(Question q : quizz.getQuestions()) {
ObjectNode nn = JsonNodeFactory.instance.objectNode();
nn.set("id",JsonNodeFactory.instance.numberNode(q.getId()));
nn.set("type", JsonNodeFactory.instance.textNode(q.getType().name()));
ObjectMapper om = new ObjectMapper();
try {
nn.set("value",om.readTree(q.getValue()));
} catch (JsonProcessingException e) {
throw new IllegalStateException("Value stored for question "+q.getId()+" is illegal (non-json)",e);
}
n.set(q.getIndex(),nn);
}
ObjectNode out = JsonNodeFactory.instance.objectNode();
out.set("success", JsonNodeFactory.instance.booleanNode(true));
out.set("questions", n);
out.set("name", JsonNodeFactory.instance.textNode(quizz.getName()));
return out;
}
@Override
public JsonNode setQuizzName(User user, long quizzId, String newName) {
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
if(authCheck.isPresent()) return authCheck.get();
if(newName.isBlank() | newName.length()>255)
return errorNode("Le nom est invalide");
Quizz quizz = qRepository.findById(quizzId).get();
quizz.setName(newName);
ObjectNode out = JsonNodeFactory.instance.objectNode();
out.set("success", JsonNodeFactory.instance.booleanNode(true));
return out;
}
public static final QTypes DEFAULT_QTYPE = QTypes.DCC;
@Override
public JsonNode addQuestion(User user, long quizzId) {
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
if(authCheck.isPresent()) return authCheck.get();
Quizz quizz = qRepository.findById(quizzId).get();
Question q = new Question();
q.setType(DEFAULT_QTYPE);
JsonNode n = DEFAULT_QTYPE.getDefaultQuestion();
q.setValue(n.toString());
q.setQuizz(quizz);
q.setIndex(quizz.getQuestionCount());
quizz.setQuestionCount(quizz.getQuestionCount()+1);
questionRepository.save(q);
ObjectNode out = JsonNodeFactory.instance.objectNode();
out.set("success", JsonNodeFactory.instance.booleanNode(true));
out.set("type", JsonNodeFactory.instance.textNode(DEFAULT_QTYPE.name()));
out.set("value", n);
return out;
}
@Override
public JsonNode removeQuestion(User user, long quizzId, long questionId) {
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
if(authCheck.isPresent()) return authCheck.get();
Quizz quizz = qRepository.findById(quizzId).get();
final Question q;
try {
q = questionRepository.getReferenceById(questionId);
} catch (EntityNotFoundException e){
return errorNode("Could not find question with id "+questionId);
}
questionRepository.findByQuizz(quizz).forEach(qq -> {
if(qq.getIndex()>q.getIndex())
qq.setIndex(qq.getIndex()-1);
});
questionRepository.delete(q);
quizz.setQuestionCount(quizz.getQuestionCount()-1);
ObjectNode out = JsonNodeFactory.instance.objectNode();
out.set("success", JsonNodeFactory.instance.booleanNode(true));
return out;
}
@Override
public JsonNode reorderQuestions(User user, long quizzId, List<Long> newOrder) {
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
if(authCheck.isPresent()) return authCheck.get();
Quizz quizz = qRepository.findById(quizzId).get();
// We need that the base set of neworder is the list of ids
// 1) The length is right
if(quizz.getQuestionCount() != newOrder.size())
return errorNode("You must put every question in order");
// 2) There is no duplicates
if(new HashSet<Long>(newOrder).size() != newOrder.size())
return errorNode("You shouldn't put duplicates in the new order");
// 3) All ids correspond to an actual question of the right quizz
List<Question> questions = new ArrayList<>(newOrder.size());
for(int i = 0;i<newOrder.size();i++){
try {
Question q = questionRepository.getReferenceById(newOrder.get(i));
if(!q.getQuizz().equals(quizz))
return errorNode("The question id "+newOrder.get(i)+" is associated to another quizz");
questions.add(q);
} catch (EntityNotFoundException e){
return errorNode("Could not find question with id "+newOrder.get(i));
}
}
// All Checks passed
for(int i=0;i<questions.size();i++){
questions.get(i).setIndex(i);
}
ObjectNode out = JsonNodeFactory.instance.objectNode();
out.set("success", JsonNodeFactory.instance.booleanNode(true));
return out;
}
@Override
public JsonNode editQuestion(User user, long quizzId, long questionId, JsonNode value) {
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
if(authCheck.isPresent()) return authCheck.get();
Quizz quizz = qRepository.findById(quizzId).get();
final Question q;
try {
q = questionRepository.getReferenceById(questionId);
} catch (EntityNotFoundException e){
return errorNode("Could not find question with id "+questionId);
}
if(!q.getQuizz().equals(quizz))
return errorNode("Question is not associated with the right quizzId");
if(!q.getType().validate(value))
return errorNode("Invalid question value");
q.setValue(value.toString());
ObjectNode out = JsonNodeFactory.instance.objectNode();
out.set("success", JsonNodeFactory.instance.booleanNode(true));
return out;
}
@Override
public JsonNode setQuestionType(User user, long quizzId, long questionId, QTypes type) {
Optional<JsonNode> authCheck = checkEditQuizz(user, quizzId);
if(authCheck.isPresent()) return authCheck.get();
Quizz quizz = qRepository.findById(quizzId).get();
final Question q;
try {
q = questionRepository.getReferenceById(questionId);
} catch (EntityNotFoundException e){
return errorNode("Could not find question with id "+questionId);
}
if(!q.getQuizz().equals(quizz))
return errorNode("Question is not associated with the right quizzId");
// If type is the same, we don't change (and dont reset the value)
JsonNode n;
if(!type.equals(q.getType())) {
// Then we need to change
q.setType(type);
n = type.getDefaultQuestion();
q.setValue(n.toString());
} else {
ObjectMapper om = new ObjectMapper();
try {
n = om.readTree(q.getValue());
} catch (JsonProcessingException e) {
throw new IllegalStateException("Value stored for question "+questionId+" is illegal (non-json)",e);
}
}
ObjectNode out = JsonNodeFactory.instance.objectNode();
out.set("success", JsonNodeFactory.instance.booleanNode(true));
out.set("new", n);
return out;
}
}

View File

@ -1,7 +1,13 @@
package com.bernard.misael.web;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
@ -11,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import com.bernard.misael.model.Quizz;
import com.bernard.misael.model.User;
import com.bernard.misael.questions.QTypes;
import com.bernard.misael.repository.QuizzRepository;
import com.bernard.misael.repository.UserRepository;
import com.bernard.misael.service.QuizzManager;
@ -24,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestBody;
@Controller
@RequestMapping("/questions")
public class QuestionsController {
@ -91,5 +99,123 @@ public class QuestionsController {
}
Logger logger = LoggerFactory.getLogger(QuestionsController.class);
@GetMapping("/quizz-edit/{q}")
public Object quizzEdit(@PathVariable("q") long quizzId, Principal p, Model m) {
if (p==null)
return "redirect:/login?restricted";
User u = ur.findByName(p.getName());
logger.info("An INFO Message");
if (u==null || !qm.canEditQuizz(u, quizzId))
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
m.addAttribute("quizzId", quizzId);
return "quizz-edit";
}
@PostMapping("/quizz-edit/{q}/get")
public ResponseEntity<JsonNode> quizzSetName(@PathVariable("q") long quizzId, Principal p) {
User u = null;
if (p!=null)
u = ur.findByName(p.getName());
if(u==null)
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
JsonNode out = qm.getQuizzInfo(u, quizzId);
return new ResponseEntity<>(out, HttpStatus.OK);
}
@PostMapping("/quizz-edit/{q}/set-name")
public ResponseEntity<JsonNode> quizzSetName(@PathVariable("q") long quizzId, @RequestBody String data, Principal p) {
User u = null;
if (p!=null)
u = ur.findByName(p.getName());
if(u==null)
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
JsonNode out = qm.setQuizzName(u, quizzId, data);
return new ResponseEntity<>(out, HttpStatus.OK);
}
@PostMapping("/quizz-edit/{q}/add-question")
public ResponseEntity<JsonNode> quizzAddQuestion(@PathVariable("q") long quizzId, Principal p) {
User u = null;
if (p!=null)
u = ur.findByName(p.getName());
if(u==null)
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
JsonNode out = qm.addQuestion(u, quizzId);
return new ResponseEntity<>(out, HttpStatus.OK);
}
@PostMapping("/quizz-edit/{q}/remove-question/{qi}")
public ResponseEntity<JsonNode> quizzSetName(@PathVariable("q") long quizzId, @PathVariable("qi") long questionId, Principal p) {
User u = null;
if (p!=null)
u = ur.findByName(p.getName());
if(u==null)
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
JsonNode out = qm.removeQuestion(u, quizzId, questionId);
return new ResponseEntity<>(out, HttpStatus.OK);
}
@PostMapping("/quizz-edit/{q}/reorder-questions")
public ResponseEntity<JsonNode> quizzReorderQuestions(@PathVariable("q") long quizzId, @RequestBody JsonNode data, Principal p) {
User u = null;
if (p!=null)
u = ur.findByName(p.getName());
if(u==null)
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
if(!data.isArray())
return new ResponseEntity<>(
JsonNodeFactory.instance.textNode("Data should be an array"),
HttpStatus.BAD_REQUEST);
List<Long> idz = new ArrayList<>(data.size());
for(int i=0;i<data.size();i++)
if(data.get(i).isNumber())
idz.add(data.get(i).asLong());
else
return new ResponseEntity<>(
JsonNodeFactory.instance.textNode("Data should be an array of numbers"),
HttpStatus.BAD_REQUEST);
JsonNode out = qm.reorderQuestions(u, quizzId, idz);
return new ResponseEntity<>(out, HttpStatus.OK);
}
@PostMapping("/quizz-edit/{q}/edit-question/{qi}")
public ResponseEntity<JsonNode> quizzEditQuestion(@PathVariable("q") long quizzId,
@PathVariable("qi") long questionId, @RequestBody JsonNode data, Principal p) {
User u = null;
if (p!=null)
u = ur.findByName(p.getName());
if(u==null)
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
JsonNode out = qm.editQuestion(u, quizzId, questionId, data);
return new ResponseEntity<>(out, HttpStatus.OK);
}
@PostMapping("/quizz-edit/{q}/set-question-type/{qi}")
public ResponseEntity<JsonNode> quizzSetQuestionType(@PathVariable("q") long quizzId,
@PathVariable("qi") long questionId, @RequestBody JsonNode data, Principal p) {
User u = null;
if (p!=null)
u = ur.findByName(p.getName());
if(u==null)
return new ResponseEntity<>(JsonNodeFactory.instance.objectNode(),HttpStatus.UNAUTHORIZED);
if(!data.isTextual())
return new ResponseEntity<>(
JsonNodeFactory.instance.textNode("Data should be a string"),
HttpStatus.BAD_REQUEST);
Optional<QTypes> qtype = Arrays.stream(QTypes.values())
.filter(q -> q.name().equals(data.textValue()))
.findAny();
if(qtype.isEmpty())
return new ResponseEntity<>(
JsonNodeFactory.instance.textNode("Unknown qtype"),
HttpStatus.BAD_REQUEST);
JsonNode out = qm.setQuestionType(u, quizzId, questionId, qtype.get());
return new ResponseEntity<>(out, HttpStatus.OK);
}
}

View File

@ -33,3 +33,7 @@ spring:
hibernate:
ddl-auto: none
generate-ddl: true
logging:
level:
root: INFO

View File

@ -0,0 +1,41 @@
ol#questions-list {
display: table;
border-spacing: 2px;
width: 90%;
}
ol#questions-list li {
width: 90%;
background-color: lemonchiffon;
border: 2pt solid;
display: table-row;
flex-direction: row;
align-items: stretch;
margin: auto;
}
ol#questions-list li div.button-box {
display:table-cell;
text-align: center;
vertical-align: middle;
width: 7em;
}
ol#questions-list li div.button-box button{
width: 7em;
}
ol#questions-list li div.content-box {
display:table-cell;
background-color: gainsboro;
color: black;
font-size: 12pt;
}
/* DCC */
div.dcc-box h5, div.dcc-box h5 input{
font-size: 16pt;
font-weight: bold;
text-decoration: underline;
width: 100%;
}

View File

@ -0,0 +1,204 @@
<!DOCTYPE html>
<html lang="fr" dir="ltr">
<head>
<div th:replace="~{html-head}"/>
<link rel="stylesheet" th:href="@{/css/quizz-edit.css}"/>
</head>
<body>
<div th:replace="~{header}"/>
<script th:src="@{/webjars/jquery/1.9.1/jquery.min.js}" type="text/javascript"></script>
<script th:src="@{/webjars/lodash/4.17.21/lodash.js}" type="text/javascript"></script>
<main>
<h1>Quizz <span id="question-name"></span><button id="edit-question-name">Edit</button></h1>
<ol id="questions-list">
</ol>
<span id="error-textbox" style="color: red"></span>
<script th:inline="javascript">
/*<![CDATA[*/
var quizzid = /*[[${quizzId}]]*/ -1;
/*]]>*/
</script>
<script>
function error(txt) {
console.log(txt)
$("#error-textbox").text(txt)
}
QTYPES = {
"DCC": {
"display" : makeAnswerBlockBCC,
"edit": makeEditBlockBCC,
"read": readEditBlockBCC
}
}
questions={}
function getdata() {
$.ajax({
url: "/questions/quizz-edit/"+quizzid+"/get",
type: "POST",
dataType: "json",
success: function(res) {
console.log(res)
if(!res["success"]) {
console.log(res["message"])
error(res["message"])
return
}
questions=res["questions"]
qname=res["name"]
$("#question-name").text(qname)
for(var i=0;i<questions.length;i++){
qu = questions[i]
qid = qu["id"]
newhtml = `
<li id="question-display-${qid}">
<div class="content-box" id="question-display-${qid}-content"/>
<div class="content-box" id="question-display-${qid}-edit"/>
<div class="button-box">
<button id="question-display-${qid}-button-edit">Edit</button>
<button id="question-display-${qid}-type-edit">Type</button>
<button id="question-display-${qid}-validate-edit">Valider</button>
<button id="question-display-${qid}-cancel-edit">Annuler</button>
</div>
</li>
`
newdom = $('<li/>').html(newhtml).contents()
$("#questions-list").append(newdom)
const thei = i
const theqid = qid
$(`#question-display-${qid}-button-edit`).on('click',e => edit(thei,theqid))
$(`#question-display-${qid}-type-edit`).on('click',e => setType(thei,theqid))
$(`#question-display-${qid}-validate-edit`).on('click',e => commitEdit(thei,theqid))
$(`#question-display-${qid}-cancel-edit`).on('click',e => cancelEdit(thei,theqid))
newcontent = QTYPES[qu["type"]]["display"](qid,qu["value"])
$(`#question-display-${qid}-content`).replaceWith(newcontent)
newedit = QTYPES[qu["type"]]["edit"](qid,qu["value"])
$(`#question-display-${qid}-edit`).replaceWith(newedit)
contentMode(qid)
}
}
})
}
function contentMode(qid) {
$(`#question-display-${qid}-content`).show()
$(`#question-display-${qid}-button-edit`).show()
$(`#question-display-${qid}-type-edit`).show()
$(`#question-display-${qid}-edit`).hide()
$(`#question-display-${qid}-validate-edit`).hide()
$(`#question-display-${qid}-cancel-edit`).hide()
}
function editMode(qid) {
$(`#question-display-${qid}-content`).hide()
$(`#question-display-${qid}-button-edit`).hide()
$(`#question-display-${qid}-type-edit`).hide()
$(`#question-display-${qid}-edit`).show()
$(`#question-display-${qid}-validate-edit`).show()
$(`#question-display-${qid}-cancel-edit`).show()
}
function lockEdit(qid) {
$(`#question-display-${qid}-edit *`).prop('disabled', true)
$(`#question-display-${qid}-validate-edit`).prop('disabled', true)
$(`#question-display-${qid}-cancel-edit`).prop('disabled', true)
}
function unlockEdit(qid) {
$(`#question-display-${qid}-edit *`).prop('disabled', false)
$(`#question-display-${qid}-validate-edit`).prop('disabled', false)
$(`#question-display-${qid}-cancel-edit`).prop('disabled', false)
}
function setType(i,id) {
//TODO à faire ^^
}
function edit(id, qid) {
editMode(qid)
}
function commitEdit(i,qid) {
const newvalue = readEditBlockBCC(qid)
lockEdit(qid)
$.ajax({
url: "/questions/quizz-edit/"+quizzid+"/edit-question/"+qid,
type: "POST",
data: JSON.stringify(newvalue),
dataType: "json",
contentType: 'application/json',
success: function(res) {
console.log(res)
if(!res["success"]) {
console.log(res["message"])
error(res["message"])
unlockEdit(qid)
return
}
questions[i]["value"] = newvalue
newcontent = QTYPES[questions[i]["type"]]["display"](qid,questions[i]["value"])
$(`#question-display-${qid}-content`).replaceWith(newcontent)
unlockEdit(qid)
contentMode(qid)
}
})
}
function cancelEdit(i,qid) {
newedit = QTYPES[questions[i]["type"]]["edit"](qid,questions[i]["value"])
$(`#question-display-${qid}-edit`).replaceWith(newedit)
contentMode(qid)
}
function makeEditBlockBCC(id,question) {
html = `
<div class="content-box dcc-box" id="question-display-${id}-edit">
<h5><input id="question-display-${id}-edit-question"
type="text" value="${_.escape(question["text"])}"/></h5>
<ol>
<li class="right">&#x2611; <input id="question-display-${id}-edit-good"
type="text" value="${_.escape(question["good"])}"/></li>
<li class="wrong">&#x2612; <input id="question-display-${id}-edit-wrong1"
type="text" value="${_.escape(question["wrong"][0])}"/></li>
<li class="wrong">&#x2612; <input id="question-display-${id}-edit-wrong2"
type="text" value="${_.escape(question["wrong"][1])}"/></li>
<li class="wrong">&#x2612; <input id="question-display-${id}-edit-wrong3"
type="text" value="${_.escape(question["wrong"][2])}"/></li>
</ol>
</div>
`
out = $('<div/>').html(html).contents()
return out
}
function readEditBlockBCC(id) {
questionText = $(`#question-display-${id}-edit-question`).val()
goodText = $(`#question-display-${id}-edit-good`).val()
wrongText1 = $(`#question-display-${id}-edit-wrong1`).val()
wrongText2 = $(`#question-display-${id}-edit-wrong2`).val()
wrongText3 = $(`#question-display-${id}-edit-wrong3`).val()
return {
"text": questionText,
"good": goodText,
"wrong": [wrongText1,wrongText2,wrongText3]
}
}
function makeAnswerBlockBCC(id,question) {
html = `
<div class="content-box dcc-box" id="question-display-${id}-content">
<h5>${_.escape(question["text"])}</h5>
<ol>
<li class="right">&#x2611; ${_.escape(question["good"])}</li>
<li class="wrong">&#x2612; ${_.escape(question["wrong"][0])}</li>
<li class="wrong">&#x2612; ${_.escape(question["wrong"][1])}</li>
<li class="wrong">&#x2612; ${_.escape(question["wrong"][2])}</li>
</ol>
</div>
`
out = $('<div/>').html(html).contents()
return out
}
getdata()
</script>
</main>
</body>
</html>

View File

@ -8,7 +8,7 @@
<div th:replace="~{header}"/>
<main>
<ul>
<li>Premier item ?</li>
<li th:if="${#lists.isEmpty(quizz)}">Aucun quizz de disponible malheureusement :(</li>
<li th:each="q : ${quizz}"><a th:href="@{/questions/form/{id}(id=${q.id})}">Quizz <span th:text="${q.name}"/></a></li>
</ul>
</main>

View File

@ -3,6 +3,8 @@ package com.bernard.misael;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import com.bernard.misael.questions.QTypes;
@SpringBootTest
class MisaelApplicationTests {
@ -10,4 +12,11 @@ class MisaelApplicationTests {
void contextLoads() {
}
@Test
void validateDefaultQuestions() {
for(QTypes qt : QTypes.values()){
assert qt.validate(qt.getDefaultQuestion());
}
}
}

View File

@ -96,3 +96,5 @@ plugin.org.flywaydb.flyway=11.3.2
## # available=11.3.4
plugin.io.spring.dependency-management=1.1.7
version.org.webjars..lodash=4.17.21